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