/* * 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 "defs.h" #include "error.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 int _get_work_area(int argc, char *argv[], char **work_area) { char **exec_argv; int arg; *work_area = getenv("OPK_WORK_AREA"); if (*work_area == NULL || *work_area[0] == '\0') { opkg_opk_error(_("OPK_WORK_AREA environment variable not set")); opkg_opk_error(_("Possibly building package dependent on " "opkg-opk with opkbuild too old to use " "it")); opkg_opk_error(_("Executing system mknod instead")); exec_argv = calloc(argc + 1, sizeof(*exec_argv)); if (exec_argv == NULL) { opkg_opk_error(_("Failed to allocate memory")); return OPKG_OPK_ERROR; } exec_argv[0] = malloc(strlen(MKNOD) + 1); if (exec_argv[0] == NULL) { opkg_opk_error(_("Failed to allocate memory")); free(exec_argv); return OPKG_OPK_ERROR; } 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 */ opkg_opk_error(_("Failed to execute system mknod")); free(exec_argv[0]); free(exec_argv); return OPKG_OPK_ERROR; } return OPKG_OPK_OK; } static int _strtol_or_err(const char *str, long int *l) { char *end; *l = strtol(str, &end, 10); if (*end != '\0') { opkg_opk_error(_("Invalid number \"%s\""), str); return OPKG_OPK_ERROR; } return OPKG_OPK_OK; } int main(int argc, char *argv[]) { char *work_area; mode_t mode; int opt; char *name; char type; long int major; long int minor; size_t work_area_len; size_t specials_file_name_len; 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 opkg_opk_set_program_name(argv[0]); if (_get_work_area(argc, argv, &work_area) != OPKG_OPK_OK) { return EXIT_FAILURE; } mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; #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) { opkg_opk_error(_("Invalid mode \"%s\""), optarg); return EXIT_FAILURE; } break; default: opkg_opk_error(_("Warning: Unknown option: " "\"%c\""), optopt); } } argc -= optind; argv += optind; if (argc < 2) { opkg_opk_error(_("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) { opkg_opk_error(_("Wrong number of operands")); return EXIT_FAILURE; } if (mkfifo(name, mode) != 0) { opkg_opk_errno(_("Failed to create link " "\"%s\""), name); return EXIT_FAILURE; } return EXIT_SUCCESS; default: opkg_opk_error(_("Invalid node type \"%c\""), argv[1][0]); return EXIT_FAILURE; } if (argc != 4) { opkg_opk_error(_("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; } /* Find path of file relative to binary package data directory within * build work area. */ work_area_len = strlen(work_area); if (work_area[work_area_len - 1] == '/') { --work_area_len; } if (strncmp(name, work_area, work_area_len) != 0 || name[work_area_len] != '/') { opkg_opk_error(_("Node \"%s\" not within work area \"%s\""), name, work_area); return EXIT_FAILURE; } do { ++work_area_len; if (name[work_area_len] == '\0') { opkg_opk_error(_("Node \"%s\" not within a binary " "package data directory"), name); return EXIT_FAILURE; } } while (name[work_area_len] != '/'); specials_file_name_len = strlen(work_area) + strlen("/specials") + 1; specials_file_name = malloc(specials_file_name_len * 2 + 1); if (specials_file_name == NULL) { opkg_opk_error(_("Failed to allocate memory")); return EXIT_FAILURE; } specials_lock_name = specials_file_name + specials_file_name_len ; 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) { opkg_opk_errno(_("Failed to create node")); goto err0; } /* 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; } } opkg_opk_errno(_("Failed to lock specials file")); goto err1; opened: close(specials_fd); specials_file = fopen(specials_file_name, "a"); if (specials_file == NULL) { opkg_opk_errno(_("Failed to open specials file stream")); goto err2; } /* If "specials" is empty, write header. */ if (fstat(fileno(specials_file), &stat_buf) != 0) { opkg_opk_errno(_("Failed to stat specials file")); goto err3; } if (stat_buf.st_size == 0) { if (fputs("version=1\n", specials_file) < 0 || fputs("type major minor name\n", specials_file) < 0) { opkg_opk_errno(_("Failed to write specials file")); goto err3; } } if (fprintf(specials_file, "%c %8ld %8ld %s\n", type, major, minor, name + work_area_len) < 0) { opkg_opk_errno(_("Failed to write specials file")); goto err3; } fclose(specials_file); unlink(specials_lock_name); free(specials_file_name); return EXIT_SUCCESS; err3: fclose(specials_file); err2: unlink(specials_lock_name); err1: unlink(name); err0: free(specials_file_name); return EXIT_FAILURE; }