xref: /freebsd/contrib/wpa/src/common/proc_coord.c (revision 71e72c9e91c4b8007a4292e09669e8b549c29e97)
1 /*
2  * Coordination of operations between processes
3  * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
4  *
5  * This software may be distributed under the terms of the BSD license.
6  * See README for more details.
7  */
8 
9 #include "utils/includes.h"
10 #include <sys/un.h>
11 #include <fcntl.h>
12 #include <dirent.h>
13 
14 #include "utils/common.h"
15 #include "utils/eloop.h"
16 #include "utils/list.h"
17 #include "proc_coord.h"
18 
19 
20 struct proc_coord_msg_header {
21 	u32 msg_type; /* enum proc_coord_message_types */
22 	u32 cmd; /* proc_coord_commands */
23 	u32 seq;
24 };
25 
26 enum proc_coord_peer_state {
27 	PROC_COORD_PEER_WAITING,
28 	PROC_COORD_PEER_ACTIVE,
29 	PROC_COORD_PEER_TIMED_OUT,
30 };
31 
32 struct proc_coord_peer {
33 	struct dl_list list;
34 	pid_t pid;
35 	enum proc_coord_peer_state state;
36 	struct os_reltime last_rx;
37 };
38 
39 struct proc_coord_pending_request {
40 	struct dl_list list;
41 	struct proc_coord_peer *peer;
42 	enum proc_coord_commands cmd;
43 	u32 seq;
44 	proc_coord_response_cb cb;
45 	void *cb_ctx;
46 	struct os_reltime timeout;
47 };
48 
49 struct proc_coord_handler {
50 	struct dl_list list;
51 	proc_coord_cb cb;
52 	void *cb_ctx;
53 };
54 
55 struct proc_coord {
56 	pid_t pid;
57 	char *dir;
58 	char *own_sock;
59 	int sock;
60 	u32 next_seq;
61 	struct dl_list handlers; /* struct proc_coord_handler::list */
62 	struct dl_list peers; /* struct proc_coord_peer::list */
63 	struct dl_list requests; /* struct proc_coord_pending_request::list */
64 };
65 
66 
67 static void proc_coord_set_req_expire_timer(struct proc_coord *pc);
68 
69 
proc_coord_get_peer(struct proc_coord * pc,pid_t pid)70 static struct proc_coord_peer * proc_coord_get_peer(struct proc_coord *pc,
71 						    pid_t pid)
72 {
73 	struct proc_coord_peer *peer;
74 
75 	dl_list_for_each(peer, &pc->peers, struct proc_coord_peer, list) {
76 		if (peer->pid == pid)
77 			return peer;
78 	}
79 
80 	return NULL;
81 }
82 
83 
proc_coord_add_peer(struct proc_coord * pc,pid_t pid)84 static struct proc_coord_peer * proc_coord_add_peer(struct proc_coord *pc,
85 						    pid_t pid)
86 {
87 	struct proc_coord_peer *peer;
88 
89 	peer = os_zalloc(sizeof(*peer));
90 	if (!peer)
91 		return NULL;
92 
93 	peer->pid = pid;
94 	peer->state = PROC_COORD_PEER_WAITING;
95 	dl_list_add(&pc->peers, &peer->list);
96 
97 	return peer;
98 }
99 
100 
proc_coord_remove_req(struct proc_coord_pending_request * req)101 static void proc_coord_remove_req(struct proc_coord_pending_request *req)
102 {
103 	dl_list_del(&req->list);
104 	if (req->cb)
105 		req->cb(req->cb_ctx, req->peer->pid, NULL);
106 	os_free(req);
107 }
108 
109 
proc_coord_remove_peer(struct proc_coord * pc,struct proc_coord_peer * peer)110 static void proc_coord_remove_peer(struct proc_coord *pc,
111 				   struct proc_coord_peer *peer)
112 {
113 	struct proc_coord_pending_request *req, *tmp;
114 
115 	dl_list_for_each_safe(req, tmp, &pc->requests,
116 			      struct proc_coord_pending_request, list) {
117 		if (req->peer == peer)
118 			proc_coord_remove_req(req);
119 	}
120 
121 	dl_list_del(&peer->list);
122 	os_free(peer);
123 }
124 
125 
proc_coord_expire_requests(void * eloop_ctx,void * timeout_ctx)126 static void proc_coord_expire_requests(void *eloop_ctx, void *timeout_ctx)
127 {
128 	struct proc_coord *pc = eloop_ctx;
129 	struct proc_coord_pending_request *req, *tmp;
130 	struct os_reltime now;
131 
132 	os_get_reltime(&now);
133 	dl_list_for_each_safe(req, tmp, &pc->requests,
134 			      struct proc_coord_pending_request, list) {
135 		if (os_reltime_before(&req->timeout, &now)) {
136 			wpa_printf(MSG_DEBUG,
137 				   "proc_coord: Pending request peer=%u cmd=%d seq=%u timed out",
138 				   req->peer->pid, req->cmd, req->seq);
139 			proc_coord_remove_req(req);
140 		}
141 	}
142 
143 	proc_coord_set_req_expire_timer(pc);
144 }
145 
146 
proc_coord_cancel_wait(struct proc_coord * pc,proc_coord_response_cb cb,void * cb_ctx)147 void proc_coord_cancel_wait(struct proc_coord *pc, proc_coord_response_cb cb,
148 			    void *cb_ctx)
149 {
150 	struct proc_coord_pending_request *req, *tmp;
151 
152 	dl_list_for_each_safe(req, tmp, &pc->requests,
153 			      struct proc_coord_pending_request, list) {
154 		if (req->cb == cb && req->cb_ctx == cb_ctx) {
155 			req->cb = NULL;
156 			req->cb_ctx = NULL;
157 			proc_coord_remove_req(req);
158 		}
159 	}
160 
161 	proc_coord_set_req_expire_timer(pc);
162 }
163 
164 
proc_coord_set_req_expire_timer(struct proc_coord * pc)165 static void proc_coord_set_req_expire_timer(struct proc_coord *pc)
166 {
167 	struct proc_coord_pending_request *req;
168 	struct os_reltime *first = NULL;
169 
170 	eloop_cancel_timeout(proc_coord_expire_requests, pc, NULL);
171 	dl_list_for_each(req, &pc->requests, struct proc_coord_pending_request,
172 			 list) {
173 		if (!first || os_reltime_before(&req->timeout, first))
174 			first = &req->timeout;
175 	}
176 
177 	if (first) {
178 		struct os_reltime now, res;
179 		unsigned int ms;
180 
181 		os_get_reltime(&now);
182 		if (os_reltime_before(first, &now)) {
183 			ms = 0;
184 		} else {
185 			os_reltime_sub(first, &now, &res);
186 			ms = os_reltime_in_ms(&res);
187 		}
188 		eloop_register_timeout(ms / 1000, (ms % 1000) * 1000,
189 				       proc_coord_expire_requests, pc, NULL);
190 	}
191 }
192 
193 
194 static struct proc_coord_pending_request *
proc_coord_get_request(struct proc_coord * pc,struct proc_coord_peer * peer,enum proc_coord_commands cmd,u32 seq)195 proc_coord_get_request(struct proc_coord *pc, struct proc_coord_peer *peer,
196 		       enum proc_coord_commands cmd, u32 seq)
197 {
198 	struct proc_coord_pending_request *req;
199 
200 	dl_list_for_each(req, &pc->requests, struct proc_coord_pending_request,
201 			 list) {
202 		if (req->peer == peer && req->cmd == cmd && req->seq == seq)
203 			return req;
204 	}
205 
206 	return NULL;
207 }
208 
209 
proc_coord_send_msg(struct proc_coord * pc,struct proc_coord_peer * peer,enum proc_coord_message_types msg_type,enum proc_coord_commands cmd,u32 seq,const struct wpabuf * msg)210 static int proc_coord_send_msg(struct proc_coord *pc,
211 			       struct proc_coord_peer *peer,
212 			       enum proc_coord_message_types msg_type,
213 			       enum proc_coord_commands cmd,
214 			       u32 seq, const struct wpabuf *msg)
215 {
216 	struct proc_coord_msg_header hdr;
217 	ssize_t res;
218 	struct sockaddr_un addr;
219 	struct msghdr mh;
220 	struct iovec io[2];
221 
222 	wpa_printf(MSG_DEBUG,
223 		   "proc_coord: Send message to %d (msg_type=%u cmd=%u seq=%u)",
224 		   peer->pid, msg_type, cmd, seq);
225 
226 	os_memset(&hdr, 0, sizeof(hdr));
227 	hdr.msg_type = msg_type;
228 	hdr.cmd = cmd;
229 	hdr.seq = seq;
230 
231 	os_memset(&addr, 0, sizeof(addr));
232 	addr.sun_family = AF_UNIX;
233 	os_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%u",
234 		    pc->dir, peer->pid);
235 
236 	io[0].iov_base = &hdr;
237 	io[0].iov_len = sizeof(hdr);
238 	if (msg) {
239 		io[1].iov_base = (void *) wpabuf_head(msg);
240 		io[1].iov_len = wpabuf_len(msg);
241 	}
242 
243 	os_memset(&mh, 0, sizeof(mh));
244 	mh.msg_iov = io;
245 	mh.msg_iovlen = msg ? 2 : 1;
246 	mh.msg_name = (void *) &addr;
247 	mh.msg_namelen = sizeof(addr);
248 
249 	res = sendmsg(pc->sock, &mh, MSG_DONTWAIT);
250 	if (res < 0) {
251 		int err = errno;
252 
253 		wpa_printf(MSG_INFO, "proc_coord: sendmsg: %s",
254 			   strerror(errno));
255 		if (err == ENOENT || err == ECONNREFUSED) {
256 			wpa_printf(MSG_INFO,
257 				   "proc_coord: Remove peer %u due to connection being refused",
258 				   peer->pid);
259 			proc_coord_remove_peer(pc, peer);
260 		}
261 		return -1;
262 	}
263 	return 0;
264 }
265 
266 
proc_coord_rx_starting(struct proc_coord * pc,pid_t pid)267 static void proc_coord_rx_starting(struct proc_coord *pc, pid_t pid)
268 {
269 	wpa_printf(MSG_DEBUG, "proc_coord: Peer %u STARTING", pid);
270 }
271 
272 
proc_coord_rx_stopping(struct proc_coord * pc,pid_t pid)273 static void proc_coord_rx_stopping(struct proc_coord *pc, pid_t pid)
274 {
275 	struct proc_coord_peer *peer;
276 
277 	wpa_printf(MSG_DEBUG, "proc_coord: Peer %u STOPPING", pid);
278 
279 	peer = proc_coord_get_peer(pc, pid);
280 	if (peer) {
281 		wpa_printf(MSG_INFO,
282 			   "proc_coord: Remove peer %u due to STOPPING event",
283 			   pid);
284 		proc_coord_remove_peer(pc, peer);
285 	}
286 }
287 
288 
proc_coord_rx_ping(struct proc_coord * pc,pid_t pid,enum proc_coord_message_types msg_type,u32 seq)289 static void proc_coord_rx_ping(struct proc_coord *pc, pid_t pid,
290 			       enum proc_coord_message_types msg_type, u32 seq)
291 {
292 	if (msg_type != PROC_COORD_MSG_REQUEST)
293 		return;
294 
295 	wpa_printf(MSG_DEBUG, "proc_coord: Reply to peer %u PING", pid);
296 	proc_coord_send_response(pc, pid, PROC_COORD_CMD_PING, seq, NULL);
297 }
298 
299 
proc_coord_receive(int sock,void * eloop_ctx,void * sock_ctx)300 static void proc_coord_receive(int sock, void *eloop_ctx, void *sock_ctx)
301 {
302 	struct proc_coord *pc = eloop_ctx;
303 	struct sockaddr_un from;
304 	struct wpabuf *msg;
305 	size_t msg_len;
306 	ssize_t res;
307 	struct proc_coord_msg_header hdr;
308 	pid_t pid;
309 	char tmp[20], *pos, *end;
310 	struct proc_coord_peer *peer;
311 	struct msghdr mh;
312 	struct iovec io[2];
313 	struct proc_coord_handler *handler, *h_tmp;
314 
315 	res = recv(sock, tmp, 0, MSG_PEEK | MSG_TRUNC);
316 	if (res < 0) {
317 		wpa_printf(MSG_ERROR, "proc_coord: recv: %s",
318 			   strerror(errno));
319 		return;
320 	}
321 	msg_len = res;
322 	if (msg_len < sizeof(hdr))
323 		return;
324 	msg_len -= sizeof(hdr);
325 
326 	msg = wpabuf_alloc(msg_len);
327 	if (!msg)
328 		return;
329 
330 	io[0].iov_base = &hdr;
331 	io[0].iov_len = sizeof(hdr);
332 	io[1].iov_base = wpabuf_mhead(msg);
333 	io[1].iov_len = msg_len;
334 
335 	os_memset(&mh, 0, sizeof(mh));
336 	mh.msg_iov = io;
337 	mh.msg_iovlen = 2;
338 	mh.msg_name = (void *) &from;
339 	mh.msg_namelen = sizeof(from);
340 
341 	res = recvmsg(sock, &mh, MSG_DONTWAIT);
342 	if (res < 0) {
343 		wpa_printf(MSG_ERROR, "proc_coord: recvmsg: %s",
344 			   strerror(errno));
345 		goto out;
346 	}
347 	if ((size_t) res < sizeof(hdr))
348 		goto out;
349 
350 	wpa_printf(MSG_DEBUG, "proc_coord: Received message from %s",
351 		   from.sun_path);
352 	if ((size_t) res > sizeof(hdr)) {
353 		wpabuf_put(msg, res - sizeof(hdr));
354 		wpa_hexdump_buf(MSG_MSGDUMP,
355 				"proc_coord: Received message payload", msg);
356 	}
357 
358 	end = ((char *) &from) + mh.msg_namelen;
359 
360 	/* Require same directory for client socket */
361 	if ((size_t) (end - from.sun_path) < os_strlen(pc->dir) ||
362 	    os_strncmp(pc->dir, from.sun_path, os_strlen(pc->dir)) != 0)
363 		goto out;
364 
365 	/* Find the peer PID from the socket name */
366 	pos = end - 1;
367 	while (pos > from.sun_path) {
368 		if (*pos == '/')
369 			break;
370 		pos--;
371 	}
372 	if (pos == from.sun_path)
373 		goto out;
374 	pos++;
375 	os_memcpy(tmp, pos, end - pos);
376 	pid = atoi(tmp);
377 
378 	wpa_printf(MSG_DEBUG, "proc_coord: pid=%u msg_type=%u cmd=%u seq=%u",
379 		   pid, hdr.msg_type, hdr.cmd, hdr.seq);
380 
381 	peer = proc_coord_get_peer(pc, pid);
382 
383 	switch (hdr.msg_type) {
384 	case PROC_COORD_MSG_REQUEST:
385 		break;
386 	case PROC_COORD_MSG_RESPONSE: {
387 		struct proc_coord_pending_request *req;
388 
389 		if (!peer) {
390 			wpa_printf(MSG_DEBUG,
391 				   "proc_coord: Discard msg_type=RESPONSE from %u since there is no peer entry for it",
392 				   pid);
393 			goto out;
394 		}
395 
396 		req = proc_coord_get_request(pc, peer, hdr.cmd, hdr.seq);
397 		if (!req) {
398 			wpa_printf(MSG_DEBUG,
399 				   "proc_coord: Discard msg_type=RESPONSE from %u since there is no pending request for it",
400 				   pid);
401 			goto out;
402 		}
403 		if (req->cb)
404 			req->cb(req->cb_ctx, peer->pid, msg);
405 		dl_list_del(&req->list);
406 		os_free(req);
407 		break;
408 	}
409 	case PROC_COORD_MSG_EVENT:
410 		break;
411 	default:
412 		wpa_printf(MSG_DEBUG,
413 			   "proc_coord: Discard unknown msg_type=%u from %u",
414 			   hdr.msg_type, pid);
415 		goto out;
416 	}
417 
418 	if (!peer) {
419 		wpa_printf(MSG_DEBUG,
420 			   "proc_coord: Add new peer entry for %d based on received message",
421 			   pid);
422 		peer = proc_coord_add_peer(pc, pid);
423 		if (!peer) {
424 			wpa_printf(MSG_ERROR,
425 				   "proc_coord: Could not add peer entry for %u",
426 				   pid);
427 			goto out;
428 		}
429 	}
430 
431 	if (peer->state != PROC_COORD_PEER_ACTIVE) {
432 		wpa_printf(MSG_DEBUG,
433 			   "proc_coord: Mark peer %d active due to received message",
434 			   pid);
435 		peer->state = PROC_COORD_PEER_ACTIVE;
436 	}
437 
438 	os_get_reltime(&peer->last_rx);
439 
440 	switch (hdr.cmd) {
441 	case PROC_COORD_CMD_STARTING :
442 		proc_coord_rx_starting(pc, pid);
443 		break;
444 	case PROC_COORD_CMD_STOPPING:
445 		proc_coord_rx_stopping(pc, pid);
446 		break;
447 	case PROC_COORD_CMD_PING:
448 		proc_coord_rx_ping(pc, pid, hdr.msg_type, hdr.seq);
449 		break;
450 	default:
451 		if (hdr.msg_type == PROC_COORD_MSG_RESPONSE)
452 			break;
453 
454 		dl_list_for_each_safe(handler, h_tmp, &pc->handlers,
455 				 struct proc_coord_handler, list) {
456 			if (handler->cb(handler->cb_ctx, pid, hdr.msg_type,
457 					hdr.cmd, hdr.seq, msg))
458 				break;
459 		}
460 		break;
461 	}
462 
463 out:
464 	wpabuf_free(msg);
465 }
466 
467 
proc_coord_send_starting(struct proc_coord * pc)468 static void proc_coord_send_starting(struct proc_coord *pc)
469 {
470 	struct proc_coord_peer *peer, *tmp;
471 	int count = 0;
472 
473 	if (dl_list_empty(&pc->peers))
474 		return;
475 
476 	wpa_printf(MSG_DEBUG, "proc_coord: Send STARTING event to all peers");
477 	dl_list_for_each_safe(peer, tmp, &pc->peers, struct proc_coord_peer,
478 			      list) {
479 		if (proc_coord_send_msg(pc, peer, PROC_COORD_MSG_EVENT,
480 					PROC_COORD_CMD_STARTING, 0, NULL) == 0)
481 			count++;
482 	}
483 	wpa_printf(MSG_DEBUG, "proc_coord: STARTING sent to %d peer(s)", count);
484 }
485 
486 
proc_coord_send_stopping(struct proc_coord * pc)487 static void proc_coord_send_stopping(struct proc_coord *pc)
488 {
489 	if (dl_list_empty(&pc->peers))
490 		return;
491 
492 	wpa_printf(MSG_DEBUG,
493 		   "proc_coord: Send STOPPING event to all active peers");
494 	proc_coord_send_event(pc, 0, PROC_COORD_CMD_STOPPING, NULL);
495 }
496 
497 
proc_coord_cb_ping(void * ctx,int pid,const struct wpabuf * msg)498 static void proc_coord_cb_ping(void *ctx, int pid, const struct wpabuf *msg)
499 {
500 	struct proc_coord *pc = ctx;
501 	struct proc_coord_peer *peer;
502 
503 	peer = proc_coord_get_peer(pc, pid);
504 	if (!peer)
505 		return;
506 	if (msg) {
507 		if (peer->state != PROC_COORD_PEER_ACTIVE) {
508 			wpa_printf(MSG_DEBUG,
509 				   "proc_coord: Mark peer %d active due to response to PING",
510 				   pid);
511 			peer->state = PROC_COORD_PEER_ACTIVE;
512 		}
513 	} else {
514 		if (peer->state != PROC_COORD_PEER_TIMED_OUT) {
515 			wpa_printf(MSG_DEBUG,
516 				   "proc_coord: Mark peer %d timed out due to no response to PING",
517 				   pid);
518 			peer->state = PROC_COORD_PEER_TIMED_OUT;
519 		}
520 	}
521 }
522 
523 
proc_coord_update_peers_from_dir(struct proc_coord * pc)524 static void proc_coord_update_peers_from_dir(struct proc_coord *pc)
525 {
526 	DIR *dir;
527 	struct dirent *de;
528 	struct proc_coord_peer *peer, *tmp;
529 
530 	/* Remove peers that do not have a socket file */
531 	dl_list_for_each_safe(peer, tmp, &pc->peers, struct proc_coord_peer,
532 			      list) {
533 		char fname[256];
534 
535 		os_snprintf(fname, sizeof(fname), "%s/%d", pc->dir, peer->pid);
536 		if (!os_file_exists(fname)) {
537 			wpa_printf(MSG_DEBUG,
538 				   "proc_coord: Remove peer %d due to socket file not present",
539 				   peer->pid);
540 			proc_coord_remove_peer(pc, peer);
541 		}
542 	}
543 
544 	/* Add peer entries for all new sockets in the directory */
545 	dir = opendir(pc->dir);
546 	if (!dir)  {
547 		wpa_printf(MSG_ERROR, "proc_coord: opendir: %s",
548 			   strerror(errno));
549 		return;
550 	}
551 
552 	while ((de = readdir(dir))) {
553 		int pid = atoi(de->d_name);
554 
555 		if (pid <= 0 || pid == pc->pid)
556 			continue;
557 
558 		peer = proc_coord_get_peer(pc, pid);
559 		if (peer)
560 			continue;
561 
562 		wpa_printf(MSG_DEBUG, "proc_coord: Add new peer entry for %u",
563 			   pid);
564 		peer = proc_coord_add_peer(pc, pid);
565 		if (!peer) {
566 			wpa_printf(MSG_ERROR,
567 				   "proc_coord: Could not add peer entry for %u",
568 				   pid);
569 			continue;
570 		}
571 
572 		wpa_printf(MSG_DEBUG, "proc_coord: Ping new peer %u", pid);
573 		proc_coord_send_request(pc, pid, PROC_COORD_CMD_PING, NULL,
574 					10000, proc_coord_cb_ping, pc);
575 	}
576 
577 	closedir(dir);
578 }
579 
580 
proc_coord_update_peers(void * eloop_ctx,void * timeout_ctx)581 static void proc_coord_update_peers(void *eloop_ctx, void *timeout_ctx)
582 {
583 	struct proc_coord *pc = eloop_ctx;
584 	struct proc_coord_peer *peer, *tmp;
585 	struct os_reltime now;
586 
587 	proc_coord_update_peers_from_dir(pc);
588 
589 	os_get_reltime(&now);
590 	dl_list_for_each_safe(peer, tmp, &pc->peers, struct proc_coord_peer,
591 			      list) {
592 		if (!os_reltime_expired(&now, &peer->last_rx, 60))
593 			continue;
594 		proc_coord_send_request(pc, peer->pid, PROC_COORD_CMD_PING,
595 					NULL, 10000, NULL, NULL);
596 	}
597 
598 	eloop_register_timeout(10, 0, proc_coord_update_peers, pc, NULL);
599 }
600 
601 
proc_coord_init(const char * dir)602 struct proc_coord * proc_coord_init(const char *dir)
603 {
604 	struct proc_coord *pc;
605 	struct sockaddr_un addr;
606 	size_t len;
607 	int flags;
608 
609 	pc = os_zalloc(sizeof(*pc));
610 	if (!pc)
611 		return NULL;
612 
613 	dl_list_init(&pc->peers);
614 	dl_list_init(&pc->requests);
615 	dl_list_init(&pc->handlers);
616 	pc->sock = -1;
617 
618 	pc->dir = os_strdup(dir);
619 	if (!pc->dir)
620 		goto fail;
621 
622 	len = os_strlen(dir) + 20;
623 	pc->own_sock = os_zalloc(len);
624 	if (!pc->own_sock)
625 		goto fail;
626 	pc->pid = getpid();
627 	os_snprintf(pc->own_sock, len, "%s/%d", dir, pc->pid);
628 	wpa_printf(MSG_DEBUG, "proc_coord: Own socket at %s", pc->own_sock);
629 	unlink(pc->own_sock);
630 
631 	pc->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
632 	if (pc->sock < 0) {
633 		wpa_printf(MSG_ERROR, "proc_coord: socket(PF_UNIX): %s",
634 			   strerror(errno));
635 		goto fail;
636 	}
637 
638 	os_memset(&addr, 0, sizeof(addr));
639 	addr.sun_family = AF_UNIX;
640 	os_strlcpy(addr.sun_path, pc->own_sock, sizeof(addr.sun_path));
641 	if (bind(pc->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
642 		wpa_printf(MSG_ERROR, "proc_coord: bind(PF_UNIX) failed: %s",
643 			   strerror(errno));
644 		goto fail;
645 	}
646 
647 	flags = fcntl(pc->sock, F_GETFL);
648 	if (flags >= 0) {
649 		flags |= O_NONBLOCK;
650 		if (fcntl(pc->sock, F_SETFL, flags) < 0) {
651 			wpa_printf(MSG_INFO,
652 				   "proc_coord: fcntl(O_NONBLOCK): %s",
653 				   strerror(errno));
654 			/* Not fatal, continue on.*/
655 		}
656 	}
657 
658 	eloop_register_read_sock(pc->sock, proc_coord_receive, pc, NULL);
659 
660 	proc_coord_update_peers_from_dir(pc);
661 
662 	/* Start periodic updates quickly to recover from potential race
663 	 * conditions if the processes are started at the same time. */
664 	eloop_register_timeout(1, 0, proc_coord_update_peers, pc, NULL);
665 
666 	proc_coord_send_starting(pc);
667 
668 	return pc;
669 
670 fail:
671 	proc_coord_deinit(pc);
672 	return NULL;
673 }
674 
675 
proc_coord_deinit(struct proc_coord * pc)676 void proc_coord_deinit(struct proc_coord *pc)
677 {
678 	struct proc_coord_peer *peer, *tmp;
679 	struct proc_coord_pending_request *req, *tmp2;
680 
681 	if (!pc)
682 		return;
683 
684 	eloop_cancel_timeout(proc_coord_update_peers, pc, NULL);
685 	eloop_cancel_timeout(proc_coord_expire_requests, pc, NULL);
686 
687 	if (pc->sock >= 0) {
688 		proc_coord_send_stopping(pc);
689 		eloop_unregister_read_sock(pc->sock);
690 		close(pc->sock);
691 		unlink(pc->own_sock);
692 	}
693 
694 	dl_list_for_each_safe(peer, tmp, &pc->peers, struct proc_coord_peer,
695 			      list)
696 		proc_coord_remove_peer(pc, peer);
697 
698 	dl_list_for_each_safe(req, tmp2, &pc->requests,
699 			      struct proc_coord_pending_request, list) {
700 		dl_list_del(&req->list);
701 		os_free(req);
702 	}
703 
704 	os_free(pc->dir);
705 	os_free(pc->own_sock);
706 	os_free(pc);
707 }
708 
709 
proc_coord_register_handler(struct proc_coord * pc,proc_coord_cb cb,void * cb_ctx)710 int proc_coord_register_handler(struct proc_coord *pc, proc_coord_cb cb,
711 				void *cb_ctx)
712 {
713 	struct proc_coord_handler *handler;
714 
715 	handler = os_zalloc(sizeof(*handler));
716 	if (!handler)
717 		return -1;
718 
719 	handler->cb = cb;
720 	handler->cb_ctx = cb_ctx;
721 	dl_list_add(&pc->handlers, &handler->list);
722 	return 0;
723 }
724 
725 
proc_coord_unregister_handler(struct proc_coord * pc,proc_coord_cb cb,void * cb_ctx)726 void proc_coord_unregister_handler(struct proc_coord *pc, proc_coord_cb cb,
727 				   void *cb_ctx)
728 {
729 	struct proc_coord_handler *handler;
730 
731 	dl_list_for_each(handler, &pc->handlers, struct proc_coord_handler,
732 			 list) {
733 		if (handler->cb == cb && handler->cb_ctx == cb_ctx) {
734 			dl_list_del(&handler->list);
735 			os_free(handler);
736 			break;
737 		}
738 	}
739 }
740 
741 
proc_coord_send_event(struct proc_coord * pc,int dst,enum proc_coord_commands cmd,const struct wpabuf * msg)742 int proc_coord_send_event(struct proc_coord *pc, int dst,
743 			  enum proc_coord_commands cmd,
744 			  const struct wpabuf *msg)
745 {
746 	struct proc_coord_peer *peer, *tmp;
747 	int count = 0;
748 	u32 seq = 0;
749 
750 	dl_list_for_each_safe(peer, tmp, &pc->peers, struct proc_coord_peer,
751 			      list) {
752 		if (dst && peer->pid != dst)
753 			continue;
754 		if (!dst && peer->state != PROC_COORD_PEER_ACTIVE)
755 			continue;
756 		if (proc_coord_send_msg(pc, peer, PROC_COORD_MSG_EVENT, cmd,
757 					seq, msg) == 0)
758 			count++;
759 	}
760 
761 	return count;
762 }
763 
764 
proc_coord_send_request(struct proc_coord * pc,int dst,enum proc_coord_commands cmd,const struct wpabuf * msg,unsigned int timeout_ms,proc_coord_response_cb cb,void * cb_ctx)765 int proc_coord_send_request(struct proc_coord *pc, int dst,
766 			    enum proc_coord_commands cmd,
767 			    const struct wpabuf *msg,
768 			    unsigned int timeout_ms,
769 			    proc_coord_response_cb cb,
770 			    void *cb_ctx)
771 {
772 	struct proc_coord_peer *peer, *tmp;
773 	int count = 0;
774 	struct proc_coord_pending_request *req;
775 
776 	pc->next_seq++;
777 
778 	dl_list_for_each_safe(peer, tmp, &pc->peers, struct proc_coord_peer,
779 			      list) {
780 		if (dst && peer->pid != dst)
781 			continue;
782 		if (!dst && peer->state != PROC_COORD_PEER_ACTIVE)
783 			continue;
784 		if (proc_coord_send_msg(pc, peer, PROC_COORD_MSG_REQUEST, cmd,
785 					pc->next_seq, msg) < 0)
786 			continue;
787 		count++;
788 
789 		req = os_zalloc(sizeof(*req));
790 		if (!req)
791 			break;
792 		req->peer = peer;
793 		req->cmd = cmd;
794 		req->seq = pc->next_seq;
795 		req->cb = cb;
796 		req->cb_ctx = cb_ctx;
797 		os_get_reltime(&req->timeout);
798 		os_reltime_add_ms(&req->timeout, timeout_ms);
799 		dl_list_add(&pc->requests, &req->list);
800 	}
801 	proc_coord_set_req_expire_timer(pc);
802 
803 	return count;
804 }
805 
806 
proc_coord_send_response(struct proc_coord * pc,int dst,enum proc_coord_commands cmd,u32 seq,const struct wpabuf * msg)807 int proc_coord_send_response(struct proc_coord *pc, int dst,
808 			     enum proc_coord_commands cmd, u32 seq,
809 			     const struct wpabuf *msg)
810 {
811 	struct proc_coord_peer *peer = proc_coord_get_peer(pc, dst);
812 
813 	if (!peer)
814 		return -1;
815 	return proc_coord_send_msg(pc, peer, PROC_COORD_MSG_RESPONSE, cmd,
816 				   seq, msg);
817 }
818