chiark / gitweb /
sd-dhcp-server: add basic functionality for starting/stopping server
[elogind.git] / src / libsystemd-network / sd-dhcp-server.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright (C) 2013 Intel Corporation. All rights reserved.
7   Copyright (C) 2014 Tom Gundersen
8
9   systemd is free software; you can redistribute it and/or modify it
10   under the terms of the GNU Lesser General Public License as published by
11   the Free Software Foundation; either version 2.1 of the License, or
12   (at your option) any later version.
13
14   systemd is distributed in the hope that it will be useful, but
15   WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   Lesser General Public License for more details.
18
19   You should have received a copy of the GNU Lesser General Public License
20   along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 ***/
22
23 #include <sys/ioctl.h>
24
25 #include "sd-dhcp-server.h"
26 #include "dhcp-server-internal.h"
27 #include "dhcp-internal.h"
28
29 sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server) {
30         if (server)
31                 assert_se(REFCNT_INC(server->n_ref) >= 2);
32
33         return server;
34 }
35
36 sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) {
37         if (server && REFCNT_DEC(server->n_ref) <= 0) {
38                 log_dhcp_server(server, "UNREF");
39
40                 sd_dhcp_server_stop(server);
41
42                 sd_event_unref(server->event);
43                 free(server);
44         }
45
46         return NULL;
47 }
48
49 int sd_dhcp_server_new(sd_dhcp_server **ret) {
50         _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
51
52         assert_return(ret, -EINVAL);
53
54         server = new0(sd_dhcp_server, 1);
55         if (!server)
56                 return -ENOMEM;
57
58         server->n_ref = REFCNT_INIT;
59         server->fd = -1;
60
61         *ret = server;
62         server = NULL;
63
64         return 0;
65 }
66
67 int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event, int priority) {
68         int r;
69
70         assert_return(server, -EINVAL);
71         assert_return(!server->event, -EBUSY);
72
73         if (event)
74                 server->event = sd_event_ref(event);
75         else {
76                 r = sd_event_default(&server->event);
77                 if (r < 0)
78                         return r;
79         }
80
81         server->event_priority = priority;
82
83         return 0;
84 }
85
86 int sd_dhcp_server_detach_event(sd_dhcp_server *server) {
87         assert_return(server, -EINVAL);
88
89         server->event = sd_event_unref(server->event);
90
91         return 0;
92 }
93
94 sd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) {
95         assert_return(server, NULL);
96
97         return server->event;
98 }
99
100 int sd_dhcp_server_stop(sd_dhcp_server *server) {
101         assert_return(server, -EINVAL);
102
103         server->receive_message =
104                 sd_event_source_unref(server->receive_message);
105
106         server->fd = safe_close(server->fd);
107
108         log_dhcp_server(server, "STOPPED");
109
110         return 0;
111 }
112
113 static int server_receive_message(sd_event_source *s, int fd,
114                                   uint32_t revents, void *userdata) {
115         _cleanup_free_ uint8_t *message = NULL;
116         sd_dhcp_server *server = userdata;
117         struct iovec iov = {};
118         struct msghdr msg = {
119                 .msg_iov = &iov,
120                 .msg_iovlen = 1,
121         };
122         int buflen = 0, len, r;
123
124         assert(server);
125
126         r = ioctl(fd, FIONREAD, &buflen);
127         if (r < 0)
128                 return r;
129         if (buflen < 0)
130                 return -EIO;
131
132         message = malloc0(buflen);
133         if (!message)
134                 return -ENOMEM;
135
136         iov.iov_base = message;
137         iov.iov_len = buflen;
138
139         len = recvmsg(fd, &msg, 0);
140         if (len < buflen)
141                 return 0;
142
143         log_dhcp_server(server, "received message");
144
145         return 1;
146 }
147
148 int sd_dhcp_server_start(sd_dhcp_server *server) {
149         int r;
150
151         assert_return(server, -EINVAL);
152         assert_return(server->event, -EINVAL);
153         assert_return(!server->receive_message, -EBUSY);
154         assert_return(server->fd == -1, -EBUSY);
155
156         r = dhcp_network_bind_udp_socket(INADDR_ANY, DHCP_PORT_SERVER);
157         if (r < 0) {
158                 sd_dhcp_server_stop(server);
159                 return r;
160         }
161         server->fd = r;
162
163         r = sd_event_add_io(server->event, &server->receive_message,
164                             server->fd, EPOLLIN,
165                             server_receive_message, server);
166         if (r < 0) {
167                 sd_dhcp_server_stop(server);
168                 return r;
169         }
170
171         r = sd_event_source_set_priority(server->receive_message,
172                                          server->event_priority);
173         if (r < 0) {
174                 sd_dhcp_server_stop(server);
175                 return r;
176         }
177
178         log_dhcp_server(server, "STARTED");
179
180         return 0;
181 }