summaryrefslogtreecommitdiffstats
path: root/src/libnetdata/spawn_server/spawn_popen.c
blob: f354b1f2a77d6a43b73452043a4f2b044c866905 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// SPDX-License-Identifier: GPL-3.0-or-later

#include "spawn_popen.h"

SPAWN_SERVER *netdata_main_spawn_server = NULL;
static SPINLOCK netdata_main_spawn_server_spinlock = NETDATA_SPINLOCK_INITIALIZER;

bool netdata_main_spawn_server_init(const char *name, int argc, const char **argv) {
    if(netdata_main_spawn_server == NULL) {
        spinlock_lock(&netdata_main_spawn_server_spinlock);
        if(netdata_main_spawn_server == NULL)
            netdata_main_spawn_server = spawn_server_create(SPAWN_SERVER_OPTION_EXEC, name, NULL, argc, argv);
        spinlock_unlock(&netdata_main_spawn_server_spinlock);
    }

    return netdata_main_spawn_server != NULL;
}

void netdata_main_spawn_server_cleanup(void) {
    if(netdata_main_spawn_server) {
        spinlock_lock(&netdata_main_spawn_server_spinlock);
        if(netdata_main_spawn_server) {
            spawn_server_destroy(netdata_main_spawn_server);
            netdata_main_spawn_server = NULL;
        }
        spinlock_unlock(&netdata_main_spawn_server_spinlock);
    }
}

POPEN_INSTANCE *spawn_popen_run_argv(const char **argv) {
    netdata_main_spawn_server_init(NULL, 0, NULL);

    SPAWN_INSTANCE *si = spawn_server_exec(netdata_main_spawn_server, nd_log_collectors_fd(),
        0, argv, NULL, 0, SPAWN_INSTANCE_TYPE_EXEC);

    if(si == NULL) return NULL;

    POPEN_INSTANCE *pi = mallocz(sizeof(*pi));
    pi->si = si;
    pi->child_stdin_fp = fdopen(spawn_server_instance_write_fd(si), "w");
    pi->child_stdout_fp = fdopen(spawn_server_instance_read_fd(si), "r");

    if(!pi->child_stdin_fp) {
        nd_log(NDLS_COLLECTORS, NDLP_ERR, "Cannot open FILE on child's stdin on fd %d.", spawn_server_instance_write_fd(si));
        goto cleanup;
    }

    if(!pi->child_stdout_fp) {
        nd_log(NDLS_COLLECTORS, NDLP_ERR, "Cannot open FILE on child's stdout on fd %d.", spawn_server_instance_read_fd(si));
        goto cleanup;
    }

    return pi;

cleanup:
    if(pi->child_stdin_fp) { fclose(pi->child_stdin_fp); spawn_server_instance_write_fd(si); }
    if(pi->child_stdout_fp) { fclose(pi->child_stdout_fp); spawn_server_instance_read_fd_unset(si); }
    spawn_server_exec_kill(netdata_main_spawn_server, si);
    freez(pi);
    return NULL;
}

POPEN_INSTANCE *spawn_popen_run_variadic(const char *cmd, ...) {
    va_list args;
    va_list args_copy;
    int argc = 0;

    // Start processing variadic arguments
    va_start(args, cmd);

    // Make a copy of args to count the number of arguments
    va_copy(args_copy, args);
    while (va_arg(args_copy, char *) != NULL) argc++;
    va_end(args_copy);

    // Allocate memory for argv array (+2 for cmd and NULL terminator)
    const char *argv[argc + 2];

    // Populate the argv array
    argv[0] = cmd;

    for (int i = 1; i <= argc; i++)
        argv[i] = va_arg(args, const char *);

    argv[argc + 1] = NULL; // NULL-terminate the array

    // End processing variadic arguments
    va_end(args);

    return spawn_popen_run_argv(argv);
}

POPEN_INSTANCE *spawn_popen_run(const char *cmd) {
    if(!cmd || !*cmd) return NULL;
    
    const char *argv[] = {
        "/bin/sh",
        "-c",
        cmd,
        NULL
    };
    return spawn_popen_run_argv(argv);
}

static int spawn_popen_status_rc(int status) {
    if(WIFEXITED(status))
        return WEXITSTATUS(status);

    if(WIFSIGNALED(status)) {
        int sig = WTERMSIG(status);
        switch(sig) {
            case SIGTERM:
            case SIGPIPE:
                return 0;

            default:
                return -1;
        }
    }

    return -1;
}

int spawn_popen_wait(POPEN_INSTANCE *pi) {
    if(!pi) return -1;

    fclose(pi->child_stdin_fp); pi->child_stdin_fp = NULL; spawn_server_instance_write_fd_unset(pi->si);
    fclose(pi->child_stdout_fp); pi->child_stdout_fp = NULL; spawn_server_instance_read_fd_unset(pi->si);
    int status = spawn_server_exec_wait(netdata_main_spawn_server, pi->si);
    freez(pi);
    return spawn_popen_status_rc(status);
}

int spawn_popen_kill(POPEN_INSTANCE *pi) {
    if(!pi) return -1;

    fclose(pi->child_stdin_fp); pi->child_stdin_fp = NULL; spawn_server_instance_write_fd_unset(pi->si);
    fclose(pi->child_stdout_fp); pi->child_stdout_fp = NULL; spawn_server_instance_read_fd_unset(pi->si);
    int status = spawn_server_exec_kill(netdata_main_spawn_server, pi->si);
    freez(pi);
    return spawn_popen_status_rc(status);
}