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
27
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#include <time.h>
48#include <pthread.h>
49#else
50/* ShellExecuteA, which windows.h leaves out under WIN32_LEAN_AND_MEAN. */
51#include <shellapi.h>
52#endif
53
58void n_abort(char const* format, ...) {
59 char str[1024] = "";
60 va_list args;
61 va_start(args, format);
62
63 vsnprintf(str, sizeof str, format, args);
64 va_end(args);
65 fprintf(stderr, "%s", str);
66 exit(1);
67}
68
75int get_computer_name(char* computer_name, size_t len) {
76 if (!computer_name || len == 0) return FALSE;
77 memset(computer_name, 0, len);
78#ifdef WIN32
79 TCHAR infoBuf[len];
80 DWORD bufCharCount = (DWORD)len;
81 if (GetComputerName(infoBuf, &bufCharCount)) {
82 memcpy(computer_name, infoBuf, len);
83 } else {
84 strncpy(computer_name, "Unknown_Host_Name", len - 1);
85 computer_name[len - 1] = '\0';
86 return FALSE;
87 }
88#else
89 if (gethostname(computer_name, len) == -1) {
90 int error = errno;
91 strncpy(computer_name, "Unknown_Host_Name", len - 1);
92 computer_name[len - 1] = '\0';
93 n_log(LOG_ERR, "%s on gethostname !", strerror(error));
94 return FALSE;
95 }
96#endif
97 return TRUE;
98} /* get_computer_name */
99
105int file_exist(const char* filename) {
106 FILE* file = NULL;
107 if ((file = fopen(filename, "r")) != NULL) {
108 fclose(file);
109 return 1;
110 }
111 return 0;
112} /* file_exist */
113
118char* get_prog_dir(void) {
119 char strbuf[PATH_MAX] = "";
120
121 int error = 0;
122#ifdef __windows__
123 unsigned long int bytes = GetModuleFileName(NULL, strbuf, PATH_MAX);
124 error = errno;
125 if (bytes != 0) {
126 return strdup(dirname(strbuf));
127 }
128#else
129 char procbuf[PATH_MAX] = "";
130#ifdef __linux__
131 snprintf(procbuf, PATH_MAX, "/proc/%d/exe", (int)getpid());
132#elif defined __sun
133 snprintf(procbuf, PATH_MAX, "/proc/%d/path/a.out", (int)getpid());
134#endif
135 ssize_t bytes = MIN(readlink(procbuf, strbuf, PATH_MAX), PATH_MAX - 1);
136 error = errno;
137 if (bytes >= 0) {
138 strbuf[bytes] = '\0';
139 return strdup(dirname(strbuf));
140 }
141#endif
142 fprintf(stderr, "%s", strerror(error));
143 return NULL;
144} /* get_prog_dir */
145
150char* get_prog_name(void) {
151 char strbuf[PATH_MAX] = "";
152 int error = 0;
153#ifdef __windows__
154 unsigned long int bytes = GetModuleFileName(NULL, strbuf, PATH_MAX);
155 error = errno;
156 if (bytes != 0) {
157 return strdup(basename(strbuf));
158 }
159#else
160 char procbuf[PATH_MAX] = "";
161#ifdef __linux__
162 snprintf(procbuf, PATH_MAX, "/proc/%d/exe", (int)getpid());
163#elif defined __sun
164 snprintf(procbuf, PATH_MAX, "/proc/%d/path/a.out", (int)getpid());
165#endif
166 ssize_t bytes = MIN(readlink(procbuf, strbuf, PATH_MAX), PATH_MAX - 1);
167 error = errno;
168 if (bytes >= 0) {
169 strbuf[bytes] = '\0';
170 return strdup(basename(strbuf));
171 }
172#endif
173 fprintf(stderr, "%s", strerror(error));
174 return NULL;
175} /* get_prog_name */
176
185int n_popen(char* cmd, size_t read_buf_size, void** nstr_output, int* ret) {
186 __n_assert(cmd, return FALSE);
187
188 if (read_buf_size > INT_MAX) {
189 n_log(LOG_ERR, "read_buf_size buffer size is too large for fgets (%zu>%d)", read_buf_size, INT_MAX);
190 return FALSE;
191 }
192 if (read_buf_size > 65536) {
193 n_log(LOG_ERR, "read_buf_size %zu exceeds safe stack limit", read_buf_size);
194 return FALSE;
195 }
196
197 N_STR* output_pointer = new_nstr(read_buf_size + 1);
198
199 FILE* fp = NULL;
200 int status = 0;
201 char read_buf[read_buf_size];
202
203 fp = popen(cmd, "r");
204 int err = errno;
205 if (fp == NULL) {
206 n_log(LOG_ERR, "popen( %s ) returned NULL , %s (%d)", cmd, strerror(err), err);
207 free_nstr(&output_pointer);
208 return FALSE;
209 }
210 while (fgets(read_buf, (int)read_buf_size, fp) != NULL) {
211 size_t length = strlen(read_buf);
212 if (length > 0 && read_buf[length - 1] == '\n') {
213 read_buf[length - 1] = '\0';
214 }
215 nstrprintf_cat(output_pointer, "%s", read_buf);
216 }
217 status = pclose(fp);
218 err = errno;
219 if (status == -1) {
220 n_log(LOG_ERR, "pclose( %s ) returned -1 , %s (%d)", cmd, strerror(err), err);
221 free_nstr(&output_pointer);
222 return FALSE;
223 }
224 if (ret) (*ret) = WEXITSTATUS(status);
225 if (nstr_output) {
226 (*nstr_output) = output_pointer;
227 } else {
228 free_nstr(&output_pointer);
229 }
230 return TRUE;
231} /* n_popen( ... ) */
232
233#ifdef __windows__
247static int append_quoted(char* out, size_t out_len, size_t* used, const char* arg) {
248 size_t slashes = 0;
249 const char* p = NULL;
250
251#define N_PUT(c) \
252 do { \
253 if (*used + 1 >= out_len) \
254 return FALSE; \
255 out[(*used)++] = (c); \
256 } while (0)
257
258 if (*used)
259 N_PUT(' ');
260 N_PUT('"');
261 for (p = arg; *p; p++) {
262 if (*p == '\\') {
263 slashes++;
264 continue;
265 }
266 if (*p == '"') {
267 size_t k = 0;
268
269 for (k = 0; k < slashes * 2 + 1; k++)
270 N_PUT('\\');
271 } else {
272 size_t k = 0;
273
274 for (k = 0; k < slashes; k++)
275 N_PUT('\\');
276 }
277 slashes = 0;
278 N_PUT(*p);
279 }
280 {
281 size_t k = 0;
282
283 for (k = 0; k < slashes * 2; k++)
284 N_PUT('\\');
285 }
286 N_PUT('"');
287 out[*used] = '\0';
288#undef N_PUT
289 return TRUE;
290}
291#endif
292
293int n_spawn_detached(const char* program, const char* const* args) {
294 __n_assert(program, return FALSE);
295#ifdef __windows__
296 {
297 char line[32768];
298 size_t used = 0;
299 STARTUPINFOA si;
300 PROCESS_INFORMATION pi;
301 size_t i = 0;
302
303 line[0] = '\0';
304 if (!append_quoted(line, sizeof(line), &used, program))
305 return FALSE;
306 for (i = 0; args && args[i]; i++) {
307 if (!append_quoted(line, sizeof(line), &used, args[i])) {
308 n_log(LOG_ERR, "n_spawn_detached: the command line is too long");
309 return FALSE;
310 }
311 }
312 memset(&si, 0, sizeof(si));
313 si.cb = sizeof(si);
314 memset(&pi, 0, sizeof(pi));
315 if (!CreateProcessA(NULL, line, NULL, NULL, FALSE, DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP,
316 NULL, NULL, &si, &pi)) {
317 n_log(LOG_ERR, "n_spawn_detached: cannot start %s (%lu)", program, (unsigned long)GetLastError());
318 return FALSE;
319 }
320 CloseHandle(pi.hThread);
321 CloseHandle(pi.hProcess);
322 return TRUE;
323 }
324#else
325 {
326 const char* argv[64];
327 size_t i = 0, n = 0;
328 pid_t child = 0;
329 int status = 0;
330
331 argv[n++] = program;
332 for (i = 0; args && args[i]; i++) {
333 if (n >= sizeof(argv) / sizeof(argv[0]) - 1) {
334 n_log(LOG_ERR, "n_spawn_detached: more than %zu arguments", sizeof(argv) / sizeof(argv[0]) - 2);
335 return FALSE;
336 }
337 argv[n++] = args[i];
338 }
339 argv[n] = NULL;
340
341 /* Twice: the first child starts a session and forks the program,
342 * then leaves at once, so the program is adopted by init and never
343 * waits as a zombie on a caller that will not reap it. Only calls
344 * that are safe after a fork in a threaded process are made there. */
345 child = fork();
346 if (child < 0) {
347 n_log(LOG_ERR, "n_spawn_detached: fork failed: %s", strerror(errno));
348 return FALSE;
349 }
350 if (child == 0) {
351 pid_t grandchild = 0;
352
353 setsid();
354 grandchild = fork();
355 if (grandchild == 0) {
356 execvp(program, (char* const*)argv);
357 _exit(127);
358 }
359 _exit(grandchild < 0 ? 1 : 0);
360 }
361 if (waitpid(child, &status, 0) < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) {
362 n_log(LOG_ERR, "n_spawn_detached: could not start %s", program);
363 return FALSE;
364 }
365 return TRUE;
366 }
367#endif
368}
369
370int n_spawn_wait(const char* program, const char* const* args, int* exit_code) {
371 return n_spawn_wait_cancel(program, args, exit_code, NULL);
372}
373
374int n_spawn_wait_cancel(const char* program, const char* const* args, int* exit_code, const volatile int* cancel) {
375 if (exit_code)
376 *exit_code = -1;
377 __n_assert(program, return FALSE);
378#ifdef __windows__
379 {
380 char line[32768];
381 size_t used = 0;
382 STARTUPINFOA si;
383 PROCESS_INFORMATION pi;
384 DWORD code = 0;
385 size_t i = 0;
386
387 line[0] = '\0';
388 if (!append_quoted(line, sizeof(line), &used, program))
389 return FALSE;
390 for (i = 0; args && args[i]; i++) {
391 if (!append_quoted(line, sizeof(line), &used, args[i])) {
392 n_log(LOG_ERR, "n_spawn_wait: the command line is too long");
393 return FALSE;
394 }
395 }
396 memset(&si, 0, sizeof(si));
397 si.cb = sizeof(si);
398 memset(&pi, 0, sizeof(pi));
399 /* No console window for a tool run in the background. */
400 if (!CreateProcessA(NULL, line, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
401 n_log(LOG_ERR, "n_spawn_wait: cannot start %s (%lu)", program, (unsigned long)GetLastError());
402 return FALSE;
403 }
404 CloseHandle(pi.hThread);
405 if (!cancel) {
406 WaitForSingleObject(pi.hProcess, INFINITE);
407 } else {
408 /* A tenth of a second between looks at the flag: short enough
409 * that a Stop is felt at once, long enough to cost nothing. */
410 while (WaitForSingleObject(pi.hProcess, 100) == WAIT_TIMEOUT) {
411 if (*cancel) {
412 TerminateProcess(pi.hProcess, 1);
413 WaitForSingleObject(pi.hProcess, INFINITE);
414 CloseHandle(pi.hProcess);
415 n_log(LOG_NOTICE, "n_spawn_wait_cancel: %s stopped", program);
416 return FALSE;
417 }
418 }
419 }
420 if (!GetExitCodeProcess(pi.hProcess, &code))
421 code = (DWORD)-1;
422 CloseHandle(pi.hProcess);
423 if (exit_code)
424 *exit_code = (int)code;
425 return code == 0 ? TRUE : FALSE;
426 }
427#else
428 {
429 const char* argv[64];
430 size_t i = 0, n = 0;
431 pid_t child = 0;
432 int status = 0;
433
434 argv[n++] = program;
435 for (i = 0; args && args[i]; i++) {
436 if (n >= sizeof(argv) / sizeof(argv[0]) - 1) {
437 n_log(LOG_ERR, "n_spawn_wait: more than %zu arguments", sizeof(argv) / sizeof(argv[0]) - 2);
438 return FALSE;
439 }
440 argv[n++] = args[i];
441 }
442 argv[n] = NULL;
443
444 child = fork();
445 if (child < 0) {
446 n_log(LOG_ERR, "n_spawn_wait: fork failed: %s", strerror(errno));
447 return FALSE;
448 }
449 if (child == 0) {
450 /* Only calls that are safe after a fork in a threaded process. */
451 execvp(program, (char* const*)argv);
452 _exit(127);
453 }
454 if (cancel) {
455 /* Looked at every tenth of a second while the child runs. A
456 * stop asks it to end, gives it two seconds to write nothing
457 * more, then ends it, and reaps it either way so no zombie is
458 * left behind. */
459 int asked = 0;
460
461 for (;;) {
462 struct timespec tick = {0, 100000000L};
463 pid_t got = waitpid(child, &status, WNOHANG);
464
465 if (got == child)
466 break;
467 if (got < 0 && errno != EINTR) {
468 n_log(LOG_ERR, "n_spawn_wait_cancel: waiting for %s failed: %s", program, strerror(errno));
469 return FALSE;
470 }
471 if (*cancel) {
472 if (asked == 0)
473 kill(child, SIGTERM);
474 else if (asked == 20)
475 kill(child, SIGKILL);
476 asked++;
477 }
478 nanosleep(&tick, NULL);
479 }
480 if (asked) {
481 n_log(LOG_NOTICE, "n_spawn_wait_cancel: %s stopped", program);
482 return FALSE;
483 }
484 } else {
485 /* Retried on EINTR: a signal for another thread must not abandon
486 * a child that is still running. */
487 while (waitpid(child, &status, 0) < 0) {
488 if (errno != EINTR) {
489 n_log(LOG_ERR, "n_spawn_wait: waiting for %s failed: %s", program, strerror(errno));
490 return FALSE;
491 }
492 }
493 }
494 if (!WIFEXITED(status)) {
495 n_log(LOG_ERR, "n_spawn_wait: %s was ended by a signal", program);
496 return FALSE;
497 }
498 if (exit_code)
499 *exit_code = WEXITSTATUS(status);
500 if (WEXITSTATUS(status) == 127)
501 n_log(LOG_ERR, "n_spawn_wait: %s could not be found or started", program);
502 return WEXITSTATUS(status) == 0 ? TRUE : FALSE;
503 }
504#endif
505}
506
510#ifdef __windows__
512 HANDLE input;
514 HANDLE process;
515#else
517 int input;
519 pid_t child;
520#endif
522 char program[256];
523};
524
525N_SPAWN_PIPE* n_spawn_pipe_open(const char* program, const char* const* args, const char* log_path) {
526 N_SPAWN_PIPE* sp = NULL;
527
528 __n_assert(program, return NULL);
529 Malloc(sp, N_SPAWN_PIPE, 1);
530 __n_assert(sp, return NULL);
531 snprintf(sp->program, sizeof(sp->program), "%s", program);
532#ifdef __windows__
533 {
534 char line[32768];
535 size_t used = 0, i = 0;
536 STARTUPINFOA si;
537 PROCESS_INFORMATION pi;
538 SECURITY_ATTRIBUTES sa;
539 HANDLE read_end = NULL, log = INVALID_HANDLE_VALUE;
540
541 line[0] = '\0';
542 if (!append_quoted(line, sizeof(line), &used, program)) {
543 Free(sp);
544 return NULL;
545 }
546 for (i = 0; args && args[i]; i++) {
547 if (!append_quoted(line, sizeof(line), &used, args[i])) {
548 n_log(LOG_ERR, "n_spawn_pipe_open: the command line is too long");
549 Free(sp);
550 return NULL;
551 }
552 }
553 memset(&sa, 0, sizeof(sa));
554 sa.nLength = sizeof(sa);
555 sa.bInheritHandle = TRUE;
556 if (!CreatePipe(&read_end, &sp->input, &sa, 0)) {
557 n_log(LOG_ERR, "n_spawn_pipe_open: no pipe (%lu)", (unsigned long)GetLastError());
558 Free(sp);
559 return NULL;
560 }
561 /* Only the read end goes to the program: an inherited write end
562 * would keep its input open after ours is closed. */
563 SetHandleInformation(sp->input, HANDLE_FLAG_INHERIT, 0);
564 if (log_path) {
565 log = CreateFileA(log_path, GENERIC_WRITE, FILE_SHARE_READ, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
566 if (log == INVALID_HANDLE_VALUE)
567 n_log(LOG_ERR, "n_spawn_pipe_open: cannot write %s (%lu)", log_path, (unsigned long)GetLastError());
568 }
569 memset(&si, 0, sizeof(si));
570 si.cb = sizeof(si);
571 si.dwFlags = STARTF_USESTDHANDLES;
572 si.hStdInput = read_end;
573 si.hStdOutput = log != INVALID_HANDLE_VALUE ? log : GetStdHandle(STD_OUTPUT_HANDLE);
574 si.hStdError = log != INVALID_HANDLE_VALUE ? log : GetStdHandle(STD_ERROR_HANDLE);
575 memset(&pi, 0, sizeof(pi));
576 if (!CreateProcessA(NULL, line, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
577 n_log(LOG_ERR, "n_spawn_pipe_open: cannot start %s (%lu)", program, (unsigned long)GetLastError());
578 CloseHandle(read_end);
579 CloseHandle(sp->input);
580 if (log != INVALID_HANDLE_VALUE)
581 CloseHandle(log);
582 Free(sp);
583 return NULL;
584 }
585 /* The program holds its own copies now. */
586 CloseHandle(read_end);
587 if (log != INVALID_HANDLE_VALUE)
588 CloseHandle(log);
589 CloseHandle(pi.hThread);
590 sp->process = pi.hProcess;
591 return sp;
592 }
593#else
594 {
595 const char* argv[64];
596 size_t i = 0, n = 0;
597 int fds[2] = {-1, -1};
598 int log = -1;
599
600 argv[n++] = program;
601 for (i = 0; args && args[i]; i++) {
602 if (n >= sizeof(argv) / sizeof(argv[0]) - 1) {
603 n_log(LOG_ERR, "n_spawn_pipe_open: more than %zu arguments", sizeof(argv) / sizeof(argv[0]) - 2);
604 Free(sp);
605 return NULL;
606 }
607 argv[n++] = args[i];
608 }
609 argv[n] = NULL;
610 if (pipe(fds) != 0) {
611 n_log(LOG_ERR, "n_spawn_pipe_open: no pipe: %s", strerror(errno));
612 Free(sp);
613 return NULL;
614 }
615 /* The write end is closed in every program started from here on,
616 * this one included; the read end only in the others. */
617 fcntl(fds[1], F_SETFD, FD_CLOEXEC);
618 fcntl(fds[0], F_SETFD, FD_CLOEXEC);
619 if (log_path) {
620 log = open(log_path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
621 if (log < 0)
622 n_log(LOG_ERR, "n_spawn_pipe_open: cannot write %s: %s", log_path, strerror(errno));
623 }
624 sp->child = fork();
625 if (sp->child < 0) {
626 n_log(LOG_ERR, "n_spawn_pipe_open: fork failed: %s", strerror(errno));
627 close(fds[0]);
628 close(fds[1]);
629 if (log >= 0)
630 close(log);
631 Free(sp);
632 return NULL;
633 }
634 if (sp->child == 0) {
635 /* Only calls that are safe after a fork in a threaded process.
636 * dup2 clears close-on-exec on the copies it makes. */
637 if (dup2(fds[0], STDIN_FILENO) < 0)
638 _exit(127);
639 if (log >= 0) {
640 dup2(log, STDOUT_FILENO);
641 dup2(log, STDERR_FILENO);
642 }
643 execvp(program, (char* const*)argv);
644 _exit(127);
645 }
646 close(fds[0]);
647 if (log >= 0)
648 close(log);
649 sp->input = fds[1];
650 return sp;
651 }
652#endif
653}
654
655int n_spawn_pipe_write(N_SPAWN_PIPE* sp, const void* data, size_t len) {
656 const char* at = (const char*)data;
657
658 __n_assert(sp, return FALSE);
659 if (len && !data)
660 return FALSE;
661#ifdef __windows__
662 if (!sp->input)
663 return FALSE;
664 while (len) {
665 DWORD chunk = len > 0x40000000u ? 0x40000000u : (DWORD)len, wrote = 0;
666
667 if (!WriteFile(sp->input, at, chunk, &wrote, NULL) || wrote == 0) {
668 n_log(LOG_ERR, "n_spawn_pipe_write: %s stopped reading (%lu)", sp->program, (unsigned long)GetLastError());
669 return FALSE;
670 }
671 at += wrote;
672 len -= wrote;
673 }
674 return TRUE;
675#else
676 {
677 sigset_t pipe_set, before;
678 int ok = TRUE, raised = 0;
679
680 if (sp->input < 0)
681 return FALSE;
682 /* A program that has gone sends SIGPIPE to the writer, whose default
683 * is to end the whole caller. Held back for this thread only, which
684 * leaves the rest of the process's handling alone, and taken off the
685 * pending set afterwards if it came. */
686 sigemptyset(&pipe_set);
687 sigaddset(&pipe_set, SIGPIPE);
688 pthread_sigmask(SIG_BLOCK, &pipe_set, &before);
689 while (len) {
690 ssize_t wrote = write(sp->input, at, len);
691
692 if (wrote < 0) {
693 if (errno == EINTR)
694 continue;
695 if (errno == EPIPE)
696 raised = 1;
697 n_log(LOG_ERR, "n_spawn_pipe_write: %s stopped reading: %s", sp->program, strerror(errno));
698 ok = FALSE;
699 break;
700 }
701 at += wrote;
702 len -= (size_t)wrote;
703 }
704 if (raised) {
705 struct timespec none = {0, 0};
706
707 while (sigtimedwait(&pipe_set, NULL, &none) < 0 && errno == EINTR)
708 ;
709 }
710 pthread_sigmask(SIG_SETMASK, &before, NULL);
711 return ok;
712 }
713#endif
714}
715
716int n_spawn_pipe_close(N_SPAWN_PIPE** pipe_ptr, int stop, int* exit_code) {
717 N_SPAWN_PIPE* sp = NULL;
718 int ok = FALSE;
719
720 if (exit_code)
721 *exit_code = -1;
722 if (!pipe_ptr || !*pipe_ptr)
723 return FALSE;
724 sp = *pipe_ptr;
725#ifdef __windows__
726 {
727 DWORD code = (DWORD)-1;
728
729 if (stop)
730 TerminateProcess(sp->process, 1);
731 /* Closing the input is the end of the stream the program waits for. */
732 if (sp->input)
733 CloseHandle(sp->input);
734 WaitForSingleObject(sp->process, INFINITE);
735 if (!stop && GetExitCodeProcess(sp->process, &code)) {
736 if (exit_code)
737 *exit_code = (int)code;
738 ok = code == 0 ? TRUE : FALSE;
739 }
740 CloseHandle(sp->process);
741 }
742#else
743 {
744 int status = 0, asked = 0;
745
746 if (stop)
747 kill(sp->child, SIGTERM);
748 /* Closing the input is the end of the stream the program waits for. */
749 if (sp->input >= 0)
750 close(sp->input);
751 sp->input = -1;
752 for (;;) {
753 pid_t got = waitpid(sp->child, &status, stop ? WNOHANG : 0);
754
755 if (got == sp->child)
756 break;
757 if (got < 0) {
758 if (errno == EINTR)
759 continue;
760 n_log(LOG_ERR, "n_spawn_pipe_close: waiting for %s failed: %s", sp->program, strerror(errno));
761 Free(sp);
762 *pipe_ptr = NULL;
763 return FALSE;
764 }
765 /* Stopping: two seconds of grace, then the end. */
766 if (++asked == 20)
767 kill(sp->child, SIGKILL);
768 {
769 struct timespec tick = {0, 100000000L};
770
771 nanosleep(&tick, NULL);
772 }
773 }
774 if (stop) {
775 n_log(LOG_NOTICE, "n_spawn_pipe_close: %s stopped", sp->program);
776 } else if (!WIFEXITED(status)) {
777 n_log(LOG_ERR, "n_spawn_pipe_close: %s was ended by a signal", sp->program);
778 } else {
779 if (exit_code)
780 *exit_code = WEXITSTATUS(status);
781 if (WEXITSTATUS(status) == 127)
782 n_log(LOG_ERR, "n_spawn_pipe_close: %s could not be found or started", sp->program);
783 ok = WEXITSTATUS(status) == 0 ? TRUE : FALSE;
784 }
785 }
786#endif
787 Free(sp);
788 *pipe_ptr = NULL;
789 return ok;
790}
791
792int n_open_with_default(const char* path) {
793 __n_assert(path, return FALSE);
794#ifdef __windows__
795 {
796 HINSTANCE done = ShellExecuteA(NULL, "open", path, NULL, NULL, SW_SHOWNORMAL);
797
798 /* ShellExecute reports success as a value above 32. */
799 if ((INT_PTR)done <= 32) {
800 n_log(LOG_ERR, "n_open_with_default: cannot open %s (%ld)", path, (long)(INT_PTR)done);
801 return FALSE;
802 }
803 return TRUE;
804 }
805#else
806 {
807 const char* args[2] = {path, NULL};
808#ifdef __APPLE__
809 return n_spawn_detached("open", args);
810#else
811 return n_spawn_detached("xdg-open", args);
812#endif
813 }
814#endif
815}
816
821void log_environment(int loglevel) {
822#if defined(_WIN32) || defined(__MINGW32__) || defined(__windows__)
823 LPCH env_block = GetEnvironmentStrings();
824 if (!env_block) return;
825 for (LPCH var = env_block; *var != '\0'; var += strlen(var) + 1) {
826 n_log(loglevel, "env: %s", var);
827 }
828 FreeEnvironmentStrings(env_block);
829#else
830 extern char** environ;
831 for (char** env = environ; *env != NULL; env++) {
832 n_log(loglevel, "env: %s", *env);
833 }
834#endif
835}
836
837#ifndef __windows__
842void sigchld_handler(int sig) {
843 // waitpid() might overwrite errno, so we save and restore it:
844 int saved_errno = errno;
845
846 while (waitpid(-1, NULL, WNOHANG) > 0);
847
848 errno = saved_errno;
849 n_log(LOG_DEBUG, "Signal %d", sig);
850}
851
857 struct sigaction sa;
858 sa.sa_handler = sigchld_handler; // reap all dead processes
859 sigemptyset(&sa.sa_mask);
860 sa.sa_flags = SA_RESTART;
861 if (sigaction(SIGCHLD, &sa, NULL) == -1) {
862 int error = errno;
863 n_log(LOG_ERR, "sigaction error: %s", strerror(error));
864 return FALSE;
865 }
866 return TRUE;
867}
868
876
883 int error = 0;
884
885 /* Fork off the parent process */
886 pid_t pid = fork();
887 error = errno;
888 if (pid < 0) {
889 n_log(LOG_ERR, "Error: unable to fork child: %s", strerror(error));
890 exit(1);
891 }
892 if (pid > 0) {
893 n_log(LOG_NOTICE, "Child started successfuly !");
894 exit(0);
895 }
896
897 if (!(mode & N_DAEMON_NO_SETSID)) {
898 /* On success: The child process becomes session leader
899 * setsid is detaching the process from the terminal */
900 if (setsid() < 0) {
901 error = errno;
902 n_log(LOG_ERR, "Error: unable to set session leader with setsid(): %s", strerror(error));
903 exit(1);
904 } else {
905 n_log(LOG_NOTICE, "Session leader set !");
906 }
907 }
908
909 if (!(mode & N_DAEMON_NO_SIGCHLD_IGN)) {
910 /* Ignore signal sent from child to parent process */
911 signal(SIGCHLD, SIG_IGN);
912 }
913
915 /* catching signal */
917 }
918
919 if (!(mode & N_DAEMON_NO_DOUBLE_FORK)) {
920 /* Double fork to detach from the parent, and be adopted by init process */
921 pid = fork();
922 error = errno;
923 if (pid < 0) {
924 n_log(LOG_ERR, "Error: unable to double fork child: %s", strerror(error));
925 exit(1);
926 }
927 if (pid > 0) {
928 n_log(LOG_NOTICE, "Double fork child started successfuly !");
929 exit(0);
930 }
931 }
932
933 if (!(mode & N_DAEMON_NO_UMASK)) {
934 /* reset umask */
935 umask(0);
936 }
937
938 if (!(mode & N_DAEMON_NO_CHDIR)) {
939 /* set working directory */
940 if (chdir("/") < 0) {
941 error = errno;
942 n_log(LOG_ERR, "couldn't chdir() to '/': %s", strerror(error));
943 return FALSE;
944 }
945 }
946
947 if (!(mode & N_DAEMON_NO_CLOSE)) {
948 /* Close all open file descriptors except standard ones (stdin, stdout, stderr) */
949 for (int x = 3; x < sysconf(_SC_OPEN_MAX); x++) {
950 close(x);
951 }
952 }
953
955 /* redirect stdin,stdout,stderr to /dev/null */
956 int fd = open("/dev/null", O_RDWR, 0);
957 if (fd != -1) {
958 dup2(fd, STDIN_FILENO);
959 dup2(fd, STDOUT_FILENO);
960 dup2(fd, STDERR_FILENO);
961 if (fd > 2) {
962 close(fd);
963 }
964 } else {
965 n_log(LOG_ERR, "Failed to open /dev/null: %s", strerror(errno));
966 }
967 }
968
969 return TRUE;
970} /* n_daemonize(...) */
971
979pid_t system_nb(const char* command, int* infp, int* outfp) {
980 __n_assert(command, return -1);
981
982 int p_stdin[2] = {-1, -1};
983 int p_stdout[2] = {-1, -1};
984 pid_t pid = -1;
985
986 if (outfp != NULL) {
987 if (pipe(p_stdin) != 0)
988 return -1;
989 }
990 if (infp != NULL) {
991 if (pipe(p_stdout) != 0) {
992 if (outfp != NULL) {
993 close(p_stdin[0]);
994 close(p_stdin[1]);
995 }
996 return (-1);
997 }
998 }
999
1000 pid = fork();
1001
1002 if (pid < 0) {
1003 n_log(LOG_ERR, "Couldn't fork command %s", command);
1004 if (outfp != NULL) {
1005 close(p_stdin[0]);
1006 close(p_stdin[1]);
1007 }
1008 if (infp != NULL) {
1009 close(p_stdout[0]);
1010 close(p_stdout[1]);
1011 }
1012 return pid;
1013 }
1014 if (pid == 0) {
1015 if (outfp != NULL) {
1016 close(p_stdin[1]);
1017 dup2(p_stdin[0], 0);
1018 }
1019 if (infp != NULL) {
1020 close(p_stdout[0]);
1021 dup2(p_stdout[1], 1);
1022 }
1023 execlp("sh", "sh", "-c", command, (char*)NULL);
1024 // should never get here
1025 int error = errno;
1026 n_log(LOG_ERR, "%s:%d: exec failed: %s", __FILE__, __LINE__, strerror(error));
1027 exit(42);
1028 }
1029 if (infp != NULL) {
1030 close(p_stdout[1]);
1031 *infp = p_stdout[0];
1032 }
1033 if (outfp != NULL) {
1034 close(p_stdin[0]);
1035 *outfp = p_stdin[1];
1036 }
1037 return pid;
1038} /* system_nb */
1039
1040#endif /* ifndef __windows__ */
1041
1047
1048void N_HIDE_STR(char* buf, ...) {
1049 size_t it = 0;
1050 va_list args;
1051
1052 va_start(args, buf);
1053
1054 int arg = 0;
1055
1056 while ((arg = va_arg(args, int))) {
1057 buf[it] = (char)arg;
1058 it++;
1059 }
1060 buf[it] = '\0';
1061 va_end(args);
1062} /* N_HIDE_STR */
1063
1069char* n_get_file_extension(char path[]) {
1070 char* result = NULL;
1071 size_t i = 0, n = 0;
1072
1073 __n_assert(path, return "");
1074
1075 n = strlen(path);
1076 i = n - 1;
1077 while ((i > 0) && (path[i] != '.') && (path[i] != '/') && (path[i] != '\\')) {
1078 i--;
1079 }
1080 if ((i > 0) && (i < n - 1) && (path[i] == '.') && (path[i - 1] != '/') && (path[i - 1] != '\\')) {
1081 result = path + i;
1082 } else {
1083 result = path + n;
1084 }
1085 return result;
1086}
static int mode
static void stop(int sig)
Definition ex_kafka.c:75
volatile sig_atomic_t done
void line(BITMAP *bmp, int x1, int y1, int x2, int y2, int color)
draw a line
void log_environment(int loglevel)
log environment variables in syslog
Definition n_common.c:821
#define N_DAEMON_NO_UMASK
daemonize flag: do not call umask( 0 )
Definition n_common.h:666
char * get_prog_name(void)
get current program name
Definition n_common.c:150
int n_open_with_default(const char *path)
Open a file or a URL with whatever the desktop opens it with.
Definition n_common.c:792
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:370
int n_spawn_detached(const char *program, const char *const *args)
Start a program and do not wait for it.
Definition n_common.c:293
#define N_DAEMON_NO_DOUBLE_FORK
daemonize flag: do not double fork
Definition n_common.h:662
void N_HIDE_STR(char *buf,...)
store a hidden version of a string
Definition n_common.c:1048
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:374
#define N_DAEMON_NO_SIGCHLD_IGN
daemonize flag: do not ignore SIGCHLD
Definition n_common.h:670
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
Definition n_common.h:204
#define N_DAEMON_NO_CHDIR
daemonize flag: do not call chdir("/")
Definition n_common.h:668
#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:658
#define N_DAEMON_NO_SIGCHLD_HANDLER
daemonize flag: do not use internal zombie SIGCHLD handler
Definition n_common.h:672
#define N_DAEMON_NO_STD_REDIRECT
daemonize flag: just do not redirect stdin/out/err to /dev/null
Definition n_common.h:660
int get_computer_name(char *computer_name, size_t len)
get the computer name
Definition n_common.c:75
#define N_DAEMON_NO_SETSID
daemonize flag: do not call setsid
Definition n_common.h:664
void sigchld_handler(int sig)
Handles SIGCHLD issues when forking.
Definition n_common.c:842
pid_t system_nb(const char *command, int *infp, int *outfp)
Non blocking system call.
Definition n_common.c:979
char * n_get_file_extension(char path[])
get extension of path+filename
Definition n_common.c:1069
int n_spawn_pipe_close(N_SPAWN_PIPE **pipe_ptr, int stop, int *exit_code)
End a program's input, or end the program, and wait for it.
Definition n_common.c:716
char * get_prog_dir(void)
get current program running directory
Definition n_common.c:118
int n_daemonize_ex(int mode)
Daemonize program.
Definition n_common.c:882
void n_abort(char const *format,...)
abort program with a text
Definition n_common.c:58
int n_daemonize(void)
Daemonize program.
Definition n_common.c:873
N_SPAWN_PIPE * n_spawn_pipe_open(const char *program, const char *const *args, const char *log_path)
Start a program whose standard input is fed by the caller.
Definition n_common.c:525
int n_spawn_pipe_write(N_SPAWN_PIPE *sp, const void *data, size_t len)
Write to a program's standard input, all of it.
Definition n_common.c:655
int file_exist(const char *filename)
test if file exist and if it's readable
Definition n_common.c:105
#define Free(__ptr)
Free Handler to get errors.
Definition n_common.h:263
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:185
int sigchld_handler_installer(void)
install signal SIGCHLD handler to reap zombie processes
Definition n_common.c:856
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:113
#define LOG_DEBUG
debug-level messages
Definition n_log.h:108
#define LOG_ERR
error conditions
Definition n_log.h:100
#define LOG_NOTICE
normal but significant condition
Definition n_log.h:104
#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:209
A box including a string and his lenght.
Definition n_str.h:61
char program[256]
for the log lines
Definition n_common.c:522
pid_t child
the program
Definition n_common.c:519
int input
write end of the program's standard input, -1 once closed
Definition n_common.c:517
A program started with its standard input on a pipe the caller writes to (n_spawn_pipe_open()).
Definition n_common.c:509
Common headers and low-level functions & define.
Generic log system.
N_STR and string function declaration.