diff options
author | Patrick McDermott <patrick.mcdermott@libiquity.com> | 2023-06-26 11:47:00 (EDT) |
---|---|---|
committer | Patrick McDermott <patrick.mcdermott@libiquity.com> | 2023-06-26 16:47:07 (EDT) |
commit | 2c6e02bf1b6bbd374310f458b184ab1c26717cd6 (patch) | |
tree | a8c3425606fc38520bf6fa43ef6afdcd28c2a99d | |
parent | 2622c8a1e2c1cabfd0f0c6f504325b9ae21672fa (diff) |
mknod: New helper utility
-rw-r--r-- | Makefile.am | 12 | ||||
-rw-r--r-- | helpers/local.mk | 1 | ||||
-rw-r--r-- | helpers/mknod.c | 299 | ||||
-rw-r--r-- | po/POTFILES.in | 1 |
4 files changed, 312 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am index b7655fd..9e6d12c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -40,7 +40,7 @@ endif # * Our release target PACKAGE_STRING = $(PACKAGE_NAME) $(PACKAGE_VERSION)$(PACKAGE_VERSION_GIT) -bin_PROGRAMS = opkg-opk +bin_PROGRAMS = opkg-opk mknod opkg_opk_SOURCES = opkg_opk_CFLAGS = \ @@ -54,6 +54,15 @@ opkg_opk_LDADD = \ $(ZLIB_LIBS) opkg_opk_LINK = $(LINK) version.c EXTRA_opkg_opk_DEPENDENCIES = version.c + +mknod_CFLAGS = \ + $(WARN_CFLAGS) \ + $(ASAN_CFLAGS) +mknod_CPPFLAGS = \ + -DLOCALEDIR=\"$(localedir)\" +mknod_LDADD = \ + $(ASAN_CFLAGS) + CLEANFILES = version.c CONFIG_CLEAN_FILES = EXTRA_DIST = build-aux/config.rpath autogen.sh @@ -104,6 +113,7 @@ release: $(PACKAGE) $(VERSION) '$(PACKAGE_NAME)' include $(top_srcdir)/src/local.mk +include $(top_srcdir)/helpers/local.mk include $(top_srcdir)/tests/local.mk SUBDIRS = . po diff --git a/helpers/local.mk b/helpers/local.mk new file mode 100644 index 0000000..ce5ecc7 --- /dev/null +++ b/helpers/local.mk @@ -0,0 +1 @@ +mknod_SOURCES = %reldir%/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 <https://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#ifdef ENABLE_NLS +#include <locale.h> +#include <libintl.h> +#define _(msgid) gettext(msgid) +#else +#define _(msgid) (msgid) +#endif + +#ifdef HAVE_GETOPT_LONG +#include <getopt.h> +#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; +} diff --git a/po/POTFILES.in b/po/POTFILES.in index c1b37bb..27225de 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,2 +1,3 @@ src/main.c src/opk.c +helpers/mknod.c |