1 /* SPDX-License-Identifier: BSD-2-Clause */
3 * dhcpcd - DHCP client daemon
4 * Copyright (c) 2006-2020 Roy Marples <roy@marples.name>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
53 #include "if-options.h"
60 #define DEFAULT_PATH "/usr/bin:/usr/sbin:/bin:/sbin"
62 static const char * const if_params
[] = {
80 const char * const *p
;
82 for (p
= if_params
; *p
; p
++)
83 printf(" - %s\n", *p
);
87 script_exec(char *const *argv
, char *const *env
)
90 posix_spawnattr_t attr
;
100 /* posix_spawn is a safe way of executing another image
101 * and changing signals back to how they should be. */
102 if (posix_spawnattr_init(&attr
) == -1)
105 flags
= POSIX_SPAWN_SETSIGMASK
| POSIX_SPAWN_SETSIGDEF
;
106 posix_spawnattr_setflags(&attr
, flags
);
107 sigemptyset(&defsigs
);
108 posix_spawnattr_setsigmask(&attr
, &defsigs
);
109 for (i
= 0; i
< dhcpcd_signals_len
; i
++)
110 sigaddset(&defsigs
, dhcpcd_signals
[i
]);
111 for (i
= 0; i
< dhcpcd_signals_ignore_len
; i
++)
112 sigaddset(&defsigs
, dhcpcd_signals_ignore
[i
]);
113 posix_spawnattr_setsigdefault(&attr
, &defsigs
);
116 r
= posix_spawn(&pid
, argv
[0], NULL
, &attr
, argv
, env
);
117 posix_spawnattr_destroy(&attr
);
127 append_config(FILE *fp
, const char *prefix
, const char *const *config
)
134 /* Do we need to replace existing config rather than append? */
135 for (i
= 0; config
[i
] != NULL
; i
++) {
136 if (efprintf(fp
, "%s_%s", prefix
, config
[i
]) == -1)
146 #define PROTO_IPV4LL 2
148 #define PROTO_DHCP6 4
149 #define PROTO_STATIC6 5
150 static const char *protocols
[] = {
160 efprintf(FILE *fp
, const char *fmt
, ...)
166 r
= vfprintf(fp
, fmt
, args
);
170 /* Write a trailing NULL so we can easily create env strings. */
171 if (fputc('\0', fp
) == EOF
)
177 script_buftoenv(struct dhcpcd_ctx
*ctx
, char *buf
, size_t len
)
179 char **env
, **envp
, *bufp
, *endp
;
182 /* Count the terminated env strings.
183 * Assert that the terminations are correct. */
186 for (bufp
= buf
; bufp
< endp
; bufp
++) {
190 assert(*(bufp
+ 1) != '\0');
195 assert(*(bufp
- 1) == '\0');
199 if (ctx
->script_envlen
< nenv
) {
200 env
= reallocarray(ctx
->script_env
, nenv
+ 1, sizeof(*env
));
203 ctx
->script_env
= env
;
204 ctx
->script_envlen
= nenv
;
208 envp
= ctx
->script_env
;
210 endp
--; /* Avoid setting the last \0 to an invalid pointer */
211 for (; bufp
< endp
; bufp
++) {
217 return ctx
->script_env
;
221 make_env(struct dhcpcd_ctx
*ctx
, const struct interface
*ifp
,
227 int protocol
= PROTO_LINK
;
228 const struct if_options
*ifo
;
229 const struct interface
*ifp2
;
232 const struct dhcp_state
*state
;
234 const struct ipv4ll_state
*istate
;
238 const struct dhcp6_state
*d6_state
;
240 bool is_stdin
= ifp
->name
[0] == '\0';
242 #ifdef HAVE_OPEN_MEMSTREAM
243 if (ctx
->script_fp
== NULL
) {
244 fp
= open_memstream(&ctx
->script_buf
, &ctx
->script_buflen
);
253 char tmpfile
[] = "/tmp/dhcpcd-script-env-XXXXXX";
257 tmpfd
= mkstemp(tmpfile
);
259 logerr("%s: mkstemp", __func__
);
263 fp
= fdopen(tmpfd
, "w+");
270 if (!(ifp
->ctx
->options
& DHCPCD_DUMPLEASE
)) {
271 /* Needed for scripts */
272 path
= getenv("PATH");
273 if (efprintf(fp
, "PATH=%s",
274 path
== NULL
? DEFAULT_PATH
: path
) == -1)
276 if (efprintf(fp
, "pid=%d", getpid()) == -1)
280 if (efprintf(fp
, "reason=%s", reason
) == -1)
286 state
= D_STATE(ifp
);
288 istate
= IPV4LL_CSTATE(ifp
);
292 d6_state
= D6_CSTATE(ifp
);
294 if (strcmp(reason
, "TEST") == 0) {
296 /* This space left intentionally blank
297 * as all the below statements are optional. */
301 else if (d6_state
&& d6_state
->new)
302 protocol
= PROTO_DHCP6
;
304 else if (ipv6nd_hasra(ifp
))
309 else if (istate
&& istate
->addr
!= NULL
)
310 protocol
= PROTO_IPV4LL
;
313 protocol
= PROTO_DHCP
;
317 else if (strcmp(reason
, "STATIC6") == 0)
318 protocol
= PROTO_STATIC6
;
320 else if (reason
[strlen(reason
) - 1] == '6')
321 protocol
= PROTO_DHCP6
;
323 else if (strcmp(reason
, "ROUTERADVERT") == 0)
326 else if (strcmp(reason
, "PREINIT") == 0 ||
327 strcmp(reason
, "CARRIER") == 0 ||
328 strcmp(reason
, "NOCARRIER") == 0 ||
329 strcmp(reason
, "UNKNOWN") == 0 ||
330 strcmp(reason
, "DEPARTED") == 0 ||
331 strcmp(reason
, "STOPPED") == 0)
332 protocol
= PROTO_LINK
;
335 else if (strcmp(reason
, "IPV4LL") == 0)
336 protocol
= PROTO_IPV4LL
;
339 protocol
= PROTO_DHCP
;
343 if (efprintf(fp
, "interface=%s", ifp
->name
) == -1)
345 if (protocols
[protocol
] != NULL
) {
346 if (efprintf(fp
, "protocol=%s",
347 protocols
[protocol
]) == -1)
351 if (ifp
->ctx
->options
& DHCPCD_DUMPLEASE
&& protocol
!= PROTO_LINK
)
353 if (efprintf(fp
, "if_configured=%s",
354 ifo
->options
& DHCPCD_CONFIGURE
? "true" : "false") == -1)
356 if (efprintf(fp
, "ifcarrier=%s",
357 ifp
->carrier
== LINK_UNKNOWN
? "unknown" :
358 ifp
->carrier
== LINK_UP
? "up" : "down") == -1)
360 if (efprintf(fp
, "ifmetric=%d", ifp
->metric
) == -1)
362 if (efprintf(fp
, "ifwireless=%d", ifp
->wireless
) == -1)
364 if (efprintf(fp
, "ifflags=%u", ifp
->flags
) == -1)
366 if (efprintf(fp
, "ifmtu=%d", if_getmtu(ifp
)) == -1)
369 char pssid
[IF_SSIDLEN
* 4];
371 if (print_string(pssid
, sizeof(pssid
), OT_ESCSTRING
,
372 ifp
->ssid
, ifp
->ssid_len
) != -1)
374 if (efprintf(fp
, "ifssid=%s", pssid
) == -1)
378 if (*ifp
->profile
!= '\0') {
379 if (efprintf(fp
, "profile=%s", ifp
->profile
) == -1)
382 if (ifp
->ctx
->options
& DHCPCD_DUMPLEASE
)
385 if (fprintf(fp
, "interface_order=") == -1)
387 TAILQ_FOREACH(ifp2
, ifp
->ctx
->ifaces
, next
) {
388 if (ifp2
!= TAILQ_FIRST(ifp
->ctx
->ifaces
)) {
389 if (fputc(' ', fp
) == EOF
)
392 if (fprintf(fp
, "%s", ifp2
->name
) == -1)
395 if (fputc('\0', fp
) == EOF
)
398 if (strcmp(reason
, "STOPPED") == 0) {
399 if (efprintf(fp
, "if_up=false") == -1)
401 if (efprintf(fp
, "if_down=%s",
402 ifo
->options
& DHCPCD_RELEASE
? "true" : "false") == -1)
404 } else if (strcmp(reason
, "TEST") == 0 ||
405 strcmp(reason
, "PREINIT") == 0 ||
406 strcmp(reason
, "CARRIER") == 0 ||
407 strcmp(reason
, "UNKNOWN") == 0)
409 if (efprintf(fp
, "if_up=false") == -1)
411 if (efprintf(fp
, "if_down=false") == -1)
413 } else if (1 == 2 /* appease ifdefs */
415 || (protocol
== PROTO_DHCP
&& state
&& state
->new)
417 || (protocol
== PROTO_IPV4LL
&& IPV4LL_STATE_RUNNING(ifp
))
421 || (protocol
== PROTO_STATIC6
&& IPV6_STATE_RUNNING(ifp
))
423 || (protocol
== PROTO_DHCP6
&& d6_state
&& d6_state
->new)
425 || (protocol
== PROTO_RA
&& ipv6nd_hasra(ifp
))
429 if (efprintf(fp
, "if_up=true") == -1)
431 if (efprintf(fp
, "if_down=false") == -1)
434 if (efprintf(fp
, "if_up=false") == -1)
436 if (efprintf(fp
, "if_down=true") == -1)
439 if ((af
= dhcpcd_ifafwaiting(ifp
)) != AF_MAX
) {
440 if (efprintf(fp
, "if_afwaiting=%d", af
) == -1)
443 if ((af
= dhcpcd_afwaiting(ifp
->ctx
)) != AF_MAX
) {
444 TAILQ_FOREACH(ifp2
, ifp
->ctx
->ifaces
, next
) {
445 if ((af
= dhcpcd_ifafwaiting(ifp2
)) != AF_MAX
)
450 if (efprintf(fp
, "af_waiting=%d", af
) == -1)
453 if (ifo
->options
& DHCPCD_DEBUG
) {
454 if (efprintf(fp
, "syslog_debug=true") == -1)
458 if (protocol
== PROTO_DHCP
&& state
&& state
->old
) {
459 if (dhcp_env(fp
, "old", ifp
,
460 state
->old
, state
->old_len
) == -1)
462 if (append_config(fp
, "old",
463 (const char *const *)ifo
->config
) == -1)
468 if (protocol
== PROTO_DHCP6
&& d6_state
&& d6_state
->old
) {
469 if (dhcp6_env(fp
, "old", ifp
,
470 d6_state
->old
, d6_state
->old_len
) == -1)
478 if (protocol
== PROTO_IPV4LL
&& istate
) {
479 if (ipv4ll_env(fp
, istate
->down
? "old" : "new", ifp
) == -1)
483 if (protocol
== PROTO_DHCP
&& state
&& state
->new) {
484 if (dhcp_env(fp
, "new", ifp
,
485 state
->new, state
->new_len
) == -1)
487 if (append_config(fp
, "new",
488 (const char *const *)ifo
->config
) == -1)
493 if (protocol
== PROTO_STATIC6
) {
494 if (ipv6_env(fp
, "new", ifp
) == -1)
498 if (protocol
== PROTO_DHCP6
&& D6_STATE_RUNNING(ifp
)) {
499 if (dhcp6_env(fp
, "new", ifp
,
500 d6_state
->new, d6_state
->new_len
) == -1)
504 if (protocol
== PROTO_RA
) {
505 if (ipv6nd_env(fp
, ifp
) == -1)
510 /* Add our base environment */
512 for (i
= 0; ifo
->environ
[i
] != NULL
; i
++)
513 if (efprintf(fp
, "%s", ifo
->environ
[i
]) == -1)
517 /* Convert buffer to argv */
526 #ifndef HAVE_OPEN_MEMSTREAM
527 size_t buf_len
= (size_t)buf_pos
;
528 if (ctx
->script_buflen
< buf_len
) {
529 char *buf
= realloc(ctx
->script_buf
, buf_len
);
532 ctx
->script_buf
= buf
;
533 ctx
->script_buflen
= buf_len
;
536 if (fread(ctx
->script_buf
, sizeof(char), buf_len
, fp
) != buf_len
)
545 if (script_buftoenv(ctx
, ctx
->script_buf
, (size_t)buf_pos
) == NULL
)
552 #ifndef HAVE_OPEN_MEMSTREAM
560 send_interface1(struct fd_list
*fd
, const struct interface
*ifp
,
563 struct dhcpcd_ctx
*ctx
= ifp
->ctx
;
566 len
= make_env(ifp
->ctx
, ifp
, reason
);
569 return control_queue(fd
, ctx
->script_buf
, (size_t)len
);
573 send_interface(struct fd_list
*fd
, const struct interface
*ifp
, int af
)
577 const struct dhcp_state
*d
;
580 const struct dhcp6_state
*d6
;
584 #define AF_LINK AF_PACKET
587 if (af
== AF_UNSPEC
|| af
== AF_LINK
) {
590 switch (ifp
->carrier
) {
595 reason
= "NOCARRIER";
602 if (send_interface1(fd
, ifp
, reason
) == -1)
609 if (af
== AF_UNSPEC
|| af
== AF_INET
) {
610 if (D_STATE_RUNNING(ifp
)) {
613 if (send_interface1(fd
, ifp
, d
->reason
) == -1)
619 if (IPV4LL_STATE_RUNNING(ifp
)) {
621 if (send_interface1(fd
, ifp
, "IPV4LL") == -1)
631 if (af
== AF_UNSPEC
|| af
== AF_INET6
) {
632 if (IPV6_STATE_RUNNING(ifp
)) {
634 if (send_interface1(fd
, ifp
, "STATIC6") == -1)
639 if (RS_STATE_RUNNING(ifp
)) {
641 if (send_interface1(fd
, ifp
,
642 "ROUTERADVERT") == -1)
648 if (D6_STATE_RUNNING(ifp
)) {
651 if (send_interface1(fd
, ifp
, d6
->reason
) == -1)
664 script_run(struct dhcpcd_ctx
*ctx
, char **argv
)
669 pid
= script_exec(argv
, ctx
->script_env
);
671 logerr("%s: %s", __func__
, argv
[0]);
673 /* Wait for the script to finish */
674 while (waitpid(pid
, &status
, 0) == -1) {
675 if (errno
!= EINTR
) {
676 logerr("%s: waitpid", __func__
);
681 if (WIFEXITED(status
)) {
682 if (WEXITSTATUS(status
))
683 logerrx("%s: %s: WEXITSTATUS %d",
684 __func__
, argv
[0], WEXITSTATUS(status
));
685 } else if (WIFSIGNALED(status
))
686 logerrx("%s: %s: %s",
687 __func__
, argv
[0], strsignal(WTERMSIG(status
)));
690 return WEXITSTATUS(status
);
694 script_dump(const char *env
, size_t len
)
696 const char *ep
= env
+ len
;
701 if (*(ep
- 1) != '\0') {
706 for (; env
< ep
; env
+= strlen(env
) + 1) {
707 if (strncmp(env
, "new_", 4) == 0)
715 script_runreason(const struct interface
*ifp
, const char *reason
)
717 struct dhcpcd_ctx
*ctx
= ifp
->ctx
;
723 if (ctx
->script
== NULL
&&
724 TAILQ_FIRST(&ifp
->ctx
->control_fds
) == NULL
)
728 if ((buflen
= make_env(ifp
->ctx
, ifp
, reason
)) == -1) {
733 if (strncmp(reason
, "DUMP", 4) == 0)
734 return script_dump(ctx
->script_buf
, (size_t)buflen
);
736 if (ctx
->script
== NULL
)
739 argv
[0] = ctx
->script
;
741 logdebugx("%s: executing: %s %s", ifp
->name
, argv
[0], reason
);
744 if (ctx
->options
& DHCPCD_PRIVSEP
) {
745 if (ps_root_script(ctx
,
746 ctx
->script_buf
, ctx
->script_buflen
) == -1)
752 script_run(ctx
, argv
);
755 /* Send to our listeners */
757 TAILQ_FOREACH(fd
, &ctx
->control_fds
, next
) {
758 if (!(fd
->flags
& FD_LISTEN
))
760 if (control_queue(fd
, ctx
->script_buf
, ctx
->script_buflen
)== -1)
761 logerr("%s: control_queue", __func__
);