1 /* SPDX-License-Identifier: BSD-2-Clause */
3 * dhcpcd - DHCP client daemon
4 * Copyright (c) 2006-2019 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>
51 #include "if-options.h"
57 /* Allow the OS to define another script env var name */
59 #define RC_SVCNAME "RC_SVCNAME"
62 #define DEFAULT_PATH "/usr/bin:/usr/sbin:/bin:/sbin"
64 static const char * const if_params
[] = {
82 const char * const *p
;
84 for (p
= if_params
; *p
; p
++)
85 printf(" - %s\n", *p
);
89 exec_script(const struct dhcpcd_ctx
*ctx
, char *const *argv
, char *const *env
)
92 posix_spawnattr_t attr
;
102 /* posix_spawn is a safe way of executing another image
103 * and changing signals back to how they should be. */
104 if (posix_spawnattr_init(&attr
) == -1)
107 flags
= POSIX_SPAWN_SETSIGMASK
| POSIX_SPAWN_SETSIGDEF
;
108 posix_spawnattr_setflags(&attr
, flags
);
109 sigemptyset(&defsigs
);
110 for (i
= 0; i
< dhcpcd_signals_len
; i
++)
111 sigaddset(&defsigs
, dhcpcd_signals
[i
]);
112 posix_spawnattr_setsigdefault(&attr
, &defsigs
);
113 posix_spawnattr_setsigmask(&attr
, &ctx
->sigset
);
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 make_env(const struct interface
*ifp
, const char *reason
)
179 struct dhcpcd_ctx
*ctx
= ifp
->ctx
;
181 char **env
, **envp
, *bufp
, *endp
, *path
;
184 int protocol
= PROTO_LINK
;
185 const struct if_options
*ifo
= ifp
->options
;
186 const struct interface
*ifp2
;
189 const struct dhcp_state
*state
;
191 const struct ipv4ll_state
*istate
;
195 const struct dhcp6_state
*d6_state
;
198 #ifdef HAVE_OPEN_MEMSTREAM
199 if (ctx
->script_fp
== NULL
) {
200 fp
= open_memstream(&ctx
->script_buf
, &ctx
->script_buflen
);
209 char tmpfile
[] = "/tmp/dhcpcd-script-env-XXXXXX";
213 tmpfd
= mkstemp(tmpfile
);
217 fp
= fdopen(tmpfd
, "w+");
225 state
= D_STATE(ifp
);
227 istate
= IPV4LL_CSTATE(ifp
);
231 d6_state
= D6_CSTATE(ifp
);
233 if (strcmp(reason
, "TEST") == 0) {
237 else if (d6_state
&& d6_state
->new)
238 protocol
= PROTO_DHCP6
;
240 else if (ipv6nd_hasra(ifp
))
245 else if (istate
&& istate
->addr
!= NULL
)
246 protocol
= PROTO_IPV4LL
;
249 protocol
= PROTO_DHCP
;
253 else if (strcmp(reason
, "STATIC6") == 0)
254 protocol
= PROTO_STATIC6
;
256 else if (reason
[strlen(reason
) - 1] == '6')
257 protocol
= PROTO_DHCP6
;
259 else if (strcmp(reason
, "ROUTERADVERT") == 0)
262 else if (strcmp(reason
, "PREINIT") == 0 ||
263 strcmp(reason
, "CARRIER") == 0 ||
264 strcmp(reason
, "NOCARRIER") == 0 ||
265 strcmp(reason
, "UNKNOWN") == 0 ||
266 strcmp(reason
, "DEPARTED") == 0 ||
267 strcmp(reason
, "STOPPED") == 0)
268 protocol
= PROTO_LINK
;
271 else if (strcmp(reason
, "IPV4LL") == 0)
272 protocol
= PROTO_IPV4LL
;
275 protocol
= PROTO_DHCP
;
278 /* Needed for scripts */
279 path
= getenv("PATH");
280 if (efprintf(fp
, "PATH=%s", path
== NULL
? DEFAULT_PATH
:path
) == -1)
283 if (efprintf(fp
, "interface=%s", ifp
->name
) == -1)
285 if (efprintf(fp
, "reason=%s", reason
) == -1)
287 if (ifp
->ctx
->options
& DHCPCD_DUMPLEASE
)
289 if (efprintf(fp
, "pid=%d", getpid()) == -1)
291 if (efprintf(fp
, "ifcarrier=%s",
292 ifp
->carrier
== LINK_UNKNOWN
? "unknown" :
293 ifp
->carrier
== LINK_UP
? "up" : "down") == -1)
295 if (efprintf(fp
, "ifmetric=%d", ifp
->metric
) == -1)
297 if (efprintf(fp
, "ifwireless=%d", ifp
->wireless
) == -1)
299 if (efprintf(fp
, "ifflags=%u", ifp
->flags
) == -1)
301 if (efprintf(fp
, "ifmtu=%d", if_getmtu(ifp
)) == -1)
304 if (fprintf(fp
, "interface_order=") == -1)
306 TAILQ_FOREACH(ifp2
, ifp
->ctx
->ifaces
, next
) {
307 if (ifp2
!= TAILQ_FIRST(ifp
->ctx
->ifaces
)) {
308 if (fputc(' ', fp
) == EOF
)
311 if (fprintf(fp
, "%s", ifp2
->name
) == -1)
314 if (fputc('\0', fp
) == EOF
)
317 if (strcmp(reason
, "STOPPED") == 0) {
318 if (efprintf(fp
, "if_up=false") == -1)
320 if (efprintf(fp
, "if_down=%s",
321 ifo
->options
& DHCPCD_RELEASE
? "true" : "false") == -1)
323 } else if (strcmp(reason
, "TEST") == 0 ||
324 strcmp(reason
, "PREINIT") == 0 ||
325 strcmp(reason
, "CARRIER") == 0 ||
326 strcmp(reason
, "UNKNOWN") == 0)
328 if (efprintf(fp
, "if_up=false") == -1)
330 if (efprintf(fp
, "if_down=false") == -1)
332 } else if (1 == 2 /* appease ifdefs */
334 || (protocol
== PROTO_DHCP
&& state
&& state
->new)
336 || (protocol
== PROTO_IPV4LL
&& IPV4LL_STATE_RUNNING(ifp
))
340 || (protocol
== PROTO_STATIC6
&& IPV6_STATE_RUNNING(ifp
))
342 || (protocol
== PROTO_DHCP6
&& d6_state
&& d6_state
->new)
344 || (protocol
== PROTO_RA
&& ipv6nd_hasra(ifp
))
348 if (efprintf(fp
, "if_up=true") == -1)
350 if (efprintf(fp
, "if_down=false") == -1)
353 if (efprintf(fp
, "if_up=false") == -1)
355 if (efprintf(fp
, "if_down=true") == -1)
358 if (protocols
[protocol
] != NULL
) {
359 if (efprintf(fp
, "protocol=%s", protocols
[protocol
]) == -1)
362 if ((af
= dhcpcd_ifafwaiting(ifp
)) != AF_MAX
) {
363 if (efprintf(fp
, "if_afwaiting=%d", af
) == -1)
366 if ((af
= dhcpcd_afwaiting(ifp
->ctx
)) != AF_MAX
) {
367 TAILQ_FOREACH(ifp2
, ifp
->ctx
->ifaces
, next
) {
368 if ((af
= dhcpcd_ifafwaiting(ifp2
)) != AF_MAX
)
373 if (efprintf(fp
, "af_waiting=%d", af
) == -1)
376 if (ifo
->options
& DHCPCD_DEBUG
) {
377 if (efprintf(fp
, "syslog_debug=true") == -1)
381 if (efprintf(fp
, "profile=%s", ifp
->profile
) == -1)
385 char pssid
[IF_SSIDLEN
* 4];
387 if (print_string(pssid
, sizeof(pssid
), OT_ESCSTRING
,
388 ifp
->ssid
, ifp
->ssid_len
) != -1)
390 if (efprintf(fp
, "ifssid=%s", pssid
) == -1)
395 if (protocol
== PROTO_DHCP
&& state
&& state
->old
) {
396 if (dhcp_env(fp
, "old", ifp
,
397 state
->old
, state
->old_len
) == -1)
399 if (append_config(fp
, "old",
400 (const char *const *)ifo
->config
) == -1)
405 if (protocol
== PROTO_DHCP6
&& d6_state
&& d6_state
->old
) {
406 if (dhcp6_env(fp
, "old", ifp
,
407 d6_state
->old
, d6_state
->old_len
) == -1)
415 if (protocol
== PROTO_IPV4LL
) {
416 if (ipv4ll_env(fp
, istate
->down
? "old" : "new", ifp
) == -1)
420 if (protocol
== PROTO_DHCP
&& state
&& state
->new) {
421 if (dhcp_env(fp
, "new", ifp
,
422 state
->new, state
->new_len
) == -1)
424 if (append_config(fp
, "new",
425 (const char *const *)ifo
->config
) == -1)
430 if (protocol
== PROTO_STATIC6
) {
431 if (ipv6_env(fp
, "new", ifp
) == -1)
435 if (protocol
== PROTO_DHCP6
&& D6_STATE_RUNNING(ifp
)) {
436 if (dhcp6_env(fp
, "new", ifp
,
437 d6_state
->new, d6_state
->new_len
) == -1)
441 if (protocol
== PROTO_RA
) {
442 if (ipv6nd_env(fp
, ifp
) == -1)
447 /* Add our base environment */
449 for (i
= 0; ifo
->environ
[i
] != NULL
; i
++)
450 if (efprintf(fp
, "%s", ifo
->environ
[i
]) == -1)
454 /* Convert buffer to argv */
463 #ifndef HAVE_OPEN_MEMSTREAM
464 size_t buf_len
= (size_t)buf_pos
;
465 if (ctx
->script_buflen
< buf_len
) {
466 char *buf
= realloc(ctx
->script_buf
, buf_len
);
469 ctx
->script_buf
= buf
;
470 ctx
->script_buflen
= buf_len
;
473 if (fread(ctx
->script_buf
, sizeof(char), buf_len
, fp
) != buf_len
)
479 /* Count the terminated env strings.
480 * Assert that the terminations are correct. */
482 endp
= ctx
->script_buf
+ buf_pos
;
483 for (bufp
= ctx
->script_buf
; bufp
< endp
; bufp
++) {
487 assert(*(bufp
+ 1) != '\0');
492 assert(*(bufp
- 1) == '\0');
494 if (ctx
->script_envlen
< nenv
) {
495 env
= reallocarray(ctx
->script_env
, nenv
+ 1, sizeof(*env
));
498 ctx
->script_env
= env
;
499 ctx
->script_envlen
= nenv
;
502 bufp
= ctx
->script_buf
;
503 envp
= ctx
->script_env
;
505 endp
--; /* Avoid setting the last \0 to an invalid pointer */
506 for (; bufp
< endp
; bufp
++) {
516 #ifndef HAVE_OPEN_MEMSTREAM
524 send_interface1(struct fd_list
*fd
, const struct interface
*ifp
,
527 struct dhcpcd_ctx
*ctx
= ifp
->ctx
;
530 len
= make_env(ifp
, reason
);
533 return control_queue(fd
, ctx
->script_buf
, (size_t)len
, 1);
537 send_interface(struct fd_list
*fd
, const struct interface
*ifp
)
542 const struct dhcp_state
*d
;
545 const struct dhcp6_state
*d6
;
548 switch (ifp
->carrier
) {
553 case LINK_DOWN_IFFUP
:
554 reason
= "NOCARRIER";
560 if (send_interface1(fd
, ifp
, reason
) == -1)
563 if (D_STATE_RUNNING(ifp
)) {
565 if (send_interface1(fd
, ifp
, d
->reason
) == -1)
569 if (IPV4LL_STATE_RUNNING(ifp
)) {
570 if (send_interface1(fd
, ifp
, "IPV4LL") == -1)
577 if (IPV6_STATE_RUNNING(ifp
)) {
578 if (send_interface1(fd
, ifp
, "STATIC6") == -1)
581 if (RS_STATE_RUNNING(ifp
)) {
582 if (send_interface1(fd
, ifp
, "ROUTERADVERT") == -1)
586 if (D6_STATE_RUNNING(ifp
)) {
588 if (send_interface1(fd
, ifp
, d6
->reason
) == -1)
598 script_runreason(const struct interface
*ifp
, const char *reason
)
600 struct dhcpcd_ctx
*ctx
= ifp
->ctx
;
606 if (ifp
->options
->script
== NULL
&&
607 TAILQ_FIRST(&ifp
->ctx
->control_fds
) == NULL
)
611 if (make_env(ifp
, reason
) == -1) {
616 if (ifp
->options
->script
== NULL
)
619 argv
[0] = ifp
->options
->script
;
621 logdebugx("%s: executing `%s' %s", ifp
->name
, argv
[0], reason
);
623 pid
= exec_script(ctx
, argv
, ctx
->script_env
);
625 logerr("%s: %s", __func__
, argv
[0]);
627 /* Wait for the script to finish */
628 while (waitpid(pid
, &status
, 0) == -1) {
629 if (errno
!= EINTR
) {
630 logerr("%s: waitpid", __func__
);
635 if (WIFEXITED(status
)) {
636 if (WEXITSTATUS(status
))
637 logerrx("%s: %s: WEXITSTATUS %d",
638 __func__
, argv
[0], WEXITSTATUS(status
));
639 } else if (WIFSIGNALED(status
))
640 logerrx("%s: %s: %s",
641 __func__
, argv
[0], strsignal(WTERMSIG(status
)));
645 /* Send to our listeners */
647 TAILQ_FOREACH(fd
, &ctx
->control_fds
, next
) {
648 if (!(fd
->flags
& FD_LISTEN
))
650 if (control_queue(fd
, ctx
->script_buf
, ctx
->script_buflen
,
652 logerr("%s: control_queue", __func__
);
657 return WEXITSTATUS(status
);