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