1 /* 2 * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU Lesser General Public License version 2.1 6 * as published by the Free Software Foundation 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 */ 13 14 #ifndef __STDIO_FDS_H 15 #define __STDIO_FDS_H 16 17 #include <string.h> 18 #include <sys/socket.h> 19 #include <unistd.h> 20 21 #define STDIO_FDS_NUM 3 22 #define FDS_NUM_MAX (STDIO_FDS_NUM + 1) 23 24 /* 25 * ubus carries a single file descriptor per request, so descriptor sets 26 * travel as SCM_RIGHTS over a socket pair whose receiving end is what gets 27 * passed to ubus_invoke_fd(). The payload byte carries the count. 28 */ 29 static inline int fds_send(const int *fds, int num) 30 { 31 char cmsgbuf[CMSG_SPACE(FDS_NUM_MAX * sizeof(int))]; 32 struct msghdr msg = { 0 }; 33 struct cmsghdr *cmsg; 34 struct iovec iov; 35 char count; 36 int sp[2]; 37 38 if (num < 1 || num > FDS_NUM_MAX) 39 return -1; 40 41 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp)) 42 return -1; 43 44 count = num; 45 iov.iov_base = &count; 46 iov.iov_len = sizeof(count); 47 msg.msg_iov = &iov; 48 msg.msg_iovlen = 1; 49 msg.msg_control = cmsgbuf; 50 msg.msg_controllen = CMSG_SPACE(num * sizeof(int)); 51 52 cmsg = CMSG_FIRSTHDR(&msg); 53 cmsg->cmsg_level = SOL_SOCKET; 54 cmsg->cmsg_type = SCM_RIGHTS; 55 cmsg->cmsg_len = CMSG_LEN(num * sizeof(int)); 56 memcpy(CMSG_DATA(cmsg), fds, num * sizeof(int)); 57 58 if (sendmsg(sp[0], &msg, 0) < 0) { 59 close(sp[0]); 60 close(sp[1]); 61 return -1; 62 } 63 64 close(sp[0]); 65 66 return sp[1]; 67 } 68 69 static inline int fds_recv(int sock, int *fds, int max) 70 { 71 char cmsgbuf[CMSG_SPACE(FDS_NUM_MAX * sizeof(int))]; 72 int tmp[FDS_NUM_MAX]; 73 struct msghdr msg = { 0 }; 74 struct cmsghdr *cmsg; 75 struct iovec iov; 76 char count; 77 int num, i; 78 79 iov.iov_base = &count; 80 iov.iov_len = sizeof(count); 81 msg.msg_iov = &iov; 82 msg.msg_iovlen = 1; 83 msg.msg_control = cmsgbuf; 84 msg.msg_controllen = sizeof(cmsgbuf); 85 86 if (recvmsg(sock, &msg, MSG_CMSG_CLOEXEC | MSG_DONTWAIT) < 1) 87 return -1; 88 89 cmsg = CMSG_FIRSTHDR(&msg); 90 if (!cmsg || cmsg->cmsg_level != SOL_SOCKET || 91 cmsg->cmsg_type != SCM_RIGHTS || 92 cmsg->cmsg_len < CMSG_LEN(sizeof(int)) || 93 cmsg->cmsg_len > CMSG_LEN(FDS_NUM_MAX * sizeof(int))) 94 return -1; 95 96 num = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); 97 memcpy(tmp, CMSG_DATA(cmsg), num * sizeof(int)); 98 99 if (num != count || num > max) { 100 for (i = 0; i < num; i++) 101 close(tmp[i]); 102 return -1; 103 } 104 105 memcpy(fds, tmp, num * sizeof(int)); 106 107 return num; 108 } 109 110 static inline int stdio_notify_fds_send(const int *stdio_fds, int notify_fd) 111 { 112 int fds[FDS_NUM_MAX]; 113 int num = 0; 114 115 if (stdio_fds) { 116 memcpy(fds, stdio_fds, STDIO_FDS_NUM * sizeof(int)); 117 num = STDIO_FDS_NUM; 118 } 119 120 if (notify_fd > -1) 121 fds[num++] = notify_fd; 122 123 if (!num) 124 return -1; 125 126 return fds_send(fds, num); 127 } 128 129 static inline int stdio_notify_fds_recv(int sock, int *stdio_fds, int *notify_fd) 130 { 131 int fds[FDS_NUM_MAX]; 132 int num; 133 134 num = fds_recv(sock, fds, FDS_NUM_MAX); 135 switch (num) { 136 case 1: 137 *notify_fd = fds[0]; 138 return 0; 139 case STDIO_FDS_NUM + 1: 140 *notify_fd = fds[STDIO_FDS_NUM]; 141 /* fallthrough */ 142 case STDIO_FDS_NUM: 143 memcpy(stdio_fds, fds, STDIO_FDS_NUM * sizeof(int)); 144 return 0; 145 default: 146 while (num > 0) 147 close(fds[--num]); 148 return -1; 149 } 150 } 151 152 static inline void stdio_fds_close(int *fds) 153 { 154 int i; 155 156 for (i = 0; i < STDIO_FDS_NUM; i++) { 157 if (fds[i] < 0) 158 continue; 159 160 close(fds[i]); 161 fds[i] = -1; 162 } 163 } 164 165 #endif 166
This page was automatically generated by LXR 0.3.1. • OpenWrt