]> git.proxmox.com Git - mirror_frr.git/blob - ldpd/control.c
ldpd: provide both a brief and a detailed version of some show commands
[mirror_frr.git] / ldpd / control.c
1 /* $OpenBSD$ */
2
3 /*
4 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <zebra.h>
20 #include <sys/un.h>
21
22 #include "ldpd.h"
23 #include "ldpe.h"
24 #include "log.h"
25 #include "control.h"
26
27 #define CONTROL_BACKLOG 5
28
29 static int control_accept(struct thread *);
30 static struct ctl_conn *control_connbyfd(int);
31 static struct ctl_conn *control_connbypid(pid_t);
32 static void control_close(int);
33 static int control_dispatch_imsg(struct thread *);
34
35 struct ctl_conns ctl_conns;
36
37 static int control_fd;
38
39 int
40 control_init(void)
41 {
42 struct sockaddr_un s_un;
43 int fd;
44 mode_t old_umask;
45
46 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
47 log_warn("%s: socket", __func__);
48 return (-1);
49 }
50 sock_set_nonblock(fd);
51
52 memset(&s_un, 0, sizeof(s_un));
53 s_un.sun_family = AF_UNIX;
54 strlcpy(s_un.sun_path, ctl_sock_path, sizeof(s_un.sun_path));
55
56 if (unlink(ctl_sock_path) == -1)
57 if (errno != ENOENT) {
58 log_warn("%s: unlink %s", __func__, ctl_sock_path);
59 close(fd);
60 return (-1);
61 }
62
63 old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
64 if (bind(fd, (struct sockaddr *)&s_un, sizeof(s_un)) == -1) {
65 log_warn("%s: bind: %s", __func__, ctl_sock_path);
66 close(fd);
67 umask(old_umask);
68 return (-1);
69 }
70 umask(old_umask);
71
72 if (chmod(ctl_sock_path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) {
73 log_warn("%s: chmod", __func__);
74 close(fd);
75 (void)unlink(ctl_sock_path);
76 return (-1);
77 }
78
79 control_fd = fd;
80
81 return (0);
82 }
83
84 int
85 control_listen(void)
86 {
87 if (listen(control_fd, CONTROL_BACKLOG) == -1) {
88 log_warn("%s: listen", __func__);
89 return (-1);
90 }
91
92 return (accept_add(control_fd, control_accept, NULL));
93 }
94
95 void
96 control_cleanup(void)
97 {
98 accept_del(control_fd);
99 close(control_fd);
100 unlink(ctl_sock_path);
101 }
102
103 /* ARGSUSED */
104 static int
105 control_accept(struct thread *thread)
106 {
107 int connfd;
108 socklen_t len;
109 struct sockaddr_un s_un;
110 struct ctl_conn *c;
111
112 len = sizeof(s_un);
113 if ((connfd = accept(THREAD_FD(thread), (struct sockaddr *)&s_un,
114 &len)) == -1) {
115 /*
116 * Pause accept if we are out of file descriptors, or
117 * libevent will haunt us here too.
118 */
119 if (errno == ENFILE || errno == EMFILE)
120 accept_pause();
121 else if (errno != EWOULDBLOCK && errno != EINTR &&
122 errno != ECONNABORTED)
123 log_warn("%s: accept", __func__);
124 return (0);
125 }
126 sock_set_nonblock(connfd);
127
128 if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) {
129 log_warn(__func__);
130 close(connfd);
131 return (0);
132 }
133
134 imsg_init(&c->iev.ibuf, connfd);
135 c->iev.handler_read = control_dispatch_imsg;
136 c->iev.ev_read = thread_add_read(master, c->iev.handler_read,
137 &c->iev, c->iev.ibuf.fd);
138 c->iev.handler_write = ldp_write_handler;
139 c->iev.ev_write = NULL;
140
141 TAILQ_INSERT_TAIL(&ctl_conns, c, entry);
142
143 return (0);
144 }
145
146 static struct ctl_conn *
147 control_connbyfd(int fd)
148 {
149 struct ctl_conn *c;
150
151 TAILQ_FOREACH(c, &ctl_conns, entry) {
152 if (c->iev.ibuf.fd == fd)
153 break;
154 }
155
156 return (c);
157 }
158
159 static struct ctl_conn *
160 control_connbypid(pid_t pid)
161 {
162 struct ctl_conn *c;
163
164 TAILQ_FOREACH(c, &ctl_conns, entry) {
165 if (c->iev.ibuf.pid == pid)
166 break;
167 }
168
169 return (c);
170 }
171
172 static void
173 control_close(int fd)
174 {
175 struct ctl_conn *c;
176
177 if ((c = control_connbyfd(fd)) == NULL) {
178 log_warnx("%s: fd %d: not found", __func__, fd);
179 return;
180 }
181
182 msgbuf_clear(&c->iev.ibuf.w);
183 TAILQ_REMOVE(&ctl_conns, c, entry);
184
185 THREAD_READ_OFF(c->iev.ev_read);
186 THREAD_WRITE_OFF(c->iev.ev_write);
187 close(c->iev.ibuf.fd);
188 accept_unpause();
189 free(c);
190 }
191
192 /* ARGSUSED */
193 static int
194 control_dispatch_imsg(struct thread *thread)
195 {
196 int fd = THREAD_FD(thread);
197 struct ctl_conn *c;
198 struct imsg imsg;
199 ssize_t n;
200 unsigned int ifidx;
201
202 if ((c = control_connbyfd(fd)) == NULL) {
203 log_warnx("%s: fd %d: not found", __func__, fd);
204 return (0);
205 }
206
207 c->iev.ev_read = NULL;
208
209 if (((n = imsg_read(&c->iev.ibuf)) == -1 && errno != EAGAIN) ||
210 n == 0) {
211 control_close(fd);
212 return (0);
213 }
214
215 for (;;) {
216 if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) {
217 control_close(fd);
218 return (0);
219 }
220
221 if (n == 0)
222 break;
223
224 switch (imsg.hdr.type) {
225 case IMSG_CTL_FIB_COUPLE:
226 case IMSG_CTL_FIB_DECOUPLE:
227 case IMSG_CTL_RELOAD:
228 case IMSG_CTL_KROUTE:
229 case IMSG_CTL_KROUTE_ADDR:
230 case IMSG_CTL_IFINFO:
231 /* ignore */
232 break;
233 case IMSG_CTL_SHOW_INTERFACE:
234 if (imsg.hdr.len == IMSG_HEADER_SIZE +
235 sizeof(ifidx)) {
236 memcpy(&ifidx, imsg.data, sizeof(ifidx));
237 ldpe_iface_ctl(c, ifidx);
238 imsg_compose_event(&c->iev, IMSG_CTL_END, 0,
239 0, -1, NULL, 0);
240 }
241 break;
242 case IMSG_CTL_SHOW_DISCOVERY:
243 ldpe_adj_ctl(c);
244 break;
245 case IMSG_CTL_SHOW_DISCOVERY_DTL:
246 ldpe_adj_detail_ctl(c);
247 break;
248 case IMSG_CTL_SHOW_LIB:
249 case IMSG_CTL_SHOW_L2VPN_PW:
250 case IMSG_CTL_SHOW_L2VPN_BINDING:
251 c->iev.ibuf.pid = imsg.hdr.pid;
252 ldpe_imsg_compose_lde(imsg.hdr.type, 0, imsg.hdr.pid,
253 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
254 break;
255 case IMSG_CTL_SHOW_NBR:
256 ldpe_nbr_ctl(c);
257 break;
258 case IMSG_CTL_CLEAR_NBR:
259 if (imsg.hdr.len != IMSG_HEADER_SIZE +
260 sizeof(struct ctl_nbr))
261 break;
262
263 nbr_clear_ctl(imsg.data);
264 break;
265 case IMSG_CTL_LOG_VERBOSE:
266 /* ignore */
267 break;
268 default:
269 log_debug("%s: error handling imsg %d", __func__,
270 imsg.hdr.type);
271 break;
272 }
273 imsg_free(&imsg);
274 }
275
276 imsg_event_add(&c->iev);
277
278 return (0);
279 }
280
281 int
282 control_imsg_relay(struct imsg *imsg)
283 {
284 struct ctl_conn *c;
285
286 if ((c = control_connbypid(imsg->hdr.pid)) == NULL)
287 return (0);
288
289 return (imsg_compose_event(&c->iev, imsg->hdr.type, 0, imsg->hdr.pid,
290 -1, imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE));
291 }