• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/procd/stdio-fds.h

  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