session: expose rpc_session_access() function for testing session ACLs in other rpcd...
[project/rpcd.git] / exec.c
1 /*
2  * luci-rpcd - LuCI UBUS RPC server
3  *
4  *   Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
5  *
6  * Permission to use, copy, modify, and/or 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 <fcntl.h>
20 #include <errno.h>
21 #include <unistd.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <limits.h>
25 #include <dirent.h>
26 #include <sys/stat.h>
27 #include <sys/wait.h>
28
29 #include "exec.h"
30
31 static int
32 rpc_errno_status(void)
33 {
34         switch (errno)
35         {
36         case EACCES:
37                 return UBUS_STATUS_PERMISSION_DENIED;
38
39         case ENOTDIR:
40                 return UBUS_STATUS_INVALID_ARGUMENT;
41
42         case ENOENT:
43                 return UBUS_STATUS_NOT_FOUND;
44
45         case EINVAL:
46                 return UBUS_STATUS_INVALID_ARGUMENT;
47
48         default:
49                 return UBUS_STATUS_UNKNOWN_ERROR;
50         }
51 }
52
53 static const char *
54 rpc_exec_lookup(const char *cmd)
55 {
56         struct stat s;
57         int plen = 0, clen = strlen(cmd) + 1;
58         char *search, *p;
59         static char path[PATH_MAX];
60
61         if (!stat(cmd, &s) && S_ISREG(s.st_mode))
62                 return cmd;
63
64         search = getenv("PATH");
65
66         if (!search)
67                 search = "/bin:/usr/bin:/sbin:/usr/sbin";
68
69         p = search;
70
71         do
72         {
73                 if (*p != ':' && *p != '\0')
74                         continue;
75
76                 plen = p - search;
77
78                 if ((plen + clen) >= sizeof(path))
79                         continue;
80
81                 strncpy(path, search, plen);
82                 sprintf(path + plen, "/%s", cmd);
83
84                 if (!stat(path, &s) && S_ISREG(s.st_mode))
85                         return path;
86
87                 search = p + 1;
88         }
89         while (*p++);
90
91         return NULL;
92 }
93
94
95 static void
96 rpc_ustream_to_blobmsg(struct blob_buf *blob, struct ustream *s,
97                        const char *name)
98 {
99         int len;
100         char *rbuf, *wbuf;
101
102         if ((len = ustream_pending_data(s, false)) > 0)
103         {
104                 wbuf = blobmsg_alloc_string_buffer(blob, name, len + 1);
105
106                 if (!wbuf)
107                         return;
108
109                 ustream_for_each_read_buffer(s, rbuf, len)
110                 {
111                         memcpy(wbuf, rbuf, len);
112                         wbuf += len;
113                 }
114
115                 *wbuf = 0;
116                 blobmsg_add_string_buffer(blob);
117         }
118 }
119
120 static void
121 rpc_exec_reply(struct rpc_exec_context *c, int rv)
122 {
123         uloop_timeout_cancel(&c->timeout);
124         uloop_process_delete(&c->process);
125
126         if (rv == UBUS_STATUS_OK)
127         {
128                 if (!c->stdout_cb && !c->stderr_cb && !c->finish_cb)
129                 {
130                         blobmsg_add_u32(&c->blob, "code", WEXITSTATUS(c->stat));
131                         rpc_ustream_to_blobmsg(&c->blob, &c->opipe.stream, "stdout");
132                         rpc_ustream_to_blobmsg(&c->blob, &c->epipe.stream, "stderr");
133                 }
134
135                 if (c->finish_cb)
136                         c->finish_cb(&c->blob, c->stat, c->priv);
137
138                 ubus_send_reply(c->context, &c->request, c->blob.head);
139         }
140
141         ubus_complete_deferred_request(c->context, &c->request, rv);
142
143         blob_buf_free(&c->blob);
144
145         ustream_free(&c->opipe.stream);
146         ustream_free(&c->epipe.stream);
147
148         close(c->opipe.fd.fd);
149         close(c->epipe.fd.fd);
150
151         if (c->priv)
152                 free(c->priv);
153
154         free(c);
155 }
156
157 static void
158 rpc_exec_timestdout_cb(struct uloop_timeout *t)
159 {
160         struct rpc_exec_context *c =
161                 container_of(t, struct rpc_exec_context, timeout);
162
163         kill(c->process.pid, SIGKILL);
164         rpc_exec_reply(c, UBUS_STATUS_TIMEOUT);
165 }
166
167 static void
168 rpc_exec_process_cb(struct uloop_process *p, int stat)
169 {
170         struct rpc_exec_context *c =
171                 container_of(p, struct rpc_exec_context, process);
172
173         c->stat = stat;
174
175         ustream_poll(&c->opipe.stream);
176         ustream_poll(&c->epipe.stream);
177 }
178
179 static void
180 rpc_exec_opipe_read_cb(struct ustream *s, int bytes)
181 {
182         int len, rv;
183         char *buf;
184         struct rpc_exec_context *c =
185                 container_of(s, struct rpc_exec_context, opipe.stream);
186
187         if (c->stdout_cb)
188         {
189                 do {
190                         buf = ustream_get_read_buf(s, &len);
191
192                         if (!buf || !len)
193                                 break;
194
195                         rv = c->stdout_cb(&c->blob, buf, len, c->priv);
196
197                         if (rv <= 0)
198                                 break;
199
200                         ustream_consume(s, rv);
201                 } while(1);
202         }
203         else if (ustream_read_buf_full(s))
204         {
205                 rpc_exec_reply(c, UBUS_STATUS_NOT_SUPPORTED);
206         }
207 }
208
209 static void
210 rpc_exec_epipe_read_cb(struct ustream *s, int bytes)
211 {
212         int len, rv;
213         char *buf;
214         struct rpc_exec_context *c =
215                 container_of(s, struct rpc_exec_context, epipe.stream);
216
217         if (c->stderr_cb)
218         {
219                 do {
220                         buf = ustream_get_read_buf(s, &len);
221
222                         if (!buf || !len)
223                                 break;
224
225                         rv = c->stderr_cb(&c->blob, buf, len, c->priv);
226
227                         if (rv <= 0)
228                                 break;
229
230                         ustream_consume(s, rv);
231                 } while(1);
232         }
233         else if (ustream_read_buf_full(s))
234         {
235                 rpc_exec_reply(c, UBUS_STATUS_NOT_SUPPORTED);
236         }
237 }
238
239 static void
240 rpc_exec_opipe_state_cb(struct ustream *s)
241 {
242         struct rpc_exec_context *c =
243                 container_of(s, struct rpc_exec_context, opipe.stream);
244
245         if (c->opipe.stream.eof && c->epipe.stream.eof)
246                 rpc_exec_reply(c, UBUS_STATUS_OK);
247 }
248
249 static void
250 rpc_exec_epipe_state_cb(struct ustream *s)
251 {
252         struct rpc_exec_context *c =
253                 container_of(s, struct rpc_exec_context, epipe.stream);
254
255         if (c->opipe.stream.eof && c->epipe.stream.eof)
256                 rpc_exec_reply(c, UBUS_STATUS_OK);
257 }
258
259 int
260 rpc_exec(const char **args, rpc_exec_read_cb_t out, rpc_exec_read_cb_t err,
261          rpc_exec_done_cb_t end, void *priv, struct ubus_context *ctx,
262          struct ubus_request_data *req)
263 {
264         pid_t pid;
265
266         int opipe[2];
267         int epipe[2];
268
269         const char *cmd;
270         struct rpc_exec_context *c;
271
272         cmd = rpc_exec_lookup(args[0]);
273
274         if (!cmd)
275                 return UBUS_STATUS_NOT_FOUND;
276
277         c = malloc(sizeof(*c));
278
279         if (!c)
280                 return UBUS_STATUS_UNKNOWN_ERROR;
281
282         if (pipe(opipe) || pipe(epipe))
283                 return rpc_errno_status();
284
285         switch ((pid = fork()))
286         {
287         case -1:
288                 return rpc_errno_status();
289
290         case 0:
291                 uloop_done();
292
293                 dup2(opipe[1], 1);
294                 dup2(epipe[1], 2);
295
296                 close(0);
297                 close(opipe[0]);
298                 close(opipe[1]);
299                 close(epipe[0]);
300                 close(epipe[1]);
301
302                 if (execv(cmd, (char * const *)args))
303                         return rpc_errno_status();
304
305         default:
306                 memset(c, 0, sizeof(*c));
307                 blob_buf_init(&c->blob, 0);
308
309                 c->stdout_cb = out;
310                 c->stderr_cb = err;
311                 c->finish_cb = end;
312                 c->priv      = priv;
313
314                 ustream_declare(c->opipe, opipe[0], opipe);
315                 ustream_declare(c->epipe, epipe[0], epipe);
316
317                 c->process.pid = pid;
318                 c->process.cb = rpc_exec_process_cb;
319                 uloop_process_add(&c->process);
320
321                 c->timeout.cb = rpc_exec_timestdout_cb;
322                 uloop_timeout_set(&c->timeout, RPC_EXEC_MAX_RUNTIME);
323
324                 close(opipe[1]);
325                 close(epipe[1]);
326
327                 c->context = ctx;
328                 ubus_defer_request(ctx, req, &c->request);
329         }
330
331         return UBUS_STATUS_OK;
332 }