2 __RCSID("$NetBSD: control.c,v 1.10 2015/08/21 10:39:00 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
31 #include <sys/socket.h>
53 (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
57 control_queue_purge(struct dhcpcd_ctx
*ctx
, char *data
)
63 /* If no other fd queue has the same data, free it */
65 TAILQ_FOREACH(fp
, &ctx
->control_fds
, next
) {
66 TAILQ_FOREACH(fpd
, &fp
->queue
, next
) {
67 if (fpd
->data
== data
) {
78 control_queue_free(struct fd_list
*fd
)
82 while ((fdp
= TAILQ_FIRST(&fd
->queue
))) {
83 TAILQ_REMOVE(&fd
->queue
, fdp
, next
);
85 control_queue_purge(fd
->ctx
, fdp
->data
);
88 while ((fdp
= TAILQ_FIRST(&fd
->free_queue
))) {
89 TAILQ_REMOVE(&fd
->free_queue
, fdp
, next
);
95 control_delete(struct fd_list
*fd
)
98 TAILQ_REMOVE(&fd
->ctx
->control_fds
, fd
, next
);
99 eloop_event_delete(fd
->ctx
->eloop
, fd
->fd
);
101 control_queue_free(fd
);
106 control_handle_data(void *arg
)
108 struct fd_list
*fd
= arg
;
109 char buffer
[1024], *e
, *p
, *argvp
[255], **ap
, *a
;
114 bytes
= read(fd
->fd
, buffer
, sizeof(buffer
) - 1);
115 if (bytes
== -1 || bytes
== 0) {
116 /* Control was closed or there was an error.
117 * Remove it from our list. */
121 buffer
[bytes
] = '\0';
125 /* Each command is \n terminated
126 * Each argument is NULL separated */
132 if ((size_t)argc
>= sizeof(argvp
) / sizeof(argvp
[0])) {
139 if (len
&& a
[len
- 1] == '\n') {
145 if (dhcpcd_handleargs(fd
->ctx
, fd
, argc
, argvp
) == -1) {
146 logger(fd
->ctx
, LOG_ERR
,
147 "%s: dhcpcd_handleargs: %m", __func__
);
148 if (errno
!= EINTR
&& errno
!= EAGAIN
) {
157 control_handle1(struct dhcpcd_ctx
*ctx
, int lfd
, unsigned int fd_flags
)
159 struct sockaddr_un run
;
165 if ((fd
= accept(lfd
, (struct sockaddr
*)&run
, &len
)) == -1)
167 if ((flags
= fcntl(fd
, F_GETFD
, 0)) == -1 ||
168 fcntl(fd
, F_SETFD
, flags
| FD_CLOEXEC
) == -1)
173 if ((flags
= fcntl(fd
, F_GETFL
, 0)) == -1 ||
174 fcntl(fd
, F_SETFL
, flags
| O_NONBLOCK
) == -1)
179 l
= malloc(sizeof(*l
));
184 TAILQ_INIT(&l
->queue
);
185 TAILQ_INIT(&l
->free_queue
);
186 TAILQ_INSERT_TAIL(&ctx
->control_fds
, l
, next
);
187 eloop_event_add(ctx
->eloop
, l
->fd
,
188 control_handle_data
, l
, NULL
, NULL
);
194 control_handle(void *arg
)
196 struct dhcpcd_ctx
*ctx
= arg
;
198 control_handle1(ctx
, ctx
->control_fd
, 0);
202 control_handle_unpriv(void *arg
)
204 struct dhcpcd_ctx
*ctx
= arg
;
206 control_handle1(ctx
, ctx
->control_unpriv_fd
, FD_UNPRIV
);
210 make_sock(struct sockaddr_un
*sa
, const char *ifname
, int unpriv
)
214 if ((fd
= xsocket(AF_UNIX
, SOCK_STREAM
, 0, O_NONBLOCK
|O_CLOEXEC
)) == -1)
216 memset(sa
, 0, sizeof(*sa
));
217 sa
->sun_family
= AF_UNIX
;
219 strlcpy(sa
->sun_path
, UNPRIVSOCKET
, sizeof(sa
->sun_path
));
221 snprintf(sa
->sun_path
, sizeof(sa
->sun_path
), CONTROLSOCKET
,
222 ifname
? "-" : "", ifname
? ifname
: "");
227 #define S_PRIV (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
228 #define S_UNPRIV (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
231 control_start1(struct dhcpcd_ctx
*ctx
, const char *ifname
, mode_t fmode
)
233 struct sockaddr_un sa
;
237 if ((fd
= make_sock(&sa
, ifname
, (fmode
& S_UNPRIV
) == S_UNPRIV
)) == -1)
239 len
= (socklen_t
)SUN_LEN(&sa
);
241 if (bind(fd
, (struct sockaddr
*)&sa
, len
) == -1 ||
242 chmod(sa
.sun_path
, fmode
) == -1 ||
243 (ctx
->control_group
&&
244 chown(sa
.sun_path
, geteuid(), ctx
->control_group
) == -1) ||
245 listen(fd
, sizeof(ctx
->control_fds
)) == -1)
252 if ((fmode
& S_UNPRIV
) != S_UNPRIV
)
253 strlcpy(ctx
->control_sock
, sa
.sun_path
,
254 sizeof(ctx
->control_sock
));
259 control_start(struct dhcpcd_ctx
*ctx
, const char *ifname
)
263 if ((fd
= control_start1(ctx
, ifname
, S_PRIV
)) == -1)
266 ctx
->control_fd
= fd
;
267 eloop_event_add(ctx
->eloop
, fd
, control_handle
, ctx
, NULL
, NULL
);
269 if (ifname
== NULL
&& (fd
= control_start1(ctx
, NULL
, S_UNPRIV
)) != -1){
270 /* We must be in master mode, so create an unpriviledged socket
271 * to allow normal users to learn the status of dhcpcd. */
272 ctx
->control_unpriv_fd
= fd
;
273 eloop_event_add(ctx
->eloop
, fd
, control_handle_unpriv
,
276 return ctx
->control_fd
;
280 control_stop(struct dhcpcd_ctx
*ctx
)
285 if (ctx
->options
& DHCPCD_FORKED
)
288 if (ctx
->control_fd
== -1)
290 eloop_event_delete(ctx
->eloop
, ctx
->control_fd
);
291 close(ctx
->control_fd
);
292 ctx
->control_fd
= -1;
293 if (unlink(ctx
->control_sock
) == -1)
296 if (ctx
->control_unpriv_fd
!= -1) {
297 eloop_event_delete(ctx
->eloop
, ctx
->control_unpriv_fd
);
298 close(ctx
->control_unpriv_fd
);
299 ctx
->control_unpriv_fd
= -1;
300 if (unlink(UNPRIVSOCKET
) == -1)
305 while ((l
= TAILQ_FIRST(&ctx
->control_fds
))) {
306 TAILQ_REMOVE(&ctx
->control_fds
, l
, next
);
307 eloop_event_delete(ctx
->eloop
, l
->fd
);
309 control_queue_free(l
);
317 control_open(struct dhcpcd_ctx
*ctx
, const char *ifname
)
319 struct sockaddr_un sa
;
322 if ((ctx
->control_fd
= make_sock(&sa
, ifname
, 0)) == -1)
324 len
= (socklen_t
)SUN_LEN(&sa
);
325 if (connect(ctx
->control_fd
, (struct sockaddr
*)&sa
, len
) == -1) {
326 close(ctx
->control_fd
);
327 ctx
->control_fd
= -1;
334 control_send(struct dhcpcd_ctx
*ctx
, int argc
, char * const *argv
)
345 for (i
= 0; i
< argc
; i
++) {
346 l
= strlen(argv
[i
]) + 1;
347 if (len
+ l
> sizeof(buffer
)) {
351 memcpy(buffer
+ len
, argv
[i
], l
);
354 return write(ctx
->control_fd
, buffer
, len
);
358 control_writeone(void *arg
)
362 struct fd_data
*data
;
365 data
= TAILQ_FIRST(&fd
->queue
);
366 iov
[0].iov_base
= &data
->data_len
;
367 iov
[0].iov_len
= sizeof(size_t);
368 iov
[1].iov_base
= data
->data
;
369 iov
[1].iov_len
= data
->data_len
;
370 if (writev(fd
->fd
, iov
, 2) == -1) {
371 logger(fd
->ctx
, LOG_ERR
,
372 "%s: writev fd %d: %m", __func__
, fd
->fd
);
373 if (errno
!= EINTR
&& errno
!= EAGAIN
)
378 TAILQ_REMOVE(&fd
->queue
, data
, next
);
380 control_queue_purge(fd
->ctx
, data
->data
);
381 data
->data
= NULL
; /* safety */
383 TAILQ_INSERT_TAIL(&fd
->free_queue
, data
, next
);
385 if (TAILQ_FIRST(&fd
->queue
) == NULL
)
386 eloop_event_remove_writecb(fd
->ctx
->eloop
, fd
->fd
);
390 control_queue(struct fd_list
*fd
, char *data
, size_t data_len
, uint8_t fit
)
395 d
= TAILQ_FIRST(&fd
->free_queue
);
397 TAILQ_REMOVE(&fd
->free_queue
, d
, next
);
400 TAILQ_FOREACH(d
, &fd
->queue
, next
) {
401 if (++n
== CONTROL_QUEUE_MAX
) {
406 d
= malloc(sizeof(*d
));
411 d
->data_len
= data_len
;
413 TAILQ_INSERT_TAIL(&fd
->queue
, d
, next
);
414 eloop_event_add(fd
->ctx
->eloop
, fd
->fd
,
415 NULL
, NULL
, control_writeone
, fd
);
420 control_close(struct dhcpcd_ctx
*ctx
)
423 if (ctx
->control_fd
!= -1) {
424 close(ctx
->control_fd
);
425 ctx
->control_fd
= -1;