source: rtems-libbsd/dhcpcd/script.c @ b2eb48c

5-freebsd-12
Last change on this file since b2eb48c was b2eb48c, checked in by Sebastian Huber <sebastian.huber@…>, on May 2, 2018 at 7:01:32 AM

dhcpcd: Add hooks

  • Property mode set to 100644
File size: 14.5 KB
Line 
1/*
2 * dhcpcd - DHCP client daemon
3 * Copyright (c) 2006-2014 Roy Marples <roy@marples.name>
4 * All rights reserved
5
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/stat.h>
29#include <sys/uio.h>
30#include <sys/wait.h>
31
32#include <netinet/in.h>
33#include <arpa/inet.h>
34
35#include <ctype.h>
36#include <errno.h>
37#include <signal.h>
38/* We can't include spawn.h here because it may not exist.
39 * config.h will pull it in, or our compat one. */
40#include <stdlib.h>
41#include <string.h>
42#include <syslog.h>
43#include <unistd.h>
44
45#include "config.h"
46#include "common.h"
47#include "dhcp.h"
48#include "dhcp6.h"
49#include "if-options.h"
50#include "if-pref.h"
51#include "ipv6nd.h"
52#include "net.h"
53#include "script.h"
54#ifdef __rtems__
55#include <rtems/dhcpcd.h>
56
57static SLIST_HEAD(, rtems_dhcpcd_hook) dhcpcd_hooks =
58  SLIST_HEAD_INITIALIZER(dhcpcd_hooks);
59
60void
61rtems_dhcpcd_add_hook(rtems_dhcpcd_hook *hook)
62{
63        rtems_recursive_mutex_lock(&dhcpcd_mutex);
64        SLIST_INSERT_HEAD(&dhcpcd_hooks, hook, node);
65        rtems_recursive_mutex_unlock(&dhcpcd_mutex);
66}
67
68void
69rtems_dhcpcd_remove_hook(rtems_dhcpcd_hook *hook)
70{
71        rtems_recursive_mutex_lock(&dhcpcd_mutex);
72        SLIST_REMOVE(&dhcpcd_hooks, hook, rtems_dhcpcd_hook, node);
73        rtems_recursive_mutex_unlock(&dhcpcd_mutex);
74}
75#endif /* __rtems__ */
76
77#ifndef __rtems__
78#define DEFAULT_PATH    "PATH=/usr/bin:/usr/sbin:/bin:/sbin"
79
80static const char * const if_params[] = {
81        "interface",
82        "reason",
83        "pid",
84        "ifmetric",
85        "ifwireless",
86        "ifflags",
87        "ssid",
88        "profile",
89        "interface_order",
90        NULL
91};
92
93void
94if_printoptions(void)
95{
96        const char * const *p;
97
98        for (p = if_params; *p; p++)
99                printf(" -  %s\n", *p);
100}
101
102static int
103exec_script(char *const *argv, char *const *env)
104{
105        pid_t pid;
106        posix_spawnattr_t attr;
107        short flags;
108        sigset_t defsigs;
109        int i;
110
111        /* posix_spawn is a safe way of executing another image
112         * and changing signals back to how they should be. */
113        if (posix_spawnattr_init(&attr) == -1)
114                return -1;
115        flags = POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF;
116        posix_spawnattr_setflags(&attr, flags);
117        sigemptyset(&defsigs);
118        for (i = 0; i < handle_sigs[i]; i++)
119                sigaddset(&defsigs, handle_sigs[i]);
120        posix_spawnattr_setsigdefault(&attr, &defsigs);
121        posix_spawnattr_setsigmask(&attr, &dhcpcd_sigset);
122        errno = 0;
123        i = posix_spawn(&pid, argv[0], NULL, &attr, argv, env);
124        if (i) {
125                errno = i;
126                return -1;
127        }
128        return pid;
129}
130#endif /* __rtems__ */
131
132#ifdef INET
133static char *
134make_var(const char *prefix, const char *var)
135{
136        size_t len;
137        char *v;
138
139        len = strlen(prefix) + strlen(var) + 2;
140        v = malloc(len);
141        if (v == NULL) {
142                syslog(LOG_ERR, "%s: %m", __func__);
143                return NULL;
144        }
145        snprintf(v, len, "%s_%s", prefix, var);
146        return v;
147}
148
149
150static int
151append_config(char ***env, ssize_t *len,
152    const char *prefix, const char *const *config)
153{
154        ssize_t i, j, e1;
155        char **ne, *eq, **nep, *p;
156
157        if (config == NULL)
158                return 0;
159
160        ne = *env;
161        for (i = 0; config[i] != NULL; i++) {
162                eq = strchr(config[i], '=');
163                e1 = eq - config[i] + 1;
164                for (j = 0; j < *len; j++) {
165                        if (strncmp(ne[j] + strlen(prefix) + 1,
166                                config[i], e1) == 0)
167                        {
168                                free(ne[j]);
169                                ne[j] = make_var(prefix, config[i]);
170                                if (ne[j] == NULL)
171                                        return -1;
172                                break;
173                        }
174                }
175                if (j == *len) {
176                        j++;
177                        p = make_var(prefix, config[i]);
178                        if (p == NULL)
179                                return -1;
180                        nep = realloc(ne, sizeof(char *) * (j + 1));
181                        if (nep == NULL) {
182                                syslog(LOG_ERR, "%s: %m", __func__);
183                                return -1;
184                        }
185                        ne = nep;
186                        ne[j - 1] = p;
187                        *len = j;
188                }
189        }
190        *env = ne;
191        return 0;
192}
193#endif
194
195#ifndef __rtems__
196static size_t
197arraytostr(const char *const *argv, char **s)
198{
199        const char *const *ap;
200        char *p;
201        size_t len, l;
202
203        len = 0;
204        ap = argv;
205        while (*ap)
206                len += strlen(*ap++) + 1;
207        *s = p = malloc(len);
208        if (p == NULL)
209                return -1;
210        ap = argv;
211        while (*ap) {
212                l = strlen(*ap) + 1;
213                memcpy(p, *ap, l);
214                p += l;
215                ap++;
216        }
217        return len;
218}
219#endif /* __rtems__ */
220
221static ssize_t
222make_env(const struct interface *ifp, const char *reason, char ***argv)
223{
224        char **env, **nenv, *p;
225        ssize_t e, elen, l;
226        const struct if_options *ifo = ifp->options;
227        const struct interface *ifp2;
228        int dhcp, dhcp6, ra;
229        const struct dhcp_state *state;
230#ifdef INET6
231        const struct dhcp6_state *d6_state;
232#endif
233
234        dhcp = dhcp6 = ra = 0;
235        state = D_STATE(ifp);
236#ifdef INET6
237        d6_state = D6_CSTATE(ifp);
238#endif
239        if (strcmp(reason, "TEST") == 0) {
240#ifdef INET6
241                if (d6_state && d6_state->new)
242                        dhcp6 = 1;
243                else if (ipv6nd_has_ra(ifp))
244                        ra = 1;
245                else
246#endif
247                        dhcp = 1;
248        } else if (reason[strlen(reason) - 1] == '6')
249                dhcp6 = 1;
250        else if (strcmp(reason, "ROUTERADVERT") == 0)
251                ra = 1;
252        else
253                dhcp = 1;
254
255        /* When dumping the lease, we only want to report interface and
256           reason - the other interface variables are meaningless */
257        if (options & DHCPCD_DUMPLEASE)
258                elen = 2;
259        else
260                elen = 10;
261
262#define EMALLOC(i, l) if ((env[(i)] = malloc(l)) == NULL) goto eexit;
263        /* Make our env */
264        env = calloc(1, sizeof(char *) * (elen + 1));
265        if (env == NULL)
266                goto eexit;
267        e = strlen("interface") + strlen(ifp->name) + 2;
268        EMALLOC(0, e);
269        snprintf(env[0], e, "interface=%s", ifp->name);
270        e = strlen("reason") + strlen(reason) + 2;
271        EMALLOC(1, e);
272        snprintf(env[1], e, "reason=%s", reason);
273        if (options & DHCPCD_DUMPLEASE)
274                goto dumplease;
275        e = 20;
276        EMALLOC(2, e);
277        snprintf(env[2], e, "pid=%d", getpid());
278        EMALLOC(3, e);
279        snprintf(env[3], e, "ifmetric=%d", ifp->metric);
280        EMALLOC(4, e);
281        snprintf(env[4], e, "ifwireless=%d", ifp->wireless);
282        EMALLOC(5, e);
283        snprintf(env[5], e, "ifflags=%u", ifp->flags);
284        EMALLOC(6, e);
285        snprintf(env[6], e, "ifmtu=%d", get_mtu(ifp->name));
286        l = e = strlen("interface_order=");
287        TAILQ_FOREACH(ifp2, ifaces, next) {
288                e += strlen(ifp2->name) + 1;
289        }
290        EMALLOC(7, e);
291        p = env[7];
292        strlcpy(p, "interface_order=", e);
293        e -= l;
294        p += l;
295        TAILQ_FOREACH(ifp2, ifaces, next) {
296                l = strlcpy(p, ifp2->name, e);
297                p += l;
298                e -= l;
299                *p++ = ' ';
300                e--;
301        }
302        *--p = '\0';
303        if (strcmp(reason, "TEST") == 0) {
304                env[8] = strdup("if_up=false");
305                env[9] = strdup("if_down=false");
306        } else if ((dhcp && state && state->new)
307#ifdef INET6
308            || (dhcp6 && d6_state && d6_state->new)
309            || (ra && ipv6nd_has_ra(ifp))
310#endif
311            )
312        {
313                env[8] = strdup("if_up=true");
314                env[9] = strdup("if_down=false");
315        } else {
316                env[8] = strdup("if_up=false");
317                env[9] = strdup("if_down=true");
318        }
319        if (env[8] == NULL || env[9] == NULL)
320                goto eexit;
321        if (*ifp->profile) {
322                e = strlen("profile=") + strlen(ifp->profile) + 2;
323                EMALLOC(elen, e);
324                snprintf(env[elen++], e, "profile=%s", ifp->profile);
325        }
326        if (ifp->wireless) {
327                e = strlen("new_ssid=") + strlen(ifp->ssid) + 2;
328                if (strcmp(reason, "CARRIER") == 0) {
329                        nenv = realloc(env, sizeof(char *) * (elen + 2));
330                        if (nenv == NULL)
331                                goto eexit;
332                        env = nenv;
333                        EMALLOC(elen, e);
334                        snprintf(env[elen++], e, "new_ssid=%s", ifp->ssid);
335                }
336                else if (strcmp(reason, "NOCARRIER") == 0) {
337                        nenv = realloc(env, sizeof(char *) * (elen + 2));
338                        if (nenv == NULL)
339                                goto eexit;
340                        env = nenv;
341                        EMALLOC(elen, e);
342                        snprintf(env[elen++], e, "old_ssid=%s", ifp->ssid);
343                }
344        }
345#ifdef INET
346        if (dhcp && state && state->old) {
347                e = dhcp_env(NULL, NULL, state->old, ifp);
348                if (e > 0) {
349                        nenv = realloc(env, sizeof(char *) * (elen + e + 1));
350                        if (nenv == NULL)
351                                goto eexit;
352                        env = nenv;
353                        l = dhcp_env(env + elen, "old", state->old, ifp);
354                        if (l == -1)
355                                goto eexit;
356                        elen += l;
357                }
358                if (append_config(&env, &elen, "old",
359                    (const char *const *)ifo->config) == -1)
360                        goto eexit;
361        }
362#endif
363#ifdef INET6
364        if (dhcp6 && d6_state && d6_state->old) {
365                e = dhcp6_env(NULL, NULL, ifp,
366                    d6_state->old, d6_state->old_len);
367                if (e > 0) {
368                        nenv = realloc(env, sizeof(char *) * (elen + e + 1));
369                        if (nenv == NULL)
370                                goto eexit;
371                        env = nenv;
372                        l = dhcp6_env(env + elen, "old", ifp,
373                            d6_state->old, d6_state->old_len);
374                        if (l == -1)
375                                goto eexit;
376                        elen += l;
377                }
378        }
379#endif
380
381dumplease:
382#ifdef INET
383        if (dhcp && state && state->new) {
384                e = dhcp_env(NULL, NULL, state->new, ifp);
385                if (e > 0) {
386                        nenv = realloc(env, sizeof(char *) * (elen + e + 1));
387                        if (nenv == NULL)
388                                goto eexit;
389                        env = nenv;
390                        l = dhcp_env(env + elen, "new",
391                            state->new, ifp);
392                        if (l == -1)
393                                goto eexit;
394                        elen += l;
395                }
396                if (append_config(&env, &elen, "new",
397                    (const char *const *)ifo->config) == -1)
398                        goto eexit;
399        }
400#endif
401#ifdef INET6
402        if (dhcp6 && d6_state && d6_state->new) {
403                e = dhcp6_env(NULL, NULL, ifp,
404                    d6_state->new, d6_state->new_len);
405                if (e > 0) {
406                        nenv = realloc(env, sizeof(char *) * (elen + e + 1));
407                        if (nenv == NULL)
408                                goto eexit;
409                        env = nenv;
410                        l = dhcp6_env(env + elen, "new", ifp,
411                            d6_state->new, d6_state->new_len);
412                        if (l == -1)
413                                goto eexit;
414                        elen += l;
415                }
416        }
417        if (ra) {
418                e = ipv6nd_env(NULL, NULL, ifp);
419                if (e > 0) {
420                        nenv = realloc(env, sizeof(char *) * (elen + e + 1));
421                        if (nenv == NULL)
422                                goto eexit;
423                        env = nenv;
424                        l = ipv6nd_env(env + elen, NULL, ifp);
425                        if (l == -1)
426                                goto eexit;
427                        elen += l;
428                }
429        }
430#endif
431
432        /* Add our base environment */
433        if (ifo->environ) {
434                e = 0;
435                while (ifo->environ[e++])
436                        ;
437                nenv = realloc(env, sizeof(char *) * (elen + e + 1));
438                if (nenv == NULL)
439                        goto eexit;
440                env = nenv;
441                e = 0;
442                while (ifo->environ[e]) {
443                        env[elen + e] = strdup(ifo->environ[e]);
444                        if (env[elen + e] == NULL)
445                                goto eexit;
446                        e++;
447                }
448                elen += e;
449        }
450        env[elen] = NULL;
451
452        *argv = env;
453        return elen;
454
455eexit:
456        syslog(LOG_ERR, "%s: %m", __func__);
457        nenv = env;
458        while (*nenv)
459                free(*nenv++);
460        free(env);
461        return -1;
462}
463
464#ifndef __rtems__
465static int
466send_interface1(int fd, const struct interface *iface, const char *reason)
467{
468        char **env, **ep, *s;
469        ssize_t elen;
470        struct iovec iov[2];
471        int retval;
472
473        if (make_env(iface, reason, &env) == -1)
474                return -1;
475        elen = arraytostr((const char *const *)env, &s);
476        if (elen == -1)
477                return -1;
478        iov[0].iov_base = &elen;
479        iov[0].iov_len = sizeof(ssize_t);
480        iov[1].iov_base = s;
481        iov[1].iov_len = elen;
482        retval = writev(fd, iov, 2);
483        ep = env;
484        while (*ep)
485                free(*ep++);
486        free(env);
487        free(s);
488        return retval;
489}
490
491int
492send_interface(int fd, const struct interface *iface)
493{
494        const char *reason;
495        int retval = 0;
496        int onestate = 0;
497#ifdef INET
498        const struct dhcp_state *state = D_CSTATE(iface);
499
500        if (state) {
501                onestate = 1;
502                if (send_interface1(fd, iface, state->reason) == -1)
503                        retval = -1;
504        }
505#endif
506
507#ifdef INET6
508        if (ipv6nd_has_ra(iface)) {
509                onestate = 1;
510                if (send_interface1(fd, iface, "ROUTERADVERT") == -1)
511                        retval = -1;
512        }
513        if (D6_STATE_RUNNING(iface)) {
514                onestate = 1;
515                if (send_interface1(fd, iface, "INFORM6") == -1)
516                        retval = -1;
517        }
518#endif
519
520        if (!onestate) {
521                switch (iface->carrier) {
522                case LINK_UP:
523                        reason = "CARRIER";
524                        break;
525                case LINK_DOWN:
526                        reason = "NOCARRIER";
527                        break;
528                default:
529                        reason = "UNKNOWN";
530                        break;
531                }
532                if (send_interface1(fd, iface, reason) == -1)
533                        retval = -1;
534        }
535        return retval;
536}
537#endif /* __rtems__ */
538
539int
540script_runreason(const struct interface *ifp, const char *reason)
541{
542#ifndef __rtems__
543        char *const argv[2] = { UNCONST(ifp->options->script), NULL };
544#endif /* __rtems__ */
545        char **env = NULL, **ep;
546#ifndef __rtems__
547        char *path, *bigenv;
548        ssize_t e, elen = 0;
549        pid_t pid;
550#else /* __rtems__ */
551        ssize_t elen;
552        rtems_dhcpcd_hook *hook;
553        rtems_dhcpcd_hook *hook2;
554#endif /* __rtems__ */
555        int status = 0;
556#ifndef __rtems__
557        const struct fd_list *fd;
558        struct iovec iov[2];
559#endif /* __rtems__ */
560
561        if (ifp->options->script == NULL ||
562            ifp->options->script[0] == '\0' ||
563            strcmp(ifp->options->script, "/dev/null") == 0)
564                return 0;
565
566#ifndef __rtems__
567        syslog(LOG_DEBUG, "%s: executing `%s' %s",
568            ifp->name, argv[0], reason);
569#endif /* __rtems__ */
570
571        /* Make our env */
572        elen = make_env(ifp, reason, &env);
573#ifndef __rtems__
574        ep = realloc(env, sizeof(char *) * (elen + 2));
575        if (ep == NULL) {
576                elen = -1;
577                goto out;
578        }
579        env = ep;
580        /* Add path to it */
581        path = getenv("PATH");
582        if (path) {
583                e = strlen("PATH") + strlen(path) + 2;
584                env[elen] = malloc(e);
585                if (env[elen] == NULL) {
586                        elen = -1;
587                        goto out;
588                }
589                snprintf(env[elen], e, "PATH=%s", path);
590        } else {
591                env[elen] = strdup(DEFAULT_PATH);
592                if (env[elen] == NULL) {
593                        elen = -1;
594                        goto out;
595                }
596        }
597        env[++elen] = NULL;
598
599        pid = exec_script(argv, env);
600        if (pid == -1)
601                syslog(LOG_ERR, "%s: %s: %m", __func__, argv[0]);
602        else if (pid != 0) {
603                /* Wait for the script to finish */
604                while (waitpid(pid, &status, 0) == -1) {
605                        if (errno != EINTR) {
606                                syslog(LOG_ERR, "waitpid: %m");
607                                status = 0;
608                                break;
609                        }
610                }
611                if (WIFEXITED(status)) {
612                        if (WEXITSTATUS(status))
613                                syslog(LOG_ERR,
614                                    "%s: %s: WEXITSTATUS %d",
615                                    __func__, argv[0], WEXITSTATUS(status));
616                } else if (WIFSIGNALED(status))
617                        syslog(LOG_ERR, "%s: %s: %s",
618                            __func__, argv[0], strsignal(WTERMSIG(status)));
619        }
620
621        /* Send to our listeners */
622        bigenv = NULL;
623        for (fd = control_fds; fd != NULL; fd = fd->next) {
624                if (fd->listener) {
625                        if (bigenv == NULL) {
626                                elen = arraytostr((const char *const *)env,
627                                    &bigenv);
628                                iov[0].iov_base = &elen;
629                                iov[0].iov_len = sizeof(ssize_t);
630                                iov[1].iov_base = bigenv;
631                                iov[1].iov_len = elen;
632                        }
633                        if (writev(fd->fd, iov, 2) == -1)
634                                syslog(LOG_ERR, "%s: writev: %m", __func__);
635                }
636        }
637        free(bigenv);
638
639out:
640#else /* __rtems__ */
641        rtems_recursive_mutex_lock(&dhcpcd_mutex);
642
643        SLIST_FOREACH_SAFE(hook, &dhcpcd_hooks, node, hook2) {
644                syslog(LOG_DEBUG, "%s: executing `%s' %s", ifp->name,
645                    hook->name, reason);
646                (*hook->handler)(hook, env);
647        }
648
649        rtems_recursive_mutex_unlock(&dhcpcd_mutex);
650#endif /* __rtems__ */
651        /* Cleanup */
652        ep = env;
653        while (*ep)
654                free(*ep++);
655        free(env);
656        if (elen == -1)
657                return -1;
658        return WEXITSTATUS(status);
659}
Note: See TracBrowser for help on using the repository browser.