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