Implement mmap support with STORE and RETRIEVE notifications
[ossp.git] / ossp-slave.c
blob0b125ffa4b2a34fcef025533e4993ec61d0a5c60
1 /*
2 * ossp-slave - OSS Proxy: Common codes for slaves
4 * Copyright (C) 2008-2010 SUSE Linux Products GmbH
5 * Copyright (C) 2008-2010 Tejun Heo <tj@kernel.org>
7 * This file is released under the GPLv2.
8 */
10 #define _GNU_SOURCE
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>
22 #include "ossp-slave.h"
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 " -s MMAP_SIZE mmap size\n"
36 " -l LOG_LEVEL set log level\n"
37 " -t enable log timestamps\n";
39 char ossp_user_name[OSSP_USER_NAME_LEN];
40 int ossp_cmd_fd = -1, ossp_notify_fd = -1;
41 void *ossp_mmap_addr[2];
42 struct ossp_transfer *ossp_mmap_transfer;
44 void ossp_slave_init(int argc, char **argv)
46 int have_uid = 0, have_gid = 0;
47 uid_t uid;
48 gid_t gid;
49 int mmap_fd = -1;
50 size_t mmap_size = 0;
51 int opt;
52 struct passwd *pw, pw_buf;
53 struct sigaction sa;
54 char pw_sbuf[sysconf(_SC_GETPW_R_SIZE_MAX)];
56 while ((opt = getopt(argc, argv, "u:g:c:n:m:o:s:l:t")) != -1) {
57 switch (opt) {
58 case 'u':
59 have_uid = 1;
60 uid = strtol(optarg, NULL, 0);
61 break;
62 case 'g':
63 have_gid = 1;
64 gid = strtol(optarg, NULL, 0);
65 break;
66 case 'c':
67 ossp_cmd_fd = strtol(optarg, NULL, 0);
68 break;
69 case 'n':
70 ossp_notify_fd = strtol(optarg, NULL, 0);
71 break;
72 case 'm':
73 mmap_fd = strtol(optarg, NULL, 0);
74 break;
75 case 's':
76 mmap_size = strtoul(optarg, NULL, 0);
77 break;
78 case 'l':
79 ossp_log_level = strtol(optarg, NULL, 0);
80 break;
81 case 't':
82 ossp_log_timestamp = 1;
83 break;
87 if (!have_uid || !have_gid || ossp_cmd_fd < 0 || ossp_notify_fd < 0) {
88 fprintf(stderr, usage);
89 _exit(1);
92 snprintf(ossp_user_name, sizeof(ossp_user_name), "uid%d", uid);
93 if (getpwuid_r(uid, &pw_buf, pw_sbuf, sizeof(pw_sbuf), &pw) == 0)
94 snprintf(ossp_user_name, sizeof(ossp_user_name), "%s",
95 pw->pw_name);
97 snprintf(ossp_log_name, sizeof(ossp_log_name), "ossp-padsp[%s:%d]",
98 ossp_user_name, getpid());
100 if (mmap_fd >= 0) {
101 void *p;
103 if (!mmap_size) {
104 fprintf(stderr, usage);
105 _exit(1);
108 p = mmap(NULL, mmap_size + 2 * sizeof(struct ossp_transfer),
109 PROT_READ | PROT_WRITE, MAP_SHARED, mmap_fd, 0);
110 if (p == MAP_FAILED)
111 fatal_e(-errno, "mmap failed");
113 ossp_mmap_addr[PLAY] = p;
114 ossp_mmap_addr[REC] = p + mmap_size / 2;
115 ossp_mmap_transfer = p + mmap_size;
116 close(mmap_fd);
119 /* mmap done, drop privileges */
120 if (setresgid(gid, gid, gid) || setresuid(uid, uid, uid))
121 fatal_e(-errno, "failed to drop privileges");
123 /* block SIGPIPE */
124 memset(&sa, 0, sizeof(sa));
125 sa.sa_handler = SIG_IGN;
126 if (sigaction(SIGPIPE, &sa, NULL))
127 fatal_e(-errno, "failed to ignore SIGPIPE");
130 int ossp_slave_process_command(int cmd_fd,
131 ossp_action_fn_t const *action_fn_tbl,
132 int (*action_pre_fn)(void),
133 void (*action_post_fn)(void))
135 static struct sized_buf carg_sbuf = { }, rarg_sbuf = { };
136 static struct sized_buf din_sbuf = { }, dout_sbuf = { };
137 struct ossp_cmd cmd;
138 int fd = -1;
139 char cmsg_buf[CMSG_SPACE(sizeof(fd))];
140 struct iovec iov = { &cmd, sizeof(cmd) };
141 struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1,
142 .msg_control = cmsg_buf,
143 .msg_controllen = sizeof(cmsg_buf) };
144 struct cmsghdr *cmsg;
145 size_t carg_size, din_size, rarg_size, dout_size;
146 char *carg = NULL, *din = NULL, *rarg = NULL, *dout = NULL;
147 struct ossp_reply reply = { .magic = OSSP_REPLY_MAGIC };
148 ssize_t ret;
150 ret = recvmsg(cmd_fd, &msg, 0);
151 if (ret == 0)
152 return 0;
153 if (ret < 0) {
154 ret = -errno;
155 err_e(ret, "failed to read command channel");
156 return ret;
159 if (ret != sizeof(cmd)) {
160 err("command struct size mismatch (%zu, should be %zu)",
161 ret, sizeof(cmd));
162 return -EINVAL;
165 if (cmd.magic != OSSP_CMD_MAGIC) {
166 err("illegal command magic 0x%x", cmd.magic);
167 return -EINVAL;
170 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
171 cmsg = CMSG_NXTHDR(&msg, cmsg)) {
172 if (cmsg->cmsg_level == SOL_SOCKET &&
173 cmsg->cmsg_type == SCM_RIGHTS)
174 fd = *(int *)CMSG_DATA(cmsg);
175 else {
176 err("unknown cmsg %d:%d received (opcode %d)",
177 cmsg->cmsg_level, cmsg->cmsg_type, cmd.opcode);
178 return -EINVAL;
182 if (cmd.opcode >= OSSP_NR_OPCODES) {
183 err("unknown opcode %d", cmd.opcode);
184 return -EINVAL;
187 carg_size = ossp_arg_sizes[cmd.opcode].carg_size;
188 din_size = cmd.din_size;
189 rarg_size = ossp_arg_sizes[cmd.opcode].rarg_size;
190 dout_size = cmd.dout_size;
192 if ((fd >= 0) != ossp_arg_sizes[cmd.opcode].has_fd) {
193 err("fd=%d unexpected for opcode %d", fd, cmd.opcode);
194 return -EINVAL;
197 if (ensure_sbuf_size(&carg_sbuf, carg_size) ||
198 ensure_sbuf_size(&din_sbuf, din_size) ||
199 ensure_sbuf_size(&rarg_sbuf, rarg_size) ||
200 ensure_sbuf_size(&dout_sbuf, dout_size)) {
201 err("failed to allocate command buffers");
202 return -ENOMEM;
205 if (carg_size) {
206 carg = carg_sbuf.buf;
207 ret = read_fill(cmd_fd, carg, carg_size);
208 if (ret < 0)
209 return ret;
211 if (din_size) {
212 din = din_sbuf.buf;
213 ret = read_fill(cmd_fd, din, din_size);
214 if (ret < 0)
215 return ret;
217 if (rarg_size)
218 rarg = rarg_sbuf.buf;
219 if (dout_size)
220 dout = dout_sbuf.buf;
222 ret = -EINVAL;
223 if (action_fn_tbl[cmd.opcode]) {
224 ret = action_pre_fn();
225 if (ret == 0) {
226 ret = action_fn_tbl[cmd.opcode](cmd.opcode, carg,
227 din, din_size, rarg,
228 dout, &dout_size, fd);
229 action_post_fn();
233 reply.result = ret;
234 if (ret >= 0)
235 reply.dout_size = dout_size;
236 else {
237 rarg_size = 0;
238 dout_size = 0;
241 if (write_fill(cmd_fd, &reply, sizeof(reply)) < 0 ||
242 write_fill(cmd_fd, rarg, rarg_size) < 0 ||
243 write_fill(cmd_fd, dout, dout_size) < 0)
244 return -EIO;
246 return 1;