/* * 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; }