Release version 1.3.2-5
[osspd.git] / ossp-slave.c
1 /*
2  * ossp-slave - OSS Proxy: Common codes for slaves
3  *
4  * Copyright (C) 2008-2010  SUSE Linux Products GmbH
5  * Copyright (C) 2008-2010  Tejun Heo <tj@kernel.org>
6  *
7  * This file is released under the GPLv2.
8  */
9
10 #define _GNU_SOURCE
11
12 #include <sys/types.h>
13 #include <sys/mman.h>
14 #include <sys/socket.h>
15 #include <sys/uio.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <pwd.h>
20 #include <signal.h>
21
22 #include "ossp-slave.h"
23
24 static const char *usage =
25 "usage: ossp-SLAVE [options]\n"
26 "\n"
27 "proxies commands from osspd to pulseaudio\n"
28 "\n"
29 "options:\n"
30 "    -u UID            uid to use\n"
31 "    -g GID            gid to use\n"
32 "    -c CMD_FD         fd to receive commands from osspd\n"
33 "    -n NOTIFY_FD      fd to send async notifications to osspd\n"
34 "    -m MMAP_FD        fd to use for mmap\n"
35 "    -o MMAP_OFFSET    mmap offset\n"
36 "    -s MMAP_SIZE      mmap size\n"
37 "    -l LOG_LEVEL      set log level\n"
38 "    -t                enable log timestamps\n";
39
40 char ossp_user_name[OSSP_USER_NAME_LEN];
41 int ossp_cmd_fd = -1, ossp_notify_fd = -1;
42 void *ossp_mmap_addr[2];
43
44 void ossp_slave_init(int argc, char **argv)
45 {
46         int have_uid = 0, have_gid = 0;
47         uid_t uid;
48         gid_t gid;
49         int mmap_fd = -1;
50         off_t mmap_off = 0;
51         size_t mmap_size = 0;
52         int opt;
53         struct passwd *pw, pw_buf;
54         struct sigaction sa;
55         char pw_sbuf[sysconf(_SC_GETPW_R_SIZE_MAX)];
56
57         while ((opt = getopt(argc, argv, "u:g:c:n:m:o:s:l:t")) != -1) {
58                 switch (opt) {
59                 case 'u':
60                         have_uid = 1;
61                         uid = strtol(optarg, NULL, 0);
62                         break;
63                 case 'g':
64                         have_gid = 1;
65                         gid = strtol(optarg, NULL, 0);
66                         break;
67                 case 'c':
68                         ossp_cmd_fd = strtol(optarg, NULL, 0);
69                         break;
70                 case 'n':
71                         ossp_notify_fd = strtol(optarg, NULL, 0);
72                         break;
73                 case 'm':
74                         mmap_fd = strtol(optarg, NULL, 0);
75                         break;
76                 case 'o':
77                         mmap_off = strtoull(optarg, NULL, 0);
78                         break;
79                 case 's':
80                         mmap_size = strtoul(optarg, NULL, 0);
81                         break;
82                 case 'l':
83                         ossp_log_level = strtol(optarg, NULL, 0);
84                         break;
85                 case 't':
86                         ossp_log_timestamp = 1;
87                         break;
88                 }
89         }
90
91         if (!have_uid || !have_gid || ossp_cmd_fd < 0 || ossp_notify_fd < 0) {
92                 fprintf(stderr, usage);
93                 _exit(1);
94         }
95
96         snprintf(ossp_user_name, sizeof(ossp_user_name), "uid%d", uid);
97         if (getpwuid_r(uid, &pw_buf, pw_sbuf, sizeof(pw_sbuf), &pw) == 0)
98                 snprintf(ossp_user_name, sizeof(ossp_user_name), "%s",
99                          pw->pw_name);
100
101         snprintf(ossp_log_name, sizeof(ossp_log_name), "ossp-padsp[%s:%d]",
102                  ossp_user_name, getpid());
103
104         if (mmap_fd >= 0) {
105                 void *p;
106
107                 if (!mmap_off || !mmap_size) {
108                         fprintf(stderr, usage);
109                         _exit(1);
110                 }
111
112                 p = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED,
113                          mmap_fd, mmap_off);
114                 if (p == MAP_FAILED)
115                         fatal_e(-errno, "mmap failed");
116
117                 ossp_mmap_addr[PLAY] = p;
118                 ossp_mmap_addr[REC] = p + mmap_size / 2;
119                 close(mmap_fd);
120         }
121
122         /* mmap done, drop privileges */
123         if (setresgid(gid, gid, gid) || setresuid(uid, uid, uid))
124                 fatal_e(-errno, "failed to drop privileges");
125
126         /* block SIGPIPE */
127         memset(&sa, 0, sizeof(sa));
128         sa.sa_handler = SIG_IGN;
129         if (sigaction(SIGPIPE, &sa, NULL))
130                 fatal_e(-errno, "failed to ignore SIGPIPE");
131 }
132
133 int ossp_slave_process_command(int cmd_fd,
134                                ossp_action_fn_t const *action_fn_tbl,
135                                int (*action_pre_fn)(void),
136                                void (*action_post_fn)(void))
137 {
138         static struct sized_buf carg_sbuf = { }, rarg_sbuf = { };
139         static struct sized_buf din_sbuf = { }, dout_sbuf = { };
140         struct ossp_cmd cmd;
141         int fd = -1;
142         char cmsg_buf[CMSG_SPACE(sizeof(fd))];
143         struct iovec iov = { &cmd, sizeof(cmd) };
144         struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1,
145                               .msg_control = cmsg_buf,
146                               .msg_controllen = sizeof(cmsg_buf) };
147         struct cmsghdr *cmsg;
148         size_t carg_size, din_size, rarg_size, dout_size;
149         char *carg = NULL, *din = NULL, *rarg = NULL, *dout = NULL;
150         struct ossp_reply reply = { .magic = OSSP_REPLY_MAGIC };
151         ssize_t ret;
152
153         ret = recvmsg(cmd_fd, &msg, 0);
154         if (ret == 0)
155                 return 0;
156         if (ret < 0) {
157                 ret = -errno;
158                 err_e(ret, "failed to read command channel");
159                 return ret;
160         }
161
162         if (ret != sizeof(cmd)) {
163                 err("command struct size mismatch (%zu, should be %zu)",
164                     ret, sizeof(cmd));
165                 return -EINVAL;
166         }
167
168         if (cmd.magic != OSSP_CMD_MAGIC) {
169                 err("illegal command magic 0x%x", cmd.magic);
170                 return -EINVAL;
171         }
172
173         for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
174              cmsg = CMSG_NXTHDR(&msg, cmsg)) {
175                 if (cmsg->cmsg_level == SOL_SOCKET &&
176                     cmsg->cmsg_type == SCM_RIGHTS)
177                         fd = *(int *)CMSG_DATA(cmsg);
178                 else {
179                         err("unknown cmsg %d:%d received (opcode %d)",
180                             cmsg->cmsg_level, cmsg->cmsg_type, cmd.opcode);
181                         return -EINVAL;
182                 }
183         }
184
185         if (cmd.opcode >= OSSP_NR_OPCODES) {
186                 err("unknown opcode %d", cmd.opcode);
187                 return -EINVAL;
188         }
189
190         carg_size = ossp_arg_sizes[cmd.opcode].carg_size;
191         din_size = cmd.din_size;
192         rarg_size = ossp_arg_sizes[cmd.opcode].rarg_size;
193         dout_size = cmd.dout_size;
194
195         if ((fd >= 0) != ossp_arg_sizes[cmd.opcode].has_fd) {
196                 err("fd=%d unexpected for opcode %d", fd, cmd.opcode);
197                 return -EINVAL;
198         }
199
200         if (ensure_sbuf_size(&carg_sbuf, carg_size) ||
201             ensure_sbuf_size(&din_sbuf, din_size) ||
202             ensure_sbuf_size(&rarg_sbuf, rarg_size) ||
203             ensure_sbuf_size(&dout_sbuf, dout_size)) {
204                 err("failed to allocate command buffers");
205                 return -ENOMEM;
206         }
207
208         if (carg_size) {
209                 carg = carg_sbuf.buf;
210                 ret = read_fill(cmd_fd, carg, carg_size);
211                 if (ret < 0)
212                         return ret;
213         }
214         if (din_size) {
215                 din = din_sbuf.buf;
216                 ret = read_fill(cmd_fd, din, din_size);
217                 if (ret < 0)
218                         return ret;
219         }
220         if (rarg_size)
221                 rarg = rarg_sbuf.buf;
222         if (dout_size)
223                 dout = dout_sbuf.buf;
224
225         ret = -EINVAL;
226         if (action_fn_tbl[cmd.opcode]) {
227                 ret = action_pre_fn();
228                 if (ret == 0) {
229                         ret = action_fn_tbl[cmd.opcode](cmd.opcode, carg,
230                                                         din, din_size, rarg,
231                                                         dout, &dout_size, fd);
232                         action_post_fn();
233                 }
234         }
235
236         reply.result = ret;
237         if (ret >= 0)
238                 reply.dout_size = dout_size;
239         else {
240                 rarg_size = 0;
241                 dout_size = 0;
242         }
243
244         if (write_fill(cmd_fd, &reply, sizeof(reply)) < 0 ||
245             write_fill(cmd_fd, rarg, rarg_size) < 0 ||
246             write_fill(cmd_fd, dout, dout_size) < 0)
247                 return -EIO;
248
249         return 1;
250 }