#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <syslog.h>
#include <time.h>
#include <unistd.h>
(sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
#endif
+static void
+control_queue_purge(struct dhcpcd_ctx *ctx, char *data)
+{
+ int found;
+ struct fd_list *fp;
+ struct fd_data *fpd;
+
+ /* If no other fd queue has the same data, free it */
+ found = 0;
+ TAILQ_FOREACH(fp, &ctx->control_fds, next) {
+ TAILQ_FOREACH(fpd, &fp->queue, next) {
+ if (fpd->data == data) {
+ found = 1;
+ break;
+ }
+ }
+ }
+ if (!found)
+ free(data);
+}
+
+static void
+control_queue_free(struct fd_list *fd)
+{
+ struct fd_data *fdp;
+
+ while ((fdp = TAILQ_FIRST(&fd->queue))) {
+ TAILQ_REMOVE(&fd->queue, fdp, next);
+ if (fdp->freeit)
+ control_queue_purge(fd->ctx, fdp->data);
+ free(fdp);
+ }
+ while ((fdp = TAILQ_FIRST(&fd->free_queue))) {
+ TAILQ_REMOVE(&fd->free_queue, fdp, next);
+ free(fdp);
+ }
+}
+
static void
control_handle_data(void *arg)
{
- struct fd_list *l = arg, *lp, *last;
+ struct fd_list *l = arg;
char buffer[1024], *e, *p, *argvp[255], **ap, *a;
ssize_t bytes;
size_t len;
if (bytes == -1 || bytes == 0) {
/* Control was closed or there was an error.
* Remove it from our list. */
- last = NULL;
- for (lp = l->ctx->control_fds; lp; lp = lp->next) {
- if (lp == l) {
- eloop_event_delete(lp->ctx->eloop, lp->fd, 0);
- close(lp->fd);
- if (last == NULL)
- lp->ctx->control_fds = lp->next;
- else
- last->next = lp->next;
- free(lp);
- break;
- }
- last = lp;
- }
+ TAILQ_REMOVE(&l->ctx->control_fds, l, next);
+ eloop_event_delete(l->ctx->eloop, l->fd, 0);
+ close(l->fd);
+ control_queue_free(l);
+ free(l);
return;
}
buffer[bytes] = '\0';
}
}
*ap = NULL;
- dhcpcd_handleargs(l->ctx, l, argc, argvp);
+ if (dhcpcd_handleargs(l->ctx, l, argc, argvp) == -1)
+ syslog(LOG_ERR, "%s: dhcpcd_handleargs: %m", __func__);
}
}
l->ctx = ctx;
l->fd = fd;
l->listener = 0;
- l->next = ctx->control_fds;
- ctx->control_fds = l;
+ TAILQ_INIT(&l->queue);
+ TAILQ_INIT(&l->free_queue);
+ TAILQ_INSERT_TAIL(&ctx->control_fds, l, next);
eloop_event_add(ctx->eloop, l->fd,
control_handle_data, l, NULL, NULL);
} else
if (ctx->control_fd == -1)
return 0;
eloop_event_delete(ctx->eloop, ctx->control_fd, 0);
- if (shutdown(ctx->control_fd, SHUT_RDWR) == -1)
- retval = 1;
close(ctx->control_fd);
ctx->control_fd = -1;
if (unlink(ctx->control_sock) == -1)
retval = -1;
- l = ctx->control_fds;
- while (l != NULL) {
- ctx->control_fds = l->next;
+ while ((l = TAILQ_FIRST(&ctx->control_fds))) {
+ TAILQ_REMOVE(&ctx->control_fds, l, next);
eloop_event_delete(ctx->eloop, l->fd, 0);
- shutdown(l->fd, SHUT_RDWR);
close(l->fd);
+ control_queue_free(l);
free(l);
- l = ctx->control_fds;
}
return retval;
return write(ctx->control_fd, buffer, len);
}
+static void
+control_writeone(void *arg)
+{
+ struct fd_list *fd;
+ struct iovec iov[2];
+ struct fd_data *data;
+
+ fd = arg;
+ data = TAILQ_FIRST(&fd->queue);
+ iov[0].iov_base = &data->data_len;
+ iov[0].iov_len = sizeof(size_t);
+ iov[1].iov_base = data->data;
+ iov[1].iov_len = data->data_len;
+ if (writev(fd->fd, iov, 2) == -1) {
+ syslog(LOG_ERR, "%s: writev: %m", __func__);
+ return;
+ }
+
+ TAILQ_REMOVE(&fd->queue, data, next);
+ if (data->freeit)
+ control_queue_purge(fd->ctx, data->data);
+ data->data = NULL; /* safety */
+ data->data_len = 0;
+ TAILQ_INSERT_TAIL(&fd->free_queue, data, next);
+
+ if (TAILQ_FIRST(&fd->queue) == NULL)
+ eloop_event_delete(fd->ctx->eloop, fd->fd, 1);
+}
+
+int
+control_queue(struct fd_list *fd, char *data, size_t data_len, uint8_t fit)
+{
+ struct fd_data *d;
+ size_t n;
+
+ d = TAILQ_FIRST(&fd->free_queue);
+ if (d) {
+ TAILQ_REMOVE(&fd->free_queue, d, next);
+ } else {
+ n = 0;
+ TAILQ_FOREACH(d, &fd->queue, next) {
+ if (++n == CONTROL_QUEUE_MAX) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ }
+ d = malloc(sizeof(*d));
+ if (d == NULL)
+ return -1;
+ }
+ d->data = data;
+ d->data_len = data_len;
+ d->freeit = fit;
+ TAILQ_INSERT_TAIL(&fd->queue, d, next);
+ eloop_event_add(fd->ctx->eloop, fd->fd,
+ NULL, NULL, control_writeone, fd);
+ return 0;
+}
+
void
control_close(struct dhcpcd_ctx *ctx)
{
#include "dhcpcd.h"
+/* Limit queue size per fd */
+#define CONTROL_QUEUE_MAX 100
+
+struct fd_data {
+ TAILQ_ENTRY(fd_data) next;
+ char *data;
+ size_t data_len;
+ uint8_t freeit;
+};
+TAILQ_HEAD(fd_data_head, fd_data);
+
struct fd_list {
- struct fd_list *next;
+ TAILQ_ENTRY(fd_list) next;
struct dhcpcd_ctx *ctx;
int fd;
int listener;
+ struct fd_data_head queue;
+ struct fd_data_head free_queue;
};
+TAILQ_HEAD(fd_list_head, fd_list);
int control_start(struct dhcpcd_ctx *, const char *);
int control_stop(struct dhcpcd_ctx *);
int control_open(struct dhcpcd_ctx *, const char *);
ssize_t control_send(struct dhcpcd_ctx *, int, char * const *);
+int control_queue(struct fd_list *fd, char *data, size_t data_len, uint8_t fit);
void control_close(struct dhcpcd_ctx *ctx);
#endif
}
#endif
-static void
-dhcpcd_version(void *arg)
-{
- struct fd_list *fd = arg;
- size_t len;
- struct iovec iov[2];
-
- len = strlen(VERSION) + 1;
- iov[0].iov_base = &len;
- iov[0].iov_len = sizeof(ssize_t);
- iov[1].iov_base = UNCONST(VERSION);
- iov[1].iov_len = len;
- if (writev(fd->fd, iov, 2) == -1)
- syslog(LOG_ERR, "%s: writev: %m", __func__);
- else
- eloop_event_delete(fd->ctx->eloop, fd->fd, 1);
-}
-
-static void
-dhcpcd_getconfigfile(void *arg)
-{
- struct fd_list *fd = arg;
- size_t len;
- struct iovec iov[2];
-
- len = strlen(fd->ctx->cffile) + 1;
- iov[0].iov_base = &len;
- iov[0].iov_len = sizeof(ssize_t);
- iov[1].iov_base = UNCONST(fd->ctx->cffile);
- iov[1].iov_len = len;
- if (writev(fd->fd, iov, 2) == -1)
- syslog(LOG_ERR, "%s: writev: %m", __func__);
- else
- eloop_event_delete(fd->ctx->eloop, fd->fd, 1);
-}
-
static void
dhcpcd_getinterfaces(void *arg)
{
}
if (write(fd->fd, &len, sizeof(len)) != sizeof(len))
return;
+ eloop_event_delete(fd->ctx->eloop, fd->fd, 1);
TAILQ_FOREACH(ifp, fd->ctx->ifaces, next) {
- if (send_interface(fd->fd, ifp) == -1)
+ if (send_interface(fd, ifp) == -1)
syslog(LOG_ERR, "send_interface %d: %m", fd->fd);
}
- eloop_event_delete(fd->ctx->eloop, fd->fd, 1);
}
int
* expected reply we should be safely able just to change the
* write callback on the fd */
if (strcmp(*argv, "--version") == 0) {
- eloop_event_add(fd->ctx->eloop, fd->fd, NULL, NULL,
- dhcpcd_version, fd);
- return 0;
+ return control_queue(fd, UNCONST(VERSION),
+ strlen(VERSION) + 1, 0);
} else if (strcmp(*argv, "--getconfigfile") == 0) {
- eloop_event_add(fd->ctx->eloop, fd->fd, NULL, NULL,
- dhcpcd_getconfigfile, fd);
- return 0;
+ return control_queue(fd, UNCONST(fd->ctx->cffile),
+ strlen(fd->ctx->cffile) + 1, 0);
} else if (strcmp(*argv, "--getinterfaces") == 0) {
eloop_event_add(fd->ctx->eloop, fd->fd, NULL, NULL,
dhcpcd_getinterfaces, fd);
for (opt = 0; opt < argc; opt++)
len += strlen(argv[opt]) + 1;
tmp = malloc(len);
- if (tmp == NULL) {
- syslog(LOG_ERR, "%s: %m", __func__);
+ if (tmp == NULL)
return -1;
- }
p = tmp;
for (opt = 0; opt < argc; opt++) {
l = strlen(argv[opt]);
ifo = NULL;
ctx.cffile = CONFIG;
ctx.pid_fd = ctx.control_fd = ctx.link_fd = -1;
+ TAILQ_INIT(&ctx.control_fds);
#ifdef PLUGIN_DEV
ctx.dev_fd = -1;
#endif
struct eloop_ctx *eloop;
int control_fd;
- struct fd_list *control_fds;
+ struct fd_list_head control_fds;
char control_sock[sizeof(CONTROLSOCKET) + IF_NAMESIZE];
gid_t control_group;
return -1;
}
-static ssize_t
-send_interface1(int fd, const struct interface *iface, const char *reason)
+static int
+send_interface1(struct fd_list *fd, const struct interface *iface,
+ const char *reason)
{
char **env, **ep, *s;
size_t elen;
- struct iovec iov[2];
- ssize_t retval;
+ int retval;
if (make_env(iface, reason, &env) == -1)
return -1;
elen = (size_t)arraytostr((const char *const *)env, &s);
if ((ssize_t)elen == -1)
return -1;
- iov[0].iov_base = &elen;
- iov[0].iov_len = sizeof(elen);
- iov[1].iov_base = s;
- iov[1].iov_len = elen;
- retval = writev(fd, iov, 2);
+ retval = control_queue(fd, s, elen, 1);
ep = env;
while (*ep)
free(*ep++);
free(env);
- free(s);
return retval;
}
int
-send_interface(int fd, const struct interface *ifp)
+send_interface(struct fd_list *fd, const struct interface *ifp)
{
const char *reason;
int retval = 0;
break;
}
if (send_interface1(fd, ifp, reason) == -1)
- retval = -1;
+ retval = -1;
#ifdef INET
if (D_STATE_RUNNING(ifp)) {
d = D_CSTATE(ifp);
size_t e, elen = 0;
pid_t pid;
int status = 0;
- const struct fd_list *fd;
- struct iovec iov[2];
+ struct fd_list *fd;
if (ifp->options->script &&
(ifp->options->script[0] == '\0' ||
/* Send to our listeners */
bigenv = NULL;
- for (fd = ifp->ctx->control_fds; fd != NULL; fd = fd->next) {
- if (fd->listener) {
- if (bigenv == NULL) {
- elen = (size_t)arraytostr((const char *const *)env,
- &bigenv);
- if ((ssize_t)elen == -1) {
- syslog(LOG_ERR, "%s: arraytostr: %m",
- ifp->name);
- continue;
- }
- iov[0].iov_base = &elen;
- iov[0].iov_len = sizeof(size_t);
- iov[1].iov_base = bigenv;
- iov[1].iov_len = elen;
+ TAILQ_FOREACH(fd, &ifp->ctx->control_fds, next) {
+ if (!fd->listener)
+ continue;
+ if (bigenv == NULL) {
+ elen = (size_t)arraytostr((const char *const *)env,
+ &bigenv);
+ if ((ssize_t)elen == -1) {
+ syslog(LOG_ERR, "%s: arraytostr: %m",
+ ifp->name);
+ break;
}
- if (writev(fd->fd, iov, 2) == -1)
- syslog(LOG_ERR, "%s: writev: %m", __func__);
}
+ if (control_queue(fd, bigenv, elen, 1) == -1)
+ syslog(LOG_ERR, "%s: control_queue: %m", __func__);
}
- free(bigenv);
out:
/* Cleanup */
#ifndef SCRIPT_H
#define SCRIPT_H
+#include "control.h"
+
void if_printoptions(void);
-int send_interface(int, const struct interface *);
+int send_interface(struct fd_list *, const struct interface *);
int script_runreason(const struct interface *, const char *);
#endif