Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
ex_spawn.c

start a program detached or waited for, with arguments no shell touches

start a program detached or waited for, with arguments no shell touches

Author
Castagnier Mickael
Version
1.0
Date
24/09/2026

Starts a small shell script with n_spawn_detached(), passing it a file name full of the characters a shell would take apart - spaces, quotes, a semicolon, a dollar - and checks that the script received it whole: the script writes its first argument into a file of that very name, which exists afterwards only if the argument arrived as one piece. Also checks that a program that does not exist is not fatal, and that a NULL program is refused. Then the same through n_spawn_wait(): the exit code comes back and the file is there on return, a missing program is 127. Last, n_spawn_wait_cancel(): a raised flag ends a long program within seconds.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "nilorea/n_log.h"
int main(void) {
char dir[256];
char target[512];
int failures = 0;
int i = 0;
#if defined(__windows__)
n_log(LOG_NOTICE, "ex_spawn: the detached start is checked on POSIX; nothing to do here");
return 0;
#else
snprintf(dir, sizeof(dir), "%s/ex_spawn_%d", getenv("TMPDIR") ? getenv("TMPDIR") : "/tmp",
(int)getpid());
snprintf(target, sizeof(target), "%s/a file; with \"quotes\" and $HOME", dir);
{
/* The script writes "ok" into the file named by its first argument. */
const char* args[] = {"-c", "echo ok > \"$1\"", "ex_spawn", target, NULL};
char mk[300];
snprintf(mk, sizeof(mk), "mkdir -p '%s'", dir);
if (system(mk) != 0) {
n_log(LOG_ERR, "ex_spawn: cannot make %s", dir);
return 1;
}
if (n_spawn_detached("sh", args) != TRUE) {
n_log(LOG_ERR, "ex_spawn: sh could not be started");
}
}
/* Detached: nothing to wait on, so the file is looked for. */
for (i = 0; i < 100 && !file_exist(target); i++)
usleep(20000);
if (file_exist(target)) {
n_log(LOG_NOTICE, "ex_spawn: the argument arrived whole");
} else {
n_log(LOG_ERR, "ex_spawn: no file named %s appeared", target);
}
unlink(target);
rmdir(dir);
{
const char* none[] = {NULL};
/* Not found: the child reports it and exits 127; the call itself
* started it, so it is not an error here, and nothing is left. */
n_spawn_detached("ex-spawn-no-such-program", none);
}
if (n_spawn_detached(NULL, NULL) != FALSE) {
n_log(LOG_ERR, "ex_spawn: a NULL program was accepted");
}
/* The waiting form: the exit code comes back, and the file the program
* wrote is there the moment the call returns, with no polling. */
{
const char* write_it[] = {"-c", "echo ok > \"$1\"; exit 3", "ex_spawn", target, NULL};
const char* none[] = {NULL};
char mk[300];
int code = 0;
snprintf(mk, sizeof(mk), "mkdir -p '%s'", dir);
if (system(mk) != 0) {
n_log(LOG_ERR, "ex_spawn: cannot make %s", dir);
return 1;
}
if (n_spawn_wait("sh", write_it, &code) != FALSE || code != 3) {
n_log(LOG_ERR, "ex_spawn: an exit code of 3 came back as %d", code);
}
if (!file_exist(target)) {
n_log(LOG_ERR, "ex_spawn: the waited for program's file is not there on return");
}
unlink(target);
rmdir(dir);
if (n_spawn_wait("true", none, &code) != TRUE || code != 0) {
n_log(LOG_ERR, "ex_spawn: true did not succeed (%d)", code);
}
if (n_spawn_wait("ex-spawn-no-such-program", none, &code) != FALSE || code != 127) {
n_log(LOG_ERR, "ex_spawn: a missing program came back as %d, not 127", code);
}
if (n_spawn_wait(NULL, NULL, &code) != FALSE) {
n_log(LOG_ERR, "ex_spawn: a NULL program was accepted by n_spawn_wait");
}
}
/* The stoppable form. A flag left down waits as n_spawn_wait() does; a
* flag raised ends a program that would otherwise run for half a
* minute, and the call returns within the two seconds of grace. */
{
const char* none[] = {NULL};
const char* long_one[] = {"30", NULL};
volatile int stop = 0;
int code = 0;
time_t started = 0;
if (n_spawn_wait_cancel("true", none, &code, &stop) != TRUE || code != 0) {
n_log(LOG_ERR, "ex_spawn: true with a lowered flag did not succeed (%d)", code);
}
stop = 1;
started = time(NULL);
if (n_spawn_wait_cancel("sleep", long_one, &code, &stop) != FALSE) {
n_log(LOG_ERR, "ex_spawn: a stopped program reported success");
}
if (time(NULL) - started > 5) {
n_log(LOG_ERR, "ex_spawn: the stop took %ld seconds", (long)(time(NULL) - started));
}
}
n_log(LOG_NOTICE, "ex_spawn: %s", failures ? "FAILED" : "ok");
return failures ? 1 : 0;
#endif
}
static int failures
Definition ex_atlas.c:51
int main(void)
Definition ex_atlas.c:117
static void stop(int sig)
Definition ex_kafka.c:75
int n_spawn_wait(const char *program, const char *const *args, int *exit_code)
Run a program with arguments and wait for it to finish.
Definition n_common.c:369
int n_spawn_detached(const char *program, const char *const *args)
Start a program and do not wait for it.
Definition n_common.c:292
int n_spawn_wait_cancel(const char *program, const char *const *args, int *exit_code, const volatile int *cancel)
Run a program and wait for it, ending it early when asked.
Definition n_common.c:373
int file_exist(const char *filename)
test if file exist and if it's readable
Definition n_common.c:104
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:113
#define LOG_ERR
error conditions
Definition n_log.h:100
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL ).
Definition n_log.c:123
#define LOG_NOTICE
normal but significant condition
Definition n_log.h:104
Common headers and low-level functions & define.
Generic log system.