/* * 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 #include #include "defs.h" #include "mode.h" #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; static char *_program_name; static void __attribute__((format(__printf__, 1, 2))) _err(const char *fmt, ...) { va_list ap; fprintf(stderr, "%s: ", _program_name); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fputc('\n', stderr); } static int _strtol_or_err(const char *str, long int *l) { char *end; *l = strtol(str, &end, 10); if (*end != '\0') { _err(_("Invalid number \"%s\""), str); return OPKG_OPK_ERROR; } return OPKG_OPK_OK; } int main(int argc, char *argv[]) { char *work_area; char **exec_argv; int arg; mode_t mode; int opt; char *name; char type; 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') { _err(_("OPK_WORK_AREA environment variable not set")); _err(_("Possibly building package dependent on opkg-opk with " "opkbuild too old to use it")); _err(_("Executing system mknod instead")); exec_argv = calloc(argc + 1, sizeof(*exec_argv)); if (exec_argv == NULL) { _err(_("Failed to allocate memory")); return EXIT_FAILURE; } exec_argv[0] = malloc(strlen(MKNOD) + 1); if (exec_argv[0] == NULL) { _err(_("Failed to allocate memory")); 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 */ _err(_("Failed to execute system mknod")); 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': mode = umask(0); umask(mode); mode = 0666 ^ mode; if (opkg_opk_helper_mode_parse(mode, optarg, &mode) != OPKG_OPK_OK) { _err(_("Invalid mode \"%s\""), optarg); return EXIT_FAILURE; } break; default: _err(_("Warning: Unknown option: \"%c\""), optopt); } } argc -= optind; argv += optind; if (argc < 2) { _err(_("Wrong number of operands")); 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) { _err(_("Wrong number of operands")); return EXIT_FAILURE; } if (mkfifo(name, mode) != 0) { _err(_("Failed to create link \"%s\""), name); return EXIT_FAILURE; } return EXIT_SUCCESS; default: _err(_("Invalid node type \"%c\""), argv[1][0]); return EXIT_FAILURE; } if (argc != 4) { _err(_("Wrong number of operands")); return EXIT_FAILURE; } if (_strtol_or_err(argv[2], &major) != OPKG_OPK_OK || _strtol_or_err(argv[3], &minor) != OPKG_OPK_OK) { return EXIT_FAILURE; } specials_file_name = malloc(strlen(work_area) + strlen("/specials") + 1 /* "\0" */); if (specials_file_name == NULL) { _err(_("Failed to allocate memory")); return EXIT_FAILURE; } specials_lock_name = malloc(strlen(work_area) + strlen("/specials~") + 1 /* "\0" */); if (specials_lock_name == NULL) { _err(_("Failed to allocate memory")); 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) { _err(_("Failed to create node")); 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 | O_WRONLY, 0644); if (specials_fd >= 0) { goto opened; } } _err(_("Failed to lock specials file")); free(specials_file_name); free(specials_lock_name); unlink(name); return EXIT_FAILURE; opened: specials_file = fdopen(specials_fd, "a"); if (specials_file == NULL) { _err(_("Failed to open specials file stream")); 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) { _err(_("Failed to stat specials file")); 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) { _err(_("Failed to write specials file")); 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) { _err(_("Failed to write specials file")); 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) { _err(_("Failed to move specials file")); free(specials_file_name); free(specials_lock_name); unlink(name); return EXIT_FAILURE; } free(specials_file_name); free(specials_lock_name); return EXIT_SUCCESS; }