From 2c6e02bf1b6bbd374310f458b184ab1c26717cd6 Mon Sep 17 00:00:00 2001 From: Patrick McDermott Date: Mon, 26 Jun 2023 11:47:00 -0400 Subject: mknod: New helper utility --- (limited to 'helpers/mknod.c') diff --git a/helpers/mknod.c b/helpers/mknod.c new file mode 100644 index 0000000..93ef974 --- /dev/null +++ b/helpers/mknod.c @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2023 Patrick McDermott + * + * This file is part of opkg-opk. + * + * opkg-opk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * opkg-opk is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with opkg-opk. If not, see . + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_NLS +#include +#include +#define _(msgid) gettext(msgid) +#else +#define _(msgid) (msgid) +#endif + +#ifdef HAVE_GETOPT_LONG +#include +#endif + +static const char *_OPTSTRING = "m:"; +#ifdef HAVE_GETOPT_LONG +static const struct option _LONGOPTS[] = { + { + .name = "mode", + .has_arg = 1, + .flag = NULL, + .val = 'm', + }, +}; +#endif + +static const unsigned int _TIMEOUT = 10U; + +int +main(int argc, char *argv[]) +{ + char *program_name; + char *work_area; + char **exec_argv; + int arg; + mode_t mode; + int opt; + char *name; + char type; + char *end; + long int major; + long int minor; + char *specials_file_name; + char *specials_lock_name; + int fd; + unsigned int timeout; + int specials_fd; + FILE *specials_file; + struct stat stat_buf; + +#ifdef ENABLE_NLS + bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); +#ifdef HAVE_BIND_TEXTDOMAIN_CODESET + bind_textdomain_codeset(PACKAGE, "UTF-8"); +#endif + textdomain(PACKAGE); + setlocale(LC_ALL, ""); +#endif + + program_name = argv[0]; + + work_area = getenv("OPK_WORK_AREA"); + if (work_area == NULL || work_area[0] == '\0') { + fprintf(stderr, _("%s: OPK_WORK_AREA environment variable not " + "set\n"), program_name); + fprintf(stderr, _("%s: Possibly building package dependent on " + "opkg-opk with opkbuild too old to use " + "it\n"), program_name); + fprintf(stderr, _("%s: Executing system mknod instead\n"), + program_name); + exec_argv = calloc(argc + 1, sizeof(*exec_argv)); + if (exec_argv == NULL) { + fprintf(stderr, _("%s: Failed to allocate memory\n"), + program_name); + return EXIT_FAILURE; + } + exec_argv[0] = malloc(strlen(MKNOD) + 1); + if (exec_argv[0] == NULL) { + fprintf(stderr, _("%s: Failed to allocate memory\n"), + program_name); + free(exec_argv); + return EXIT_FAILURE; + } + strcpy(exec_argv[0], MKNOD); + for (arg = 1; arg < argc; ++arg) { + exec_argv[arg] = argv[arg]; + } + exec_argv[argc] = NULL; + execve(MKNOD, exec_argv, NULL); /* Shouldn't return */ + fprintf(stderr, _("%s: Failed to execute system mknod\n"), + program_name); + free(exec_argv[0]); + free(exec_argv); + return EXIT_FAILURE; + } + + mode = 0; +#ifdef HAVE_GETOPT_LONG + while ((opt = getopt_long(argc, argv, _OPTSTRING, _LONGOPTS, NULL)) + != -1) { +#else + while ((opt = getopt(argc, argv, _OPTSTRING)) != -1) { +#endif + switch(opt) { + case 'm': + /* TODO: Parse mode. */ + break; + default: + fprintf(stderr, _("%s: Warning: Unknown option:" + " \"%c\"\n"), + program_name, optopt); + return EXIT_FAILURE; + } + } + argc -= optind; + argv += optind; + + if (argc < 2) { + fprintf(stderr, _("%s: Wrong number of operands\n"), + program_name); + return EXIT_FAILURE; + } + name = argv[0]; + switch (argv[1][0]) { + case 'c': + case 'u': + type = 'c'; + break; + case 'b': + type = 'b'; + break; + case 'p': + if (argc != 2) { + fprintf(stderr, _("%s: Wrong number of " + "operands\n"), + program_name); + return EXIT_FAILURE; + } + if (mkfifo(name, mode) != 0) { + fprintf(stderr, _("%s: Failed to create link " + "\"%s\"\n"), + program_name, name); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; + default: + fprintf(stderr, _("%s: Invalid node type \"%c\"\n"), + program_name, argv[1][0]); + return EXIT_FAILURE; + } + + if (argc != 4) { + fprintf(stderr, _("%s: Wrong number of operands\n"), + program_name); + return EXIT_FAILURE; + } + major = strtol(argv[2], &end, 10); + if (*end != '\0') { + fprintf(stderr, _("%s: Invalid number \"%s\"\n"), + program_name, argv[2]); + return EXIT_FAILURE; + } + minor = strtol(argv[3], &end, 10); + if (*end != '\0') { + fprintf(stderr, _("%s: Invalid number \"%s\"\n"), + program_name, argv[3]); + return EXIT_FAILURE; + } + + specials_file_name = malloc(strlen(work_area) + strlen("/specials") + + 1 /* "\0" */); + if (specials_file_name == NULL) { + fprintf(stderr, _("%s: Failed to allocate memory\n"), + program_name); + return EXIT_FAILURE; + } + specials_lock_name = malloc(strlen(work_area) + strlen("/specials~") + + 1 /* "\0" */); + if (specials_lock_name == NULL) { + fprintf(stderr, _("%s: Failed to allocate memory\n"), + program_name); + free(specials_file_name); + return EXIT_FAILURE; + } + sprintf(specials_file_name, "%s/specials", work_area); + sprintf(specials_lock_name, "%s/specials~", work_area); + + /* Create the named node. This should be done before appending to the + * specials file, in case a file by the specified name already exists. + */ + fd = open(name, O_CREAT | O_EXCL, mode); + if (fd < 0 || close(fd) < 0) { + fprintf(stderr, _("%s: Failed to create node\n"), program_name); + free(specials_file_name); + free(specials_lock_name); + return EXIT_FAILURE; + } + + /* Acquire lock ("specials~"). */ + for (timeout = _TIMEOUT; timeout > 0; --timeout) { + specials_fd = open(specials_lock_name, O_CREAT | O_EXCL, 0644); + if (specials_fd >= 0) { + goto opened; + } + } + fprintf(stderr, _("%s: Failed to lock specials file\n"), program_name); + free(specials_file_name); + free(specials_lock_name); + unlink(name); + return EXIT_FAILURE; + + opened: + specials_file = fdopen(specials_fd, "a"); + if (specials_file == NULL) { + fprintf(stderr, _("%s: Failed to open specials file stream\n"), + program_name); + close(specials_fd); + free(specials_file_name); + free(specials_lock_name); + unlink(name); + return EXIT_FAILURE; + } + + /* If "specials" doesn't exist, write header. */ + if (stat(specials_file_name, &stat_buf) != 0) { + if (errno != ENOENT) { + fprintf(stderr, _("%s: Failed to stat specials file\n"), + program_name); + fclose(specials_file); + free(specials_file_name); + free(specials_lock_name); + unlink(name); + return EXIT_FAILURE; + } + if (fputs("version=1\n", specials_file) < 0 || + fputs("type major minor name\n", + specials_file) < 0) { + fprintf(stderr, _("%s: Failed to write specials file\n") + , program_name); + fclose(specials_file); + free(specials_file_name); + free(specials_lock_name); + unlink(name); + return EXIT_FAILURE; + } + } + if (fprintf(specials_file, "%c %8ld %8ld %s\n", type, major, minor, + name) < 0) { + fprintf(stderr, _("%s: Failed to write specials file\n"), + program_name); + fclose(specials_file); + free(specials_file_name); + free(specials_lock_name); + unlink(name); + return EXIT_FAILURE; + } + fclose(specials_file); + + if (rename(specials_lock_name, specials_file_name) != 0) { + fprintf(stderr, _("%s: Failed to move specials file\n"), + program_name); + free(specials_file_name); + free(specials_lock_name); + unlink(name); + return EXIT_FAILURE; + } + + free(specials_file_name); + free(specials_lock_name); + + return EXIT_SUCCESS; +} -- cgit v0.9.1