blob: 827fddb9a4b25553959f1e3b9e3c494bfeede1f4 [file] [log] [blame] [edit]
/*
* BRLTTY - A background process providing access to the console screen (when in
* text mode) for a blind person using a refreshable braille display.
*
* Copyright (C) 1995-2023 by The BRLTTY Developers.
*
* BRLTTY comes with ABSOLUTELY NO WARRANTY.
*
* This is free software, placed under the terms of the
* GNU Lesser General Public License, as published by the Free Software
* Foundation; either version 2.1 of the License, or (at your option) any
* later version. Please see the file LICENSE-LGPL for details.
*
* Web Page: http://brltty.app/
*
* This software is maintained by Dave Mielke <dave@mielke.cc>.
*/
#include "prologue.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <locale.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <limits.h>
#include "program.h"
#include "messages.h"
#include "pgmpath.h"
#include "pid.h"
#include "log.h"
#include "file.h"
#include "parse.h"
#include "system.h"
const char standardStreamArgument[] = "-";
const char standardInputName[] = "<standard-input>";
const char standardOutputName[] = "<standard-output>";
const char standardErrorName[] = "<standard-error>";
const char *programPath;
const char *programName;
static char *
testProgram (const char *directory, const char *name) {
char *path;
if ((path = makePath(directory, name))) {
if (testProgramPath(path)) return path;
free(path);
}
return NULL;
}
static char *
findProgram (const char *name) {
char *path = NULL;
const char *string;
if ((string = getenv("PATH"))) {
int count;
char **array;
if ((array = splitString(string, ':', &count))) {
for (unsigned int index=0; index<count; index+=1) {
const char *directory = array[index];
if (!*directory) directory = CURRENT_DIRECTORY_NAME;
if ((path = testProgram(directory, name))) break;
}
deallocateStrings(array);
}
}
return path;
}
void
beginProgram (int argumentCount, char **argumentVector) {
#if defined(GRUB_RUNTIME)
#else /* at exit */
atexit(endProgram);
#endif /* at exit */
setlocale(LC_ALL, "");
initializeSystemObject();
ensureAllMessagesProperties();
if ((programPath = getProgramPath())) {
registerProgramMemory("program-path", &programPath);
} else {
programPath = argumentVector[0];
}
if (!isExplicitPath(programPath)) {
char *path = findProgram(programPath);
if (!path) path = testProgram(CURRENT_DIRECTORY_NAME, programPath);
if (path) programPath = path;
}
if (isExplicitPath(programPath)) {
#if defined(HAVE_REALPATH) && defined(PATH_MAX)
if (!isAbsolutePath(programPath)) {
char buffer[PATH_MAX];
char *path = realpath(programPath, buffer);
if (path) {
char *realPath = strdup(path);
if (realPath) {
programPath = realPath;
} else {
logMallocError();
}
} else {
logSystemError("realpath");
}
}
#endif /* defined(HAVE_REALPATH) && defined(PATH_MAX) */
if (!isAbsolutePath(programPath)) {
char *directory;
if ((directory = getWorkingDirectory())) {
char *path;
if ((path = makePath(directory, programPath))) programPath = path;
free(directory);
}
}
}
programName = locatePathName(programPath);
pushLogPrefix(programName);
}
const char *
getProgramDirectory (void) {
static const char *programDirectory = NULL;
if (!programDirectory) {
if ((programDirectory = getPathDirectory(programPath))) {
logMessage(LOG_DEBUG, "program directory: %s", programDirectory);
registerProgramMemory("program-directory", &programDirectory);
} else {
logMessage(LOG_WARNING, gettext("cannot determine program directory"));
programDirectory = "";
}
}
if (!*programDirectory) return NULL;
return programDirectory;
}
int
fixInstallPath (char **path) {
const char *programDirectory = getProgramDirectory();
if (!programDirectory) programDirectory = CURRENT_DIRECTORY_NAME;
const char *problem = strtext("cannot fix install path");
char *newPath = makePath(programDirectory, *path);
if (newPath) {
if (changeStringSetting(path, newPath)) {
if (isAbsolutePath(*path)) {
problem = NULL;
} else {
problem = strtext("install path not absolute");
}
}
free(newPath);
}
if (!problem) return 1;
logMessage(LOG_WARNING, "%s: %s", gettext(problem), *path);
return 0;
}
char *
makeProgramPath (const char *name) {
const char *directory = getProgramDirectory();
if (!directory) return NULL;
return makePath(directory, name);
}
char *
makeCommandPath (const char *name) {
char *path = NULL;
char *directory = NULL;
if (changeStringSetting(&directory, COMMANDS_DIRECTORY)) {
if (fixInstallPath(&directory)) {
path = makePath(directory, name);
}
}
if (directory) free(directory);
return path;
}
int
createPidFile (const char *path, ProcessIdentifier pid) {
#if defined(GRUB_RUNTIME)
errno = EROFS;
#else /* create pid file */
if (!pid) pid = getProcessIdentifier();
if (path && *path) {
if (!ensurePathDirectory(path)) return 0;
typedef enum {PFS_ready, PFS_stale, PFS_clash, PFS_error} PidFileState;
PidFileState state = PFS_error;
lockUmask();
int file = open(
path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR
#ifdef S_IRGRP
| S_IRGRP
#endif /* S_IRGRP */
#ifdef S_IROTH
| S_IROTH
#endif /* S_IROTH */
);
unlockUmask();
if (file != -1) {
int locked = acquireFileLock(file, 1);
if (locked || (errno == ENOSYS)) {
char buffer[0X20];
ssize_t length;
if ((length = read(file, buffer, sizeof(buffer))) != -1) {
ProcessIdentifier oldPid;
char terminator;
int count;
if (length == sizeof(buffer)) length -= 1;
buffer[length] = 0;
count = sscanf(buffer, "%" SCNpid "%c", &oldPid, &terminator);
state = PFS_stale;
if ((count == 1) ||
((count == 2) && ((terminator == '\n') || (terminator == '\r')))) {
if (oldPid == pid) {
state = PFS_ready;
} else if (testProcessIdentifier(oldPid)) {
logMessage(LOG_ERR, "instance already running: PID=%" PRIpid, oldPid);
state = PFS_clash;
}
}
} else {
logSystemError("read");
}
if (state == PFS_stale) {
state = PFS_error;
if (lseek(file, 0, SEEK_SET) != -1) {
if (ftruncate(file, 0) != -1) {
length = snprintf(buffer, sizeof(buffer), "%" PRIpid "\n", pid);
if (write(file, buffer, length) != -1) {
state = PFS_ready;
} else {
logSystemError("write");
}
} else {
logSystemError("ftruncate");
}
} else {
logSystemError("lseek");
}
}
if (locked) releaseFileLock(file);
}
close(file);
} else {
logMessage(LOG_WARNING, "%s: %s: %s",
gettext("cannot open process identifier file"),
path, strerror(errno));
}
switch (state) {
case PFS_ready:
return 1;
case PFS_clash:
errno = EEXIST;
break;
case PFS_error:
break;
default:
logMessage(LOG_WARNING, "unexpected PID file state: %u", state);
break;
}
}
#endif /* create pid file */
return 0;
}
int
cancelProgram (const char *pidFile) {
int cancelled = 0;
FILE *file;
if ((file = fopen(pidFile, "r"))) {
char buffer[0X100];
const char *line;
if ((line = fgets(buffer, sizeof(buffer), file))) {
char *end;
long int pid = strtol(line, &end, 10);
if (!*end || isspace((unsigned char)*end)) {
if (cancelProcess(pid)) cancelled = 1;
}
}
fclose(file);
} else {
logMessage(LOG_ERR, "%s: %s: %s",
gettext("pid file open error"),
pidFile, strerror(errno));
}
return cancelled;
}
typedef struct ProgramExitEntryStruct ProgramExitEntry;
static ProgramExitEntry *programExitEntries = NULL;
struct ProgramExitEntryStruct {
ProgramExitEntry *next;
char *name;
ProgramExitHandler *handler;
void *data;
};
void
onProgramExit (const char *name, ProgramExitHandler *handler, void *data) {
ProgramExitEntry *pxe;
if ((pxe = malloc(sizeof(*pxe)))) {
pxe->name = strdup(name);
pxe->handler = handler;
pxe->data = data;
pxe->next = programExitEntries;
programExitEntries = pxe;
logMessage(LOG_DEBUG, "program exit event added: %s", name);
} else {
logMallocError();
}
}
static void
exitProgramMemory (void *data) {
char **pointer = data;
if (*pointer) {
free(*pointer);
*pointer = NULL;
}
}
void
registerProgramMemory (const char *name, void *pointer) {
onProgramExit(name, exitProgramMemory, pointer);
}
void
endProgram (void) {
logMessage(LOG_DEBUG, "stopping program components");
while (programExitEntries) {
ProgramExitEntry *pxe = programExitEntries;
const char *name = pxe->name;
programExitEntries = pxe->next;
if (!name) name = "unknown";
logMessage(LOG_DEBUG, "stopping program component: %s", name);
pxe->handler(pxe->data);
if (pxe->name) free(pxe->name);
free(pxe);
}
logMessage(LOG_DEBUG, "stopped program components");
popLogPrefix();
}