2 __RCSID("$NetBSD: script.c,v 1.23 2015/09/04 12:25:01 roy Exp $");
5 * dhcpcd - DHCP client daemon
6 * Copyright (c) 2006-2015 Roy Marples <roy@marples.name>
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
41 /* We can't include spawn.h here because it may not exist.
42 * config.h will pull it in, or our compat one. */
52 #include "if-options.h"
60 #include "compat/posix_spawn.h"
63 /* Allow the OS to define another script env var name */
65 #define RC_SVCNAME "RC_SVCNAME"
68 #define DEFAULT_PATH "PATH=/usr/bin:/usr/sbin:/bin:/sbin"
70 static const char * const if_params
[] = {
87 const char * const *p
;
89 for (p
= if_params
; *p
; p
++)
90 printf(" - %s\n", *p
);
94 exec_script(const struct dhcpcd_ctx
*ctx
, char *const *argv
, char *const *env
)
97 posix_spawnattr_t attr
;
107 /* posix_spawn is a safe way of executing another image
108 * and changing signals back to how they should be. */
109 if (posix_spawnattr_init(&attr
) == -1)
112 flags
= POSIX_SPAWN_SETSIGMASK
| POSIX_SPAWN_SETSIGDEF
;
113 posix_spawnattr_setflags(&attr
, flags
);
114 sigemptyset(&defsigs
);
115 for (i
= 0; i
< dhcpcd_signals_len
; i
++)
116 sigaddset(&defsigs
, dhcpcd_signals
[i
]);
117 posix_spawnattr_setsigdefault(&attr
, &defsigs
);
118 posix_spawnattr_setsigmask(&attr
, &ctx
->sigset
);
121 r
= posix_spawn(&pid
, argv
[0], NULL
, &attr
, argv
, env
);
131 make_var(struct dhcpcd_ctx
*ctx
, const char *prefix
, const char *var
)
136 len
= strlen(prefix
) + strlen(var
) + 2;
137 if ((v
= malloc(len
)) == NULL
) {
138 logger(ctx
, LOG_ERR
, "%s: %m", __func__
);
141 snprintf(v
, len
, "%s_%s", prefix
, var
);
147 append_config(struct dhcpcd_ctx
*ctx
, char ***env
, size_t *len
,
148 const char *prefix
, const char *const *config
)
151 char **ne
, *eq
, **nep
, *p
;
159 for (i
= 0; config
[i
] != NULL
; i
++) {
160 eq
= strchr(config
[i
], '=');
161 e1
= (size_t)(eq
- config
[i
] + 1);
162 for (j
= 0; j
< *len
; j
++) {
163 if (strncmp(ne
[j
], prefix
, strlen(prefix
)) == 0 &&
164 ne
[j
][strlen(prefix
)] == '_' &&
165 strncmp(ne
[j
] + strlen(prefix
) + 1,
168 p
= make_var(ctx
, prefix
, config
[i
]);
180 p
= make_var(ctx
, prefix
, config
[i
]);
185 nep
= realloc(ne
, sizeof(char *) * (j
+ 1));
187 logger(ctx
, LOG_ERR
, "%s: %m", __func__
);
203 arraytostr(const char *const *argv
, char **s
)
205 const char *const *ap
;
214 len
+= strlen(*ap
++) + 1;
215 *s
= p
= malloc(len
);
229 make_env(const struct interface
*ifp
, const char *reason
, char ***argv
)
231 char **env
, **nenv
, *p
;
233 #if defined(INET) || defined(INET6)
236 const struct if_options
*ifo
= ifp
->options
;
237 const struct interface
*ifp2
;
241 const struct dhcp_state
*state
;
242 const struct ipv4ll_state
*istate
;
245 const struct dhcp6_state
*d6_state
;
251 state
= D_STATE(ifp
);
252 istate
= IPV4LL_CSTATE(ifp
);
256 d6_state
= D6_CSTATE(ifp
);
258 if (strcmp(reason
, "TEST") == 0) {
261 else if (d6_state
&& d6_state
->new)
263 else if (ipv6nd_hasra(ifp
))
267 else if (state
->added
)
274 else if (reason
[strlen(reason
) - 1] == '6')
276 else if (strcmp(reason
, "ROUTERADVERT") == 0)
279 else if (strcmp(reason
, "PREINIT") == 0 ||
280 strcmp(reason
, "CARRIER") == 0 ||
281 strcmp(reason
, "NOCARRIER") == 0 ||
282 strcmp(reason
, "UNKNOWN") == 0 ||
283 strcmp(reason
, "DEPARTED") == 0 ||
284 strcmp(reason
, "STOPPED") == 0)
286 /* This space left intentionally blank */
289 else if (strcmp(reason
, "IPV4LL") == 0)
295 /* When dumping the lease, we only want to report interface and
296 reason - the other interface variables are meaningless */
297 if (ifp
->ctx
->options
& DHCPCD_DUMPLEASE
)
302 #define EMALLOC(i, l) if ((env[(i)] = malloc((l))) == NULL) goto eexit;
303 /* Make our env + space for profile, wireless and debug */
304 env
= calloc(1, sizeof(char *) * (elen
+ 4 + 1));
307 e
= strlen("interface") + strlen(ifp
->name
) + 2;
309 snprintf(env
[0], e
, "interface=%s", ifp
->name
);
310 e
= strlen("reason") + strlen(reason
) + 2;
312 snprintf(env
[1], e
, "reason=%s", reason
);
313 if (ifp
->ctx
->options
& DHCPCD_DUMPLEASE
)
317 snprintf(env
[2], e
, "pid=%d", getpid());
319 snprintf(env
[3], e
, "ifcarrier=%s",
320 ifp
->carrier
== LINK_UNKNOWN
? "unknown" :
321 ifp
->carrier
== LINK_UP
? "up" : "down");
323 snprintf(env
[4], e
, "ifmetric=%d", ifp
->metric
);
325 snprintf(env
[5], e
, "ifwireless=%d", ifp
->wireless
);
327 snprintf(env
[6], e
, "ifflags=%u", ifp
->flags
);
329 snprintf(env
[7], e
, "ifmtu=%d", if_getmtu(ifp
));
330 l
= e
= strlen("interface_order=");
331 TAILQ_FOREACH(ifp2
, ifp
->ctx
->ifaces
, next
) {
332 e
+= strlen(ifp2
->name
) + 1;
336 strlcpy(p
, "interface_order=", e
);
339 TAILQ_FOREACH(ifp2
, ifp
->ctx
->ifaces
, next
) {
340 l
= strlcpy(p
, ifp2
->name
, e
);
347 if (strcmp(reason
, "STOPPED") == 0) {
348 env
[9] = strdup("if_up=false");
349 if (ifo
->options
& DHCPCD_RELEASE
)
350 env
[10] = strdup("if_down=true");
352 env
[10] = strdup("if_down=false");
353 } else if (strcmp(reason
, "TEST") == 0 ||
354 strcmp(reason
, "PREINIT") == 0 ||
355 strcmp(reason
, "CARRIER") == 0 ||
356 strcmp(reason
, "UNKNOWN") == 0)
358 env
[9] = strdup("if_up=false");
359 env
[10] = strdup("if_down=false");
360 } else if (1 == 2 /* appease ifdefs */
362 || (dhcp
&& state
&& state
->new)
363 || (ipv4ll
&& IPV4LL_STATE_RUNNING(ifp
))
366 || (dhcp6
&& d6_state
&& d6_state
->new)
367 || (ra
&& ipv6nd_hasra(ifp
))
371 env
[9] = strdup("if_up=true");
372 env
[10] = strdup("if_down=false");
374 env
[9] = strdup("if_up=false");
375 env
[10] = strdup("if_down=true");
377 if (env
[9] == NULL
|| env
[10] == NULL
)
379 if ((af
= dhcpcd_ifafwaiting(ifp
)) != AF_MAX
) {
382 snprintf(env
[elen
++], e
, "if_afwaiting=%d", af
);
384 if ((af
= dhcpcd_afwaiting(ifp
->ctx
)) != AF_MAX
) {
385 TAILQ_FOREACH(ifp2
, ifp
->ctx
->ifaces
, next
) {
386 if ((af
= dhcpcd_ifafwaiting(ifp2
)) != AF_MAX
)
393 snprintf(env
[elen
++], e
, "af_waiting=%d", af
);
395 if (ifo
->options
& DHCPCD_DEBUG
) {
396 e
= strlen("syslog_debug=true") + 1;
398 snprintf(env
[elen
++], e
, "syslog_debug=true");
401 e
= strlen("profile=") + strlen(ifp
->profile
) + 1;
403 snprintf(env
[elen
++], e
, "profile=%s", ifp
->profile
);
406 static const char *pfx
= "ifssid=";
410 pfx_len
= strlen(pfx
);
411 psl
= print_string(NULL
, 0, ESCSTRING
,
412 (const uint8_t *)ifp
->ssid
, ifp
->ssid_len
);
414 EMALLOC(elen
, pfx_len
+ (size_t)psl
+ 1);
415 memcpy(env
[elen
], pfx
, pfx_len
);
416 print_string(env
[elen
] + pfx_len
, (size_t)psl
+ 1,
418 (const uint8_t *)ifp
->ssid
, ifp
->ssid_len
);
423 if (dhcp
&& state
&& state
->old
) {
424 n
= dhcp_env(NULL
, NULL
, state
->old
, ifp
);
428 nenv
= realloc(env
, sizeof(char *) *
429 (elen
+ (size_t)n
+ 1));
433 n
= dhcp_env(env
+ elen
, "old", state
->old
, ifp
);
438 if (append_config(ifp
->ctx
, &env
, &elen
, "old",
439 (const char *const *)ifo
->config
) == -1)
444 if (dhcp6
&& d6_state
&& d6_state
->old
) {
445 n
= dhcp6_env(NULL
, NULL
, ifp
,
446 d6_state
->old
, d6_state
->old_len
);
448 nenv
= realloc(env
, sizeof(char *) *
449 (elen
+ (size_t)n
+ 1));
453 n
= dhcp6_env(env
+ elen
, "old", ifp
,
454 d6_state
->old
, d6_state
->old_len
);
465 n
= ipv4ll_env(NULL
, NULL
, ifp
);
467 nenv
= realloc(env
, sizeof(char *) *
468 (elen
+ (size_t)n
+ 1));
472 if ((n
= ipv4ll_env(env
+ elen
,
473 istate
->down
? "old" : "new", ifp
)) == -1)
478 if (dhcp
&& state
&& state
->new) {
479 n
= dhcp_env(NULL
, NULL
, state
->new, ifp
);
481 nenv
= realloc(env
, sizeof(char *) *
482 (elen
+ (size_t)n
+ 1));
486 n
= dhcp_env(env
+ elen
, "new",
492 if (append_config(ifp
->ctx
, &env
, &elen
, "new",
493 (const char *const *)ifo
->config
) == -1)
498 if (dhcp6
&& D6_STATE_RUNNING(ifp
)) {
499 n
= dhcp6_env(NULL
, NULL
, ifp
,
500 d6_state
->new, d6_state
->new_len
);
502 nenv
= realloc(env
, sizeof(char *) *
503 (elen
+ (size_t)n
+ 1));
507 n
= dhcp6_env(env
+ elen
, "new", ifp
,
508 d6_state
->new, d6_state
->new_len
);
515 n
= ipv6nd_env(NULL
, NULL
, ifp
);
517 nenv
= realloc(env
, sizeof(char *) *
518 (elen
+ (size_t)n
+ 1));
522 n
= ipv6nd_env(env
+ elen
, NULL
, ifp
);
530 /* Add our base environment */
533 while (ifo
->environ
[e
++])
535 nenv
= realloc(env
, sizeof(char *) * (elen
+ e
+ 1));
540 while (ifo
->environ
[e
]) {
541 env
[elen
+ e
] = strdup(ifo
->environ
[e
]);
542 if (env
[elen
+ e
] == NULL
)
551 return (ssize_t
)elen
;
554 logger(ifp
->ctx
, LOG_ERR
, "%s: %m", __func__
);
565 send_interface1(struct fd_list
*fd
, const struct interface
*iface
,
568 char **env
, **ep
, *s
;
572 if (make_env(iface
, reason
, &env
) == -1)
575 elen
= (size_t)arraytostr((const char *const *)env
, &s
);
576 if ((ssize_t
)elen
== -1) {
580 retval
= control_queue(fd
, s
, elen
, 1);
589 send_interface(struct fd_list
*fd
, const struct interface
*ifp
)
594 const struct dhcp_state
*d
;
597 const struct dhcp6_state
*d6
;
600 switch (ifp
->carrier
) {
605 reason
= "NOCARRIER";
611 if (send_interface1(fd
, ifp
, reason
) == -1)
614 if (D_STATE_RUNNING(ifp
)) {
616 if (send_interface1(fd
, ifp
, d
->reason
) == -1)
619 if (IPV4LL_STATE_RUNNING(ifp
)) {
620 if (send_interface1(fd
, ifp
, "IPV4LL") == -1)
626 if (RS_STATE_RUNNING(ifp
)) {
627 if (send_interface1(fd
, ifp
, "ROUTERADVERT") == -1)
630 if (D6_STATE_RUNNING(ifp
)) {
632 if (send_interface1(fd
, ifp
, d6
->reason
) == -1)
641 script_runreason(const struct interface
*ifp
, const char *reason
)
644 char **env
= NULL
, **ep
;
645 char *svcname
, *path
, *bigenv
;
651 if (ifp
->options
->script
&&
652 (ifp
->options
->script
[0] == '\0' ||
653 strcmp(ifp
->options
->script
, "/dev/null") == 0) &&
654 TAILQ_FIRST(&ifp
->ctx
->control_fds
) == NULL
)
658 elen
= (size_t)make_env(ifp
, reason
, &env
);
659 if (elen
== (size_t)-1) {
660 logger(ifp
->ctx
, LOG_ERR
, "%s: make_env: %m", ifp
->name
);
664 if (ifp
->options
->script
&&
665 (ifp
->options
->script
[0] == '\0' ||
666 strcmp(ifp
->options
->script
, "/dev/null") == 0))
669 argv
[0] = ifp
->options
->script
? ifp
->options
->script
: UNCONST(SCRIPT
);
671 logger(ifp
->ctx
, LOG_DEBUG
, "%s: executing `%s' %s",
672 ifp
->name
, argv
[0], reason
);
674 /* Resize for PATH and RC_SVCNAME */
675 svcname
= getenv(RC_SVCNAME
);
676 ep
= realloc(env
, sizeof(char *) * (elen
+ 2 + (svcname
? 1 : 0)));
683 path
= getenv("PATH");
685 e
= strlen("PATH") + strlen(path
) + 2;
686 env
[elen
] = malloc(e
);
687 if (env
[elen
] == NULL
) {
691 snprintf(env
[elen
], e
, "PATH=%s", path
);
693 env
[elen
] = strdup(DEFAULT_PATH
);
694 if (env
[elen
] == NULL
) {
700 e
= strlen(RC_SVCNAME
) + strlen(svcname
) + 2;
701 env
[++elen
] = malloc(e
);
702 if (env
[elen
] == NULL
) {
706 snprintf(env
[elen
], e
, "%s=%s", RC_SVCNAME
, svcname
);
710 pid
= exec_script(ifp
->ctx
, argv
, env
);
712 logger(ifp
->ctx
, LOG_ERR
, "%s: %s: %m", __func__
, argv
[0]);
714 /* Wait for the script to finish */
715 while (waitpid(pid
, &status
, 0) == -1) {
716 if (errno
!= EINTR
) {
717 logger(ifp
->ctx
, LOG_ERR
, "waitpid: %m");
722 if (WIFEXITED(status
)) {
723 if (WEXITSTATUS(status
))
724 logger(ifp
->ctx
, LOG_ERR
,
725 "%s: %s: WEXITSTATUS %d",
726 __func__
, argv
[0], WEXITSTATUS(status
));
727 } else if (WIFSIGNALED(status
))
728 logger(ifp
->ctx
, LOG_ERR
, "%s: %s: %s",
729 __func__
, argv
[0], strsignal(WTERMSIG(status
)));
733 /* Send to our listeners */
736 TAILQ_FOREACH(fd
, &ifp
->ctx
->control_fds
, next
) {
737 if (!(fd
->flags
& FD_LISTEN
))
739 if (bigenv
== NULL
) {
740 elen
= (size_t)arraytostr((const char *const *)env
,
742 if ((ssize_t
)elen
== -1) {
743 logger(ifp
->ctx
, LOG_ERR
, "%s: arraytostr: %m",
748 if (control_queue(fd
, bigenv
, elen
, 1) == -1)
749 logger(ifp
->ctx
, LOG_ERR
,
750 "%s: control_queue: %m", __func__
);
765 return WEXITSTATUS(status
);