event_exec.c 10.7 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
/* Icecast
 *
 * This program is distributed under the GNU General Public License, version 2.
 * A copy of this license is included with this source.
 *
 * Copyright 2014,      Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

Philipp Schafft's avatar
Philipp Schafft committed
13
14
#include <string.h>
#include <errno.h>
15
16
17
18
19
20
21
22
23
24
25
26

#ifndef _WIN32
#include <sys/wait.h>
/* for __setup_empty_script_environment() */
#include <sys/stat.h>
#include <fcntl.h>
#endif

#include "event.h"
#include "logging.h"
#define CATMODULE "event_exec"

27
28
29
30
31
32
33
34
typedef enum event_exec_argvtype_tag {
    ARGVTYPE_NO_DEFAULTS = 0,
    ARGVTYPE_ONLY_URI,
    ARGVTYPE_URI_AND_TRIGGER,
    ARGVTYPE_LEGACY,

    ARGVTYPE_DFAULT = ARGVTYPE_LEGACY
} event_exec_argvtype_t;
35
36
37
38

typedef struct event_exec {
    /* name and path of executable */
    char *executable;
39
40
41
42
43
44

    /* what to add to argv[] */
    event_exec_argvtype_t argvtype;

    /* actual argv[] */
    char **argv;
45
46
} event_exec_t;

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
/* OS independed code: */
static inline size_t __argvtype2offset(event_exec_argvtype_t argvtype) {
    switch (argvtype) {
        case ARGVTYPE_NO_DEFAULTS:     return 1; break;
        case ARGVTYPE_ONLY_URI:        return 2; break;
        case ARGVTYPE_URI_AND_TRIGGER: return 3; break;
        case ARGVTYPE_LEGACY:          return 2; break;
        default: return 0; break; /* This should never happen. */
    }
}

/* BEFORE RELEASE 2.4.2 DOCUMENT: Document names of possible values. */
static inline event_exec_argvtype_t __str2argvtype(const char *str) {
    if (!str)
        str = "(BAD VALUE)";

    if (strcmp(str, "default") == 0) {
        return ARGVTYPE_DFAULT;
    } else if (strcmp(str, "no_defaults") == 0) {
        return ARGVTYPE_NO_DEFAULTS;
    } else if (strcmp(str, "uri") == 0) {
        return ARGVTYPE_ONLY_URI;
    } else if (strcmp(str, "uri_and_trigger") == 0) {
        return ARGVTYPE_URI_AND_TRIGGER;
    } else if (strcmp(str, "legacy") == 0) {
        return ARGVTYPE_LEGACY;
    } else {
        ICECAST_LOG_ERROR("Unknown argument type %s, using \"default\"");
        return ARGVTYPE_DFAULT;
    }
}

static inline char **__setup_argv(event_exec_t *self, event_t *event) {
    self->argv[0] = self->executable;

    switch (self->argvtype) {
        case ARGVTYPE_NO_DEFAULTS:
            /* nothing to do */
        break;
        case ARGVTYPE_URI_AND_TRIGGER:
            self->argv[2] = event->trigger ? event->trigger : "";
        /* fall through */
        case ARGVTYPE_ONLY_URI:
            self->argv[1] = event->uri ? event->uri : "";
        break;
        case ARGVTYPE_LEGACY:
            /* This mode is similar to ARGVTYPE_ONLY_URI
             * but if URI is unknown the parameter is skipped!
             */
            if (event->uri) {
                self->argv[1] = event->uri;
            } else {
                self->argv[1] = self->executable;
                return &self->argv[1];
            }
        break;
    }

    return self->argv;
}

/* OS depended code: */
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#ifdef _WIN32
/* TODO #2101: Implement script executing on win* */
#else
/* this sets up the new environment for script execution.
 * We ignore most failtures as we can not handle them anyway.
 */
#ifdef HAVE_SETENV
static inline void __update_environ(const char *name, const char *value) {
    if (!name || !value) return;
    setenv(name, value, 1);
}
#else
#define __update_environ(x,y)
#endif
123
static inline void __setup_environ(ice_config_t *config, event_exec_t *self, event_t *event) {
124
125
126
127
    mount_proxy *mountinfo;
    source_t *source;
    char buf[80];

128
    /* BEFORE RELEASE 2.4.2 DOCUMENT: Document all those env vars. */
129
130
131
132
133
134
    __update_environ("ICECAST_VERSION",   ICECAST_VERSION_STRING);
    __update_environ("ICECAST_HOSTNAME",  config->hostname);
    __update_environ("ICECAST_ADMIN",     config->admin);
    __update_environ("ICECAST_LOGDIR",    config->log_dir);
    __update_environ("EVENT_URI",         event->uri);
    __update_environ("EVENT_TRIGGER",     event->trigger); /* new name */
135
    __update_environ("SOURCE_ACTION",     event->trigger); /* old name (deprecated) */
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
    __update_environ("CLIENT_IP",         event->connection_ip);
    __update_environ("CLIENT_ROLE",       event->client_role);
    __update_environ("CLIENT_USERNAME",   event->client_username);
    __update_environ("CLIENT_USERAGENT",  event->client_useragent);

    snprintf(buf, sizeof(buf), "%lu", event->connection_id);
    __update_environ("CLIENT_ID",         buf);
    snprintf(buf, sizeof(buf), "%lli", (long long int)event->connection_time);
    __update_environ("CLIENT_CONNECTION_TIME", buf);
    snprintf(buf, sizeof(buf), "%i", event->client_admin_command);
    __update_environ("CLIENT_ADMIN_COMMAND", buf);

    mountinfo = config_find_mount(config, event->uri, MOUNT_TYPE_NORMAL);
    if (mountinfo) {
        __update_environ("MOUNT_NAME",        mountinfo->stream_name);
        __update_environ("MOUNT_DESCRIPTION", mountinfo->stream_description);
        __update_environ("MOUNT_URL",         mountinfo->stream_url);
        __update_environ("MOUNT_GENRE",       mountinfo->stream_genre);
    }

    avl_tree_rlock(global.source_tree);
    source = source_find_mount(event->uri);
    if (source) {
        __update_environ("SOURCE_MOUNTPOINT", source->mount);
        __update_environ("SOURCE_PUBLIC",     source->yp_public ? "true" : "false");
        __update_environ("SROUCE_HIDDEN",     source->hidden    ? "true" : "false");
    }
    avl_tree_unlock(global.source_tree);
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
}

static inline void __setup_file_descriptors(ice_config_t *config) {
    int i;

    /* close at least the first 1024 handles */
    for (i = 0; i < 1024; i++)
        close(i);

    /* open null device */
    i = open(config->null_device, O_RDWR);
    if (i != -1) {
        /* attach null device to stdin, stdout and stderr */
        if (i != 0)
            dup2(i, 0);
        if (i != 1)
            dup2(i, 1);
        if (i != 2)
            dup2(i, 2);

        /* close null device */
        if (i > 2)
            close(i);
    }
}

static inline void __setup_empty_script_environment(event_exec_t *self, event_t *event) {
    ice_config_t *config = config_get_config();

    __setup_file_descriptors(config);
    __setup_environ(config, self, event);
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218

    config_release_config();
}

static void _run_script (event_exec_t *self, event_t *event) {
    pid_t pid, external_pid;

    /* do a fork twice so that the command has init as parent */
    external_pid = fork();
    switch (external_pid)
    {
        case 0:
            switch (pid = fork ())
            {
                case -1:
                    ICECAST_LOG_ERROR("Unable to fork %s (%s)", self->executable, strerror (errno));
                    break;
                case 0:  /* child */
                    if (access(self->executable, R_OK|X_OK) != 0) {
                        ICECAST_LOG_ERROR("Unable to run command %s (%s)", self->executable, strerror(errno));
                        exit(1);
                    }
                    ICECAST_LOG_DEBUG("Starting command %s", self->executable);
                    __setup_empty_script_environment(self, event);
219
                    execv(self->executable, __setup_argv(self, event));
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
                    exit(1);
                default: /* parent */
                    break;
            }
            exit (0);
        case -1:
            ICECAST_LOG_ERROR("Unable to fork %s", strerror (errno));
            break;
        default: /* parent */
            waitpid (external_pid, NULL, 0);
            break;
    }
}
#endif

static int event_exec_emit(void *state, event_t *event) {
    event_exec_t *self = state;
#ifdef _WIN32
238
    /* BEFORE RELEASE 2.4.2 DOCUMENT: Document this not working on win*. */
239
240
241
242
243
244
245
246
247
    ICECAST_LOG_ERROR("<event type=\"exec\" ...> not supported on win*");
#else
    _run_script(self, event);
#endif
    return 0;
}

static void event_exec_free(void *state) {
    event_exec_t *self = state;
248
249
250
251
252
253
254
    size_t i;

    for (i = __argvtype2offset(self->argvtype); self->argv[i]; i++)
        free(self->argv[i]);

    free(self->argv);
    free(self->executable);
255
256
257
258
259
    free(self);
}

int event_get_exec(event_registration_t *er, config_options_t *options) {
    event_exec_t *self = calloc(1, sizeof(event_exec_t));
260
261
    config_options_t *cur;
    size_t extra_argc = 0;
262
263
264
265

    if (!self)
        return -1;

266
267
268
    self->argvtype = ARGVTYPE_DFAULT;

    if ((cur = options)) {
269
        do {
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
            if (cur->name) {
                /* BEFORE RELEASE 2.4.2 DOCUMENT: Document supported options:
                 * <option name="executable" value="..." />
                 * <option name="default_arguments" value="..." /> (for values see near top of documment)
                 */
                if (strcmp(cur->name, "executable") == 0) {
                    free(self->executable);
                    self->executable = NULL;
                    if (cur->value)
                        self->executable = strdup(cur->value);
                } else if (strcmp(cur->name, "default_arguments") == 0) {
                    self->argvtype = __str2argvtype(cur->value);
                } else {
                    ICECAST_LOG_ERROR("Unknown <option> tag with name %s.", cur->name);
                }
            } else if (cur->type) {
                if (strcmp(cur->type, "argument") == 0) {
                    extra_argc++;
                } else {
                    ICECAST_LOG_ERROR("Unknown <option> tag with type %s.", cur->type);
                }
291
            }
292
        } while ((cur = cur->next));
293
294
295
296
297
298
299
300
    }

    if (!self->executable) {
        ICECAST_LOG_ERROR("No executable given.");
        event_exec_free(self);
        return -1;
    }

301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
    self->argv = calloc(__argvtype2offset(self->argvtype) + extra_argc + 1, sizeof(char*));
    if (!self->argv) {
        ICECAST_LOG_ERROR("Can not allocate argv[]");
        event_exec_free(self);
        return -1;
    }

    extra_argc = __argvtype2offset(self->argvtype);

    if ((cur = options)) {
        do {
            if (cur->type) {
                /* BEFORE RELEASE 2.4.2 DOCUMENT: Document supported options:
                 * <option type="argument" value="..." />
                 */
                if (strcmp(cur->type, "argument") == 0) {
                    if (cur->value) {
                        self->argv[extra_argc] = strdup(cur->value);
                        if (!self->argv[extra_argc]) {
                            ICECAST_LOG_ERROR("Can not allocate argv[x]");
                            event_exec_free(self);
                            return -1;
                        }
                        extra_argc++;
                    }
                } else {
                    ICECAST_LOG_ERROR("Unknown <option> tag with type %s.", cur->type);
                }
            }
        } while ((cur = cur->next));
    }

333
334
335
336
337
    er->state = self;
    er->emit = event_exec_emit;
    er->free = event_exec_free;
    return 0;
}