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

4.115-freebsd-12freebsd-9.3
Last change on this file since f2ed769 was f2ed769, checked in by Sebastian Huber <sebastian.huber@…>, on Jan 30, 2014 at 12:29:46 PM

DHCPCD(8): Import

Import DHCPCD(8) from:

http://roy.marples.name/projects/dhcpcd/

The upstream sources can be obtained via:

fossil clone http://roy.marples.name/projects/dhcpcd

The imported version is 2014-01-29 19:46:44 [6b209507bb].

  • Property mode set to 100644
File size: 13.3 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
55#define DEFAULT_PATH    "PATH=/usr/bin:/usr/sbin:/bin:/sbin"
56
57static const char * const if_params[] = {
58        "interface",
59        "reason",
60        "pid",
61        "ifmetric",
62        "ifwireless",
63        "ifflags",
64        "ssid",
65        "profile",
66        "interface_order",
67        NULL
68};
69
70void
71if_printoptions(void)
72{
73        const char * const *p;
74
75        for (p = if_params; *p; p++)
76                printf(" -  %s\n", *p);
77}
78
79static int
80exec_script(char *const *argv, char *const *env)
81{
82        pid_t pid;
83        posix_spawnattr_t attr;
84        short flags;
85        sigset_t defsigs;
86        int i;
87
88        /* posix_spawn is a safe way of executing another image
89         * and changing signals back to how they should be. */
90        if (posix_spawnattr_init(&attr) == -1)
91                return -1;
92        flags = POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF;
93        posix_spawnattr_setflags(&attr, flags);
94        sigemptyset(&defsigs);
95        for (i = 0; i < handle_sigs[i]; i++)
96                sigaddset(&defsigs, handle_sigs[i]);
97        posix_spawnattr_setsigdefault(&attr, &defsigs);
98        posix_spawnattr_setsigmask(&attr, &dhcpcd_sigset);
99        errno = 0;
100        i = posix_spawn(&pid, argv[0], NULL, &attr, argv, env);
101        if (i) {
102                errno = i;
103                return -1;
104        }
105        return pid;
106}
107
108#ifdef INET
109static char *
110make_var(const char *prefix, const char *var)
111{
112        size_t len;
113        char *v;
114
115        len = strlen(prefix) + strlen(var) + 2;
116        v = malloc(len);
117        if (v == NULL) {
118                syslog(LOG_ERR, "%s: %m", __func__);
119                return NULL;
120        }
121        snprintf(v, len, "%s_%s", prefix, var);
122        return v;
123}
124
125
126static int
127append_config(char ***env, ssize_t *len,
128    const char *prefix, const char *const *config)
129{
130        ssize_t i, j, e1;
131        char **ne, *eq, **nep, *p;
132
133        if (config == NULL)
134                return 0;
135
136        ne = *env;
137        for (i = 0; config[i] != NULL; i++) {
138                eq = strchr(config[i], '=');
139                e1 = eq - config[i] + 1;
140                for (j = 0; j < *len; j++) {
141                        if (strncmp(ne[j] + strlen(prefix) + 1,
142                                config[i], e1) == 0)
143                        {
144                                free(ne[j]);
145                                ne[j] = make_var(prefix, config[i]);
146                                if (ne[j] == NULL)
147                                        return -1;
148                                break;
149                        }
150                }
151                if (j == *len) {
152                        j++;
153                        p = make_var(prefix, config[i]);
154                        if (p == NULL)
155                                return -1;
156                        nep = realloc(ne, sizeof(char *) * (j + 1));
157                        if (nep == NULL) {
158                                syslog(LOG_ERR, "%s: %m", __func__);
159                                return -1;
160                        }
161                        ne = nep;
162                        ne[j - 1] = p;
163                        *len = j;
164                }
165        }
166        *env = ne;
167        return 0;
168}
169#endif
170
171static size_t
172arraytostr(const char *const *argv, char **s)
173{
174        const char *const *ap;
175        char *p;
176        size_t len, l;
177
178        len = 0;
179        ap = argv;
180        while (*ap)
181                len += strlen(*ap++) + 1;
182        *s = p = malloc(len);
183        if (p == NULL)
184                return -1;
185        ap = argv;
186        while (*ap) {
187                l = strlen(*ap) + 1;
188                memcpy(p, *ap, l);
189                p += l;
190                ap++;
191        }
192        return len;
193}
194
195static ssize_t
196make_env(const struct interface *ifp, const char *reason, char ***argv)
197{
198        char **env, **nenv, *p;
199        ssize_t e, elen, l;
200        const struct if_options *ifo = ifp->options;
201        const struct interface *ifp2;
202        int dhcp, dhcp6, ra;
203        const struct dhcp_state *state;
204#ifdef INET6
205        const struct dhcp6_state *d6_state;
206#endif
207
208        dhcp = dhcp6 = ra = 0;
209        state = D_STATE(ifp);
210#ifdef INET6
211        d6_state = D6_CSTATE(ifp);
212#endif
213        if (strcmp(reason, "TEST") == 0) {
214#ifdef INET6
215                if (d6_state && d6_state->new)
216                        dhcp6 = 1;
217                else if (ipv6nd_has_ra(ifp))
218                        ra = 1;
219                else
220#endif
221                        dhcp = 1;
222        } else if (reason[strlen(reason) - 1] == '6')
223                dhcp6 = 1;
224        else if (strcmp(reason, "ROUTERADVERT") == 0)
225                ra = 1;
226        else
227                dhcp = 1;
228
229        /* When dumping the lease, we only want to report interface and
230           reason - the other interface variables are meaningless */
231        if (options & DHCPCD_DUMPLEASE)
232                elen = 2;
233        else
234                elen = 10;
235
236#define EMALLOC(i, l) if ((env[(i)] = malloc(l)) == NULL) goto eexit;
237        /* Make our env */
238        env = calloc(1, sizeof(char *) * (elen + 1));
239        if (env == NULL)
240                goto eexit;
241        e = strlen("interface") + strlen(ifp->name) + 2;
242        EMALLOC(0, e);
243        snprintf(env[0], e, "interface=%s", ifp->name);
244        e = strlen("reason") + strlen(reason) + 2;
245        EMALLOC(1, e);
246        snprintf(env[1], e, "reason=%s", reason);
247        if (options & DHCPCD_DUMPLEASE)
248                goto dumplease;
249        e = 20;
250        EMALLOC(2, e);
251        snprintf(env[2], e, "pid=%d", getpid());
252        EMALLOC(3, e);
253        snprintf(env[3], e, "ifmetric=%d", ifp->metric);
254        EMALLOC(4, e);
255        snprintf(env[4], e, "ifwireless=%d", ifp->wireless);
256        EMALLOC(5, e);
257        snprintf(env[5], e, "ifflags=%u", ifp->flags);
258        EMALLOC(6, e);
259        snprintf(env[6], e, "ifmtu=%d", get_mtu(ifp->name));
260        l = e = strlen("interface_order=");
261        TAILQ_FOREACH(ifp2, ifaces, next) {
262                e += strlen(ifp2->name) + 1;
263        }
264        EMALLOC(7, e);
265        p = env[7];
266        strlcpy(p, "interface_order=", e);
267        e -= l;
268        p += l;
269        TAILQ_FOREACH(ifp2, ifaces, next) {
270                l = strlcpy(p, ifp2->name, e);
271                p += l;
272                e -= l;
273                *p++ = ' ';
274                e--;
275        }
276        *--p = '\0';
277        if (strcmp(reason, "TEST") == 0) {
278                env[8] = strdup("if_up=false");
279                env[9] = strdup("if_down=false");
280        } else if ((dhcp && state && state->new)
281#ifdef INET6
282            || (dhcp6 && d6_state && d6_state->new)
283            || (ra && ipv6nd_has_ra(ifp))
284#endif
285            )
286        {
287                env[8] = strdup("if_up=true");
288                env[9] = strdup("if_down=false");
289        } else {
290                env[8] = strdup("if_up=false");
291                env[9] = strdup("if_down=true");
292        }
293        if (env[8] == NULL || env[9] == NULL)
294                goto eexit;
295        if (*ifp->profile) {
296                e = strlen("profile=") + strlen(ifp->profile) + 2;
297                EMALLOC(elen, e);
298                snprintf(env[elen++], e, "profile=%s", ifp->profile);
299        }
300        if (ifp->wireless) {
301                e = strlen("new_ssid=") + strlen(ifp->ssid) + 2;
302                if (strcmp(reason, "CARRIER") == 0) {
303                        nenv = realloc(env, sizeof(char *) * (elen + 2));
304                        if (nenv == NULL)
305                                goto eexit;
306                        env = nenv;
307                        EMALLOC(elen, e);
308                        snprintf(env[elen++], e, "new_ssid=%s", ifp->ssid);
309                }
310                else if (strcmp(reason, "NOCARRIER") == 0) {
311                        nenv = realloc(env, sizeof(char *) * (elen + 2));
312                        if (nenv == NULL)
313                                goto eexit;
314                        env = nenv;
315                        EMALLOC(elen, e);
316                        snprintf(env[elen++], e, "old_ssid=%s", ifp->ssid);
317                }
318        }
319#ifdef INET
320        if (dhcp && state && state->old) {
321                e = dhcp_env(NULL, NULL, state->old, ifp);
322                if (e > 0) {
323                        nenv = realloc(env, sizeof(char *) * (elen + e + 1));
324                        if (nenv == NULL)
325                                goto eexit;
326                        env = nenv;
327                        l = dhcp_env(env + elen, "old", state->old, ifp);
328                        if (l == -1)
329                                goto eexit;
330                        elen += l;
331                }
332                if (append_config(&env, &elen, "old",
333                    (const char *const *)ifo->config) == -1)
334                        goto eexit;
335        }
336#endif
337#ifdef INET6
338        if (dhcp6 && d6_state && d6_state->old) {
339                e = dhcp6_env(NULL, NULL, ifp,
340                    d6_state->old, d6_state->old_len);
341                if (e > 0) {
342                        nenv = realloc(env, sizeof(char *) * (elen + e + 1));
343                        if (nenv == NULL)
344                                goto eexit;
345                        env = nenv;
346                        l = dhcp6_env(env + elen, "old", ifp,
347                            d6_state->old, d6_state->old_len);
348                        if (l == -1)
349                                goto eexit;
350                        elen += l;
351                }
352        }
353#endif
354
355dumplease:
356#ifdef INET
357        if (dhcp && state && state->new) {
358                e = dhcp_env(NULL, NULL, state->new, ifp);
359                if (e > 0) {
360                        nenv = realloc(env, sizeof(char *) * (elen + e + 1));
361                        if (nenv == NULL)
362                                goto eexit;
363                        env = nenv;
364                        l = dhcp_env(env + elen, "new",
365                            state->new, ifp);
366                        if (l == -1)
367                                goto eexit;
368                        elen += l;
369                }
370                if (append_config(&env, &elen, "new",
371                    (const char *const *)ifo->config) == -1)
372                        goto eexit;
373        }
374#endif
375#ifdef INET6
376        if (dhcp6 && d6_state && d6_state->new) {
377                e = dhcp6_env(NULL, NULL, ifp,
378                    d6_state->new, d6_state->new_len);
379                if (e > 0) {
380                        nenv = realloc(env, sizeof(char *) * (elen + e + 1));
381                        if (nenv == NULL)
382                                goto eexit;
383                        env = nenv;
384                        l = dhcp6_env(env + elen, "new", ifp,
385                            d6_state->new, d6_state->new_len);
386                        if (l == -1)
387                                goto eexit;
388                        elen += l;
389                }
390        }
391        if (ra) {
392                e = ipv6nd_env(NULL, NULL, ifp);
393                if (e > 0) {
394                        nenv = realloc(env, sizeof(char *) * (elen + e + 1));
395                        if (nenv == NULL)
396                                goto eexit;
397                        env = nenv;
398                        l = ipv6nd_env(env + elen, NULL, ifp);
399                        if (l == -1)
400                                goto eexit;
401                        elen += l;
402                }
403        }
404#endif
405
406        /* Add our base environment */
407        if (ifo->environ) {
408                e = 0;
409                while (ifo->environ[e++])
410                        ;
411                nenv = realloc(env, sizeof(char *) * (elen + e + 1));
412                if (nenv == NULL)
413                        goto eexit;
414                env = nenv;
415                e = 0;
416                while (ifo->environ[e]) {
417                        env[elen + e] = strdup(ifo->environ[e]);
418                        if (env[elen + e] == NULL)
419                                goto eexit;
420                        e++;
421                }
422                elen += e;
423        }
424        env[elen] = NULL;
425
426        *argv = env;
427        return elen;
428
429eexit:
430        syslog(LOG_ERR, "%s: %m", __func__);
431        nenv = env;
432        while (*nenv)
433                free(*nenv++);
434        free(env);
435        return -1;
436}
437
438static int
439send_interface1(int fd, const struct interface *iface, const char *reason)
440{
441        char **env, **ep, *s;
442        ssize_t elen;
443        struct iovec iov[2];
444        int retval;
445
446        if (make_env(iface, reason, &env) == -1)
447                return -1;
448        elen = arraytostr((const char *const *)env, &s);
449        if (elen == -1)
450                return -1;
451        iov[0].iov_base = &elen;
452        iov[0].iov_len = sizeof(ssize_t);
453        iov[1].iov_base = s;
454        iov[1].iov_len = elen;
455        retval = writev(fd, iov, 2);
456        ep = env;
457        while (*ep)
458                free(*ep++);
459        free(env);
460        free(s);
461        return retval;
462}
463
464int
465send_interface(int fd, const struct interface *iface)
466{
467        const char *reason;
468        int retval = 0;
469        int onestate = 0;
470#ifdef INET
471        const struct dhcp_state *state = D_CSTATE(iface);
472
473        if (state) {
474                onestate = 1;
475                if (send_interface1(fd, iface, state->reason) == -1)
476                        retval = -1;
477        }
478#endif
479
480#ifdef INET6
481        if (ipv6nd_has_ra(iface)) {
482                onestate = 1;
483                if (send_interface1(fd, iface, "ROUTERADVERT") == -1)
484                        retval = -1;
485        }
486        if (D6_STATE_RUNNING(iface)) {
487                onestate = 1;
488                if (send_interface1(fd, iface, "INFORM6") == -1)
489                        retval = -1;
490        }
491#endif
492
493        if (!onestate) {
494                switch (iface->carrier) {
495                case LINK_UP:
496                        reason = "CARRIER";
497                        break;
498                case LINK_DOWN:
499                        reason = "NOCARRIER";
500                        break;
501                default:
502                        reason = "UNKNOWN";
503                        break;
504                }
505                if (send_interface1(fd, iface, reason) == -1)
506                        retval = -1;
507        }
508        return retval;
509}
510
511int
512script_runreason(const struct interface *ifp, const char *reason)
513{
514        char *const argv[2] = { UNCONST(ifp->options->script), NULL };
515        char **env = NULL, **ep;
516        char *path, *bigenv;
517        ssize_t e, elen = 0;
518        pid_t pid;
519        int status = 0;
520        const struct fd_list *fd;
521        struct iovec iov[2];
522
523        if (ifp->options->script == NULL ||
524            ifp->options->script[0] == '\0' ||
525            strcmp(ifp->options->script, "/dev/null") == 0)
526                return 0;
527
528        syslog(LOG_DEBUG, "%s: executing `%s' %s",
529            ifp->name, argv[0], reason);
530
531        /* Make our env */
532        elen = make_env(ifp, reason, &env);
533        ep = realloc(env, sizeof(char *) * (elen + 2));
534        if (ep == NULL) {
535                elen = -1;
536                goto out;
537        }
538        env = ep;
539        /* Add path to it */
540        path = getenv("PATH");
541        if (path) {
542                e = strlen("PATH") + strlen(path) + 2;
543                env[elen] = malloc(e);
544                if (env[elen] == NULL) {
545                        elen = -1;
546                        goto out;
547                }
548                snprintf(env[elen], e, "PATH=%s", path);
549        } else {
550                env[elen] = strdup(DEFAULT_PATH);
551                if (env[elen] == NULL) {
552                        elen = -1;
553                        goto out;
554                }
555        }
556        env[++elen] = NULL;
557
558        pid = exec_script(argv, env);
559        if (pid == -1)
560                syslog(LOG_ERR, "%s: %s: %m", __func__, argv[0]);
561        else if (pid != 0) {
562                /* Wait for the script to finish */
563                while (waitpid(pid, &status, 0) == -1) {
564                        if (errno != EINTR) {
565                                syslog(LOG_ERR, "waitpid: %m");
566                                status = 0;
567                                break;
568                        }
569                }
570                if (WIFEXITED(status)) {
571                        if (WEXITSTATUS(status))
572                                syslog(LOG_ERR,
573                                    "%s: %s: WEXITSTATUS %d",
574                                    __func__, argv[0], WEXITSTATUS(status));
575                } else if (WIFSIGNALED(status))
576                        syslog(LOG_ERR, "%s: %s: %s",
577                            __func__, argv[0], strsignal(WTERMSIG(status)));
578        }
579
580        /* Send to our listeners */
581        bigenv = NULL;
582        for (fd = control_fds; fd != NULL; fd = fd->next) {
583                if (fd->listener) {
584                        if (bigenv == NULL) {
585                                elen = arraytostr((const char *const *)env,
586                                    &bigenv);
587                                iov[0].iov_base = &elen;
588                                iov[0].iov_len = sizeof(ssize_t);
589                                iov[1].iov_base = bigenv;
590                                iov[1].iov_len = elen;
591                        }
592                        if (writev(fd->fd, iov, 2) == -1)
593                                syslog(LOG_ERR, "%s: writev: %m", __func__);
594                }
595        }
596        free(bigenv);
597
598out:
599        /* Cleanup */
600        ep = env;
601        while (*ep)
602                free(*ep++);
603        free(env);
604        if (elen == -1)
605                return -1;
606        return WEXITSTATUS(status);
607}
Note: See TracBrowser for help on using the repository browser.