Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
n_common.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
27#include "nilorea/n_common.h"
28#include "nilorea/n_log.h"
29#include "nilorea/n_str.h"
30
31#include <limits.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <libgen.h>
35
36#include "nilorea/n_windows.h"
37#include <errno.h>
38#include <strings.h>
39#include <unistd.h>
40#include <sys/types.h>
41#include <sys/stat.h>
42#include <fcntl.h>
43
44#ifndef __windows__
45#include <sys/wait.h>
46#endif
47
52void n_abort(char const* format, ...) {
53 char str[1024] = "";
54 va_list args;
55 va_start(args, format);
56
57 vsnprintf(str, sizeof str, format, args);
58 va_end(args);
59 fprintf(stderr, "%s", str);
60 exit(1);
61}
62
69int get_computer_name(char* computer_name, size_t len) {
70 if (!computer_name || len == 0) return FALSE;
71 memset(computer_name, 0, len);
72#ifdef WIN32
73 TCHAR infoBuf[len];
74 DWORD bufCharCount = (DWORD)len;
75 if (GetComputerName(infoBuf, &bufCharCount)) {
76 memcpy(computer_name, infoBuf, len);
77 } else {
78 strncpy(computer_name, "Unknown_Host_Name", len - 1);
79 computer_name[len - 1] = '\0';
80 return FALSE;
81 }
82#else
83 if (gethostname(computer_name, len) == -1) {
84 int error = errno;
85 strncpy(computer_name, "Unknown_Host_Name", len - 1);
86 computer_name[len - 1] = '\0';
87 n_log(LOG_ERR, "%s on gethostname !", strerror(error));
88 return FALSE;
89 }
90#endif
91 return TRUE;
92} /* get_computer_name */
93
99int file_exist(const char* filename) {
100 FILE* file = NULL;
101 if ((file = fopen(filename, "r")) != NULL) {
102 fclose(file);
103 return 1;
104 }
105 return 0;
106} /* file_exist */
107
112char* get_prog_dir(void) {
113 char strbuf[PATH_MAX] = "";
114
115 int error = 0;
116#ifdef __windows__
117 unsigned long int bytes = GetModuleFileName(NULL, strbuf, PATH_MAX);
118 error = errno;
119 if (bytes != 0) {
120 return strdup(dirname(strbuf));
121 }
122#else
123 char procbuf[PATH_MAX] = "";
124#ifdef __linux__
125 snprintf(procbuf, PATH_MAX, "/proc/%d/exe", (int)getpid());
126#elif defined __sun
127 snprintf(procbuf, PATH_MAX, "/proc/%d/path/a.out", (int)getpid());
128#endif
129 ssize_t bytes = MIN(readlink(procbuf, strbuf, PATH_MAX), PATH_MAX - 1);
130 error = errno;
131 if (bytes >= 0) {
132 strbuf[bytes] = '\0';
133 return strdup(dirname(strbuf));
134 }
135#endif
136 fprintf(stderr, "%s", strerror(error));
137 return NULL;
138} /* get_prog_dir */
139
144char* get_prog_name(void) {
145 char strbuf[PATH_MAX] = "";
146 int error = 0;
147#ifdef __windows__
148 unsigned long int bytes = GetModuleFileName(NULL, strbuf, PATH_MAX);
149 error = errno;
150 if (bytes != 0) {
151 return strdup(basename(strbuf));
152 }
153#else
154 char procbuf[PATH_MAX] = "";
155#ifdef __linux__
156 snprintf(procbuf, PATH_MAX, "/proc/%d/exe", (int)getpid());
157#elif defined __sun
158 snprintf(procbuf, PATH_MAX, "/proc/%d/path/a.out", (int)getpid());
159#endif
160 ssize_t bytes = MIN(readlink(procbuf, strbuf, PATH_MAX), PATH_MAX - 1);
161 error = errno;
162 if (bytes >= 0) {
163 strbuf[bytes] = '\0';
164 return strdup(basename(strbuf));
165 }
166#endif
167 fprintf(stderr, "%s", strerror(error));
168 return NULL;
169} /* get_prog_name */
170
179int n_popen(char* cmd, size_t read_buf_size, void** nstr_output, int* ret) {
180 __n_assert(cmd, return FALSE);
181
182 if (read_buf_size > INT_MAX) {
183 n_log(LOG_ERR, "read_buf_size buffer size is too large for fgets (%zu>%d)", read_buf_size, INT_MAX);
184 return FALSE;
185 }
186 if (read_buf_size > 65536) {
187 n_log(LOG_ERR, "read_buf_size %zu exceeds safe stack limit", read_buf_size);
188 return FALSE;
189 }
190
191 N_STR* output_pointer = new_nstr(read_buf_size + 1);
192
193 FILE* fp = NULL;
194 int status = 0;
195 char read_buf[read_buf_size];
196
197 fp = popen(cmd, "r");
198 int err = errno;
199 if (fp == NULL) {
200 n_log(LOG_ERR, "popen( %s ) returned NULL , %s (%d)", cmd, strerror(err), err);
201 free_nstr(&output_pointer);
202 return FALSE;
203 }
204 while (fgets(read_buf, (int)read_buf_size, fp) != NULL) {
205 size_t length = strlen(read_buf);
206 if (length > 0 && read_buf[length - 1] == '\n') {
207 read_buf[length - 1] = '\0';
208 }
209 nstrprintf_cat(output_pointer, "%s", read_buf);
210 }
211 status = pclose(fp);
212 err = errno;
213 if (status == -1) {
214 n_log(LOG_ERR, "pclose( %s ) returned -1 , %s (%d)", cmd, strerror(err), err);
215 free_nstr(&output_pointer);
216 return FALSE;
217 }
218 if (ret) (*ret) = WEXITSTATUS(status);
219 if (nstr_output) {
220 (*nstr_output) = output_pointer;
221 } else {
222 free_nstr(&output_pointer);
223 }
224 return TRUE;
225} /* n_popen( ... ) */
226
231void log_environment(int loglevel) {
232#if defined(_WIN32) || defined(__MINGW32__) || defined(__windows__)
233 LPCH env_block = GetEnvironmentStrings();
234 if (!env_block) return;
235 for (LPCH var = env_block; *var != '\0'; var += strlen(var) + 1) {
236 n_log(loglevel, "env: %s", var);
237 }
238 FreeEnvironmentStrings(env_block);
239#else
240 extern char** environ;
241 for (char** env = environ; *env != NULL; env++) {
242 n_log(loglevel, "env: %s", *env);
243 }
244#endif
245}
246
247#ifndef __windows__
252void sigchld_handler(int sig) {
253 // waitpid() might overwrite errno, so we save and restore it:
254 int saved_errno = errno;
255
256 while (waitpid(-1, NULL, WNOHANG) > 0);
257
258 errno = saved_errno;
259 n_log(LOG_DEBUG, "Signal %d", sig);
260}
261
267 struct sigaction sa;
268 sa.sa_handler = sigchld_handler; // reap all dead processes
269 sigemptyset(&sa.sa_mask);
270 sa.sa_flags = SA_RESTART;
271 if (sigaction(SIGCHLD, &sa, NULL) == -1) {
272 int error = errno;
273 n_log(LOG_ERR, "sigaction error: %s", strerror(error));
274 return FALSE;
275 }
276 return TRUE;
277}
278
286
293 int error = 0;
294
295 /* Fork off the parent process */
296 pid_t pid = fork();
297 error = errno;
298 if (pid < 0) {
299 n_log(LOG_ERR, "Error: unable to fork child: %s", strerror(error));
300 exit(1);
301 }
302 if (pid > 0) {
303 n_log(LOG_NOTICE, "Child started successfuly !");
304 exit(0);
305 }
306
307 if (!(mode & N_DAEMON_NO_SETSID)) {
308 /* On success: The child process becomes session leader
309 * setsid is detaching the process from the terminal */
310 if (setsid() < 0) {
311 error = errno;
312 n_log(LOG_ERR, "Error: unable to set session leader with setsid(): %s", strerror(error));
313 exit(1);
314 } else {
315 n_log(LOG_NOTICE, "Session leader set !");
316 }
317 }
318
319 if (!(mode & N_DAEMON_NO_SIGCHLD_IGN)) {
320 /* Ignore signal sent from child to parent process */
321 signal(SIGCHLD, SIG_IGN);
322 }
323
325 /* catching signal */
327 }
328
329 if (!(mode & N_DAEMON_NO_DOUBLE_FORK)) {
330 /* Double fork to detach from the parent, and be adopted by init process */
331 pid = fork();
332 error = errno;
333 if (pid < 0) {
334 n_log(LOG_ERR, "Error: unable to double fork child: %s", strerror(error));
335 exit(1);
336 }
337 if (pid > 0) {
338 n_log(LOG_NOTICE, "Double fork child started successfuly !");
339 exit(0);
340 }
341 }
342
343 if (!(mode & N_DAEMON_NO_UMASK)) {
344 /* reset umask */
345 umask(0);
346 }
347
348 if (!(mode & N_DAEMON_NO_CHDIR)) {
349 /* set working directory */
350 if (chdir("/") < 0) {
351 error = errno;
352 n_log(LOG_ERR, "couldn't chdir() to '/': %s", strerror(error));
353 return FALSE;
354 }
355 }
356
357 if (!(mode & N_DAEMON_NO_CLOSE)) {
358 /* Close all open file descriptors except standard ones (stdin, stdout, stderr) */
359 for (int x = 3; x < sysconf(_SC_OPEN_MAX); x++) {
360 close(x);
361 }
362 }
363
365 /* redirect stdin,stdout,stderr to /dev/null */
366 int fd = open("/dev/null", O_RDWR, 0);
367 if (fd != -1) {
368 dup2(fd, STDIN_FILENO);
369 dup2(fd, STDOUT_FILENO);
370 dup2(fd, STDERR_FILENO);
371 if (fd > 2) {
372 close(fd);
373 }
374 } else {
375 n_log(LOG_ERR, "Failed to open /dev/null: %s", strerror(errno));
376 }
377 }
378
379 return TRUE;
380} /* n_daemonize(...) */
381
389pid_t system_nb(const char* command, int* infp, int* outfp) {
390 __n_assert(command, return -1);
391
392 int p_stdin[2] = {-1, -1};
393 int p_stdout[2] = {-1, -1};
394 pid_t pid = -1;
395
396 if (outfp != NULL) {
397 if (pipe(p_stdin) != 0)
398 return -1;
399 }
400 if (infp != NULL) {
401 if (pipe(p_stdout) != 0) {
402 if (outfp != NULL) {
403 close(p_stdin[0]);
404 close(p_stdin[1]);
405 }
406 return (-1);
407 }
408 }
409
410 pid = fork();
411
412 if (pid < 0) {
413 n_log(LOG_ERR, "Couldn't fork command %s", command);
414 if (outfp != NULL) {
415 close(p_stdin[0]);
416 close(p_stdin[1]);
417 }
418 if (infp != NULL) {
419 close(p_stdout[0]);
420 close(p_stdout[1]);
421 }
422 return pid;
423 }
424 if (pid == 0) {
425 if (outfp != NULL) {
426 close(p_stdin[1]);
427 dup2(p_stdin[0], 0);
428 }
429 if (infp != NULL) {
430 close(p_stdout[0]);
431 dup2(p_stdout[1], 1);
432 }
433 execlp("sh", "sh", "-c", command, (char*)NULL);
434 // should never get here
435 int error = errno;
436 n_log(LOG_ERR, "%s:%d: exec failed: %s", __FILE__, __LINE__, strerror(error));
437 exit(42);
438 }
439 if (infp != NULL) {
440 close(p_stdout[1]);
441 *infp = p_stdout[0];
442 }
443 if (outfp != NULL) {
444 close(p_stdin[0]);
445 *outfp = p_stdin[1];
446 }
447 return pid;
448} /* system_nb */
449
450#endif /* ifndef __windows__ */
451
458void N_HIDE_STR(char* buf, ...) {
459 size_t it = 0;
460 va_list args;
461
462 va_start(args, buf);
463
464 int arg = 0;
465
466 while ((arg = va_arg(args, int))) {
467 buf[it] = (char)arg;
468 it++;
469 }
470 buf[it] = '\0';
471 va_end(args);
472} /* N_HIDE_STR */
473
479char* n_get_file_extension(char path[]) {
480 char* result = NULL;
481 size_t i = 0, n = 0;
482
483 __n_assert(path, return "");
484
485 n = strlen(path);
486 i = n - 1;
487 while ((i > 0) && (path[i] != '.') && (path[i] != '/') && (path[i] != '\\')) {
488 i--;
489 }
490 if ((i > 0) && (i < n - 1) && (path[i] == '.') && (path[i - 1] != '/') && (path[i - 1] != '\\')) {
491 result = path + i;
492 } else {
493 result = path + n;
494 }
495 return result;
496}
static int mode
void log_environment(int loglevel)
log environment variables in syslog
Definition n_common.c:231
#define N_DAEMON_NO_UMASK
daemonize flag: do not call umask( 0 )
Definition n_common.h:512
char * get_prog_name(void)
get current program name
Definition n_common.c:144
#define N_DAEMON_NO_DOUBLE_FORK
daemonize flag: do not double fork
Definition n_common.h:508
void N_HIDE_STR(char *buf,...)
store a hidden version of a string
Definition n_common.c:458
#define N_DAEMON_NO_SIGCHLD_IGN
daemonize flag: do not ignore SIGCHLD
Definition n_common.h:516
#define N_DAEMON_NO_CHDIR
daemonize flag: do not call chdir("/")
Definition n_common.h:514
#define __n_assert(__ptr, __ret)
macro to assert things
Definition n_common.h:278
#define N_DAEMON_NO_CLOSE
daemonize flag: no descriptor close at all
Definition n_common.h:504
#define N_DAEMON_NO_SIGCHLD_HANDLER
daemonize flag: do not use internal zombie SIGCHLD handler
Definition n_common.h:518
#define N_DAEMON_NO_STD_REDIRECT
daemonize flag: just do not redirect stdin/out/err to /dev/null
Definition n_common.h:506
int get_computer_name(char *computer_name, size_t len)
get the computer name
Definition n_common.c:69
#define N_DAEMON_NO_SETSID
daemonize flag: do not call setsid
Definition n_common.h:510
void sigchld_handler(int sig)
Handles SIGCHLD issues when forking.
Definition n_common.c:252
pid_t system_nb(const char *command, int *infp, int *outfp)
Non blocking system call.
Definition n_common.c:389
char * n_get_file_extension(char path[])
get extension of path+filename
Definition n_common.c:479
char * get_prog_dir(void)
get current program running directory
Definition n_common.c:112
int n_daemonize_ex(int mode)
Daemonize program.
Definition n_common.c:292
void n_abort(char const *format,...)
abort program with a text
Definition n_common.c:52
int sigchld_handler_installer()
install signal SIGCHLD handler to reap zombie processes
Definition n_common.c:266
int n_daemonize(void)
Daemonize program.
Definition n_common.c:283
int file_exist(const char *filename)
test if file exist and if it's readable
Definition n_common.c:99
int n_popen(char *cmd, size_t read_buf_size, void **nstr_output, int *ret)
launch a command and return output and status
Definition n_common.c:179
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:88
#define LOG_DEBUG
debug-level messages
Definition n_log.h:83
#define LOG_ERR
error conditions
Definition n_log.h:75
#define LOG_NOTICE
normal but significant condition
Definition n_log.h:79
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
Definition n_str.h:201
#define nstrprintf_cat(__nstr_var, __format,...)
Macro to quickly allocate and sprintf and cat to a N_STR.
Definition n_str.h:119
N_STR * new_nstr(NSTRBYTE size)
create a new N_STR string
Definition n_str.c:206
A box including a string and his lenght.
Definition n_str.h:60
Common headers and low-level functions & define.
Generic log system.
N_STR and string function declaration.