1*79256953Sratchov /* $OpenBSD: aucat.c,v 1.79 2021/11/07 20:51:47 ratchov Exp $ */
282bfc72bSratchov /*
382bfc72bSratchov * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
482bfc72bSratchov *
582bfc72bSratchov * Permission to use, copy, modify, and distribute this software for any
682bfc72bSratchov * purpose with or without fee is hereby granted, provided that the above
782bfc72bSratchov * copyright notice and this permission notice appear in all copies.
882bfc72bSratchov *
982bfc72bSratchov * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1082bfc72bSratchov * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1182bfc72bSratchov * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1282bfc72bSratchov * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1382bfc72bSratchov * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1482bfc72bSratchov * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1582bfc72bSratchov * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1682bfc72bSratchov */
1782bfc72bSratchov
1882bfc72bSratchov #include <sys/types.h>
1982bfc72bSratchov #include <sys/socket.h>
20edc7bd3bSratchov #include <sys/stat.h>
2182bfc72bSratchov #include <sys/un.h>
2282bfc72bSratchov
23edc7bd3bSratchov #include <netinet/in.h>
24ad38cbdeSratchov #include <netinet/tcp.h>
25edc7bd3bSratchov #include <netdb.h>
26edc7bd3bSratchov
2782bfc72bSratchov #include <errno.h>
2882bfc72bSratchov #include <fcntl.h>
29edc7bd3bSratchov #include <limits.h>
3082bfc72bSratchov #include <poll.h>
3182bfc72bSratchov #include <stdio.h>
3282bfc72bSratchov #include <stdlib.h>
3382bfc72bSratchov #include <string.h>
3482bfc72bSratchov #include <unistd.h>
3582bfc72bSratchov
3682bfc72bSratchov #include "aucat.h"
3782bfc72bSratchov #include "debug.h"
3882bfc72bSratchov
39fcda7a7eSratchov
4082bfc72bSratchov /*
4182bfc72bSratchov * read a message, return 0 if not completed
4282bfc72bSratchov */
4382bfc72bSratchov int
_aucat_rmsg(struct aucat * hdl,int * eof)44d418f94bSratchov _aucat_rmsg(struct aucat *hdl, int *eof)
4582bfc72bSratchov {
4682bfc72bSratchov ssize_t n;
4782bfc72bSratchov unsigned char *data;
4882bfc72bSratchov
4982bfc72bSratchov if (hdl->rstate != RSTATE_MSG) {
50d418f94bSratchov DPRINTF("_aucat_rmsg: bad state\n");
5182bfc72bSratchov abort();
5282bfc72bSratchov }
5382bfc72bSratchov while (hdl->rtodo > 0) {
5482bfc72bSratchov data = (unsigned char *)&hdl->rmsg;
5582bfc72bSratchov data += sizeof(struct amsg) - hdl->rtodo;
5695d14907Sratchov while ((n = read(hdl->fd, data, hdl->rtodo)) == -1) {
5782bfc72bSratchov if (errno == EINTR)
5882bfc72bSratchov continue;
5982bfc72bSratchov if (errno != EAGAIN) {
6082bfc72bSratchov *eof = 1;
61d418f94bSratchov DPERROR("_aucat_rmsg: read");
6282bfc72bSratchov }
6382bfc72bSratchov return 0;
6482bfc72bSratchov }
6582bfc72bSratchov if (n == 0) {
66d418f94bSratchov DPRINTF("_aucat_rmsg: eof\n");
6782bfc72bSratchov *eof = 1;
6882bfc72bSratchov return 0;
6982bfc72bSratchov }
7082bfc72bSratchov hdl->rtodo -= n;
7182bfc72bSratchov }
72b5fec221Sratchov if (ntohl(hdl->rmsg.cmd) == AMSG_DATA) {
73b5fec221Sratchov hdl->rtodo = ntohl(hdl->rmsg.u.data.size);
7482bfc72bSratchov hdl->rstate = RSTATE_DATA;
7582bfc72bSratchov } else {
7682bfc72bSratchov hdl->rtodo = sizeof(struct amsg);
7782bfc72bSratchov hdl->rstate = RSTATE_MSG;
7882bfc72bSratchov }
7982bfc72bSratchov return 1;
8082bfc72bSratchov }
8182bfc72bSratchov
8282bfc72bSratchov /*
8382bfc72bSratchov * write a message, return 0 if not completed
8482bfc72bSratchov */
8582bfc72bSratchov int
_aucat_wmsg(struct aucat * hdl,int * eof)86d418f94bSratchov _aucat_wmsg(struct aucat *hdl, int *eof)
8782bfc72bSratchov {
8882bfc72bSratchov ssize_t n;
8982bfc72bSratchov unsigned char *data;
9082bfc72bSratchov
91b90e880fSjsg if (hdl->wstate == WSTATE_IDLE) {
9282bfc72bSratchov hdl->wstate = WSTATE_MSG;
9382bfc72bSratchov hdl->wtodo = sizeof(struct amsg);
94b90e880fSjsg }
9582bfc72bSratchov if (hdl->wstate != WSTATE_MSG) {
96d418f94bSratchov DPRINTF("_aucat_wmsg: bad state\n");
9782bfc72bSratchov abort();
9882bfc72bSratchov }
9982bfc72bSratchov while (hdl->wtodo > 0) {
10082bfc72bSratchov data = (unsigned char *)&hdl->wmsg;
10182bfc72bSratchov data += sizeof(struct amsg) - hdl->wtodo;
10295d14907Sratchov while ((n = write(hdl->fd, data, hdl->wtodo)) == -1) {
10382bfc72bSratchov if (errno == EINTR)
10482bfc72bSratchov continue;
10582bfc72bSratchov if (errno != EAGAIN) {
10682bfc72bSratchov *eof = 1;
107d418f94bSratchov DPERROR("_aucat_wmsg: write");
10882bfc72bSratchov }
10982bfc72bSratchov return 0;
11082bfc72bSratchov }
11182bfc72bSratchov hdl->wtodo -= n;
11282bfc72bSratchov }
113b5fec221Sratchov if (ntohl(hdl->wmsg.cmd) == AMSG_DATA) {
114b5fec221Sratchov hdl->wtodo = ntohl(hdl->wmsg.u.data.size);
11582bfc72bSratchov hdl->wstate = WSTATE_DATA;
11682bfc72bSratchov } else {
11782bfc72bSratchov hdl->wtodo = 0xdeadbeef;
11882bfc72bSratchov hdl->wstate = WSTATE_IDLE;
11982bfc72bSratchov }
12082bfc72bSratchov return 1;
12182bfc72bSratchov }
12282bfc72bSratchov
12382bfc72bSratchov size_t
_aucat_rdata(struct aucat * hdl,void * buf,size_t len,int * eof)124d418f94bSratchov _aucat_rdata(struct aucat *hdl, void *buf, size_t len, int *eof)
12582bfc72bSratchov {
12682bfc72bSratchov ssize_t n;
12782bfc72bSratchov
12882bfc72bSratchov if (hdl->rstate != RSTATE_DATA) {
129d418f94bSratchov DPRINTF("_aucat_rdata: bad state\n");
13082bfc72bSratchov abort();
13182bfc72bSratchov }
13282bfc72bSratchov if (len > hdl->rtodo)
13382bfc72bSratchov len = hdl->rtodo;
13495d14907Sratchov while ((n = read(hdl->fd, buf, len)) == -1) {
13582bfc72bSratchov if (errno == EINTR)
13682bfc72bSratchov continue;
13782bfc72bSratchov if (errno != EAGAIN) {
13882bfc72bSratchov *eof = 1;
139d418f94bSratchov DPERROR("_aucat_rdata: read");
14082bfc72bSratchov }
14182bfc72bSratchov return 0;
14282bfc72bSratchov }
14382bfc72bSratchov if (n == 0) {
144d418f94bSratchov DPRINTF("_aucat_rdata: eof\n");
14582bfc72bSratchov *eof = 1;
14682bfc72bSratchov return 0;
14782bfc72bSratchov }
14882bfc72bSratchov hdl->rtodo -= n;
14982bfc72bSratchov if (hdl->rtodo == 0) {
15082bfc72bSratchov hdl->rstate = RSTATE_MSG;
15182bfc72bSratchov hdl->rtodo = sizeof(struct amsg);
15282bfc72bSratchov }
153d418f94bSratchov DPRINTFN(2, "_aucat_rdata: read: n = %zd\n", n);
15482bfc72bSratchov return n;
15582bfc72bSratchov }
15682bfc72bSratchov
15782bfc72bSratchov size_t
_aucat_wdata(struct aucat * hdl,const void * buf,size_t len,unsigned int wbpf,int * eof)158d418f94bSratchov _aucat_wdata(struct aucat *hdl, const void *buf, size_t len,
1597207b069Sratchov unsigned int wbpf, int *eof)
16082bfc72bSratchov {
16182bfc72bSratchov ssize_t n;
1622e63c6e8Sratchov size_t datasize;
16382bfc72bSratchov
16482bfc72bSratchov switch (hdl->wstate) {
16582bfc72bSratchov case WSTATE_IDLE:
1662e63c6e8Sratchov datasize = len;
1672e63c6e8Sratchov if (datasize > AMSG_DATAMAX)
1682e63c6e8Sratchov datasize = AMSG_DATAMAX;
1692e63c6e8Sratchov datasize -= datasize % wbpf;
1702e63c6e8Sratchov if (datasize == 0)
1712e63c6e8Sratchov datasize = wbpf;
172b5fec221Sratchov hdl->wmsg.cmd = htonl(AMSG_DATA);
1732e63c6e8Sratchov hdl->wmsg.u.data.size = htonl(datasize);
17482bfc72bSratchov hdl->wtodo = sizeof(struct amsg);
17582bfc72bSratchov hdl->wstate = WSTATE_MSG;
17682bfc72bSratchov /* FALLTHROUGH */
17782bfc72bSratchov case WSTATE_MSG:
178d418f94bSratchov if (!_aucat_wmsg(hdl, eof))
17982bfc72bSratchov return 0;
18082bfc72bSratchov }
18182bfc72bSratchov if (len > hdl->wtodo)
18282bfc72bSratchov len = hdl->wtodo;
18382bfc72bSratchov if (len == 0) {
184d418f94bSratchov DPRINTF("_aucat_wdata: len == 0\n");
18582bfc72bSratchov abort();
18682bfc72bSratchov }
18795d14907Sratchov while ((n = write(hdl->fd, buf, len)) == -1) {
18882bfc72bSratchov if (errno == EINTR)
18982bfc72bSratchov continue;
19082bfc72bSratchov if (errno != EAGAIN) {
19182bfc72bSratchov *eof = 1;
192d418f94bSratchov DPERROR("_aucat_wdata: write");
19382bfc72bSratchov }
19482bfc72bSratchov return 0;
19582bfc72bSratchov }
196d418f94bSratchov DPRINTFN(2, "_aucat_wdata: write: n = %zd\n", n);
19782bfc72bSratchov hdl->wtodo -= n;
19882bfc72bSratchov if (hdl->wtodo == 0) {
19982bfc72bSratchov hdl->wstate = WSTATE_IDLE;
20082bfc72bSratchov hdl->wtodo = 0xdeadbeef;
20182bfc72bSratchov }
20282bfc72bSratchov return n;
20382bfc72bSratchov }
20482bfc72bSratchov
205122afd1dSderaadt static int
aucat_mkcookie(unsigned char * cookie)206edc7bd3bSratchov aucat_mkcookie(unsigned char *cookie)
207edc7bd3bSratchov {
208096da69dSratchov #define COOKIE_DIR "/.sndio"
209096da69dSratchov #define COOKIE_SUFFIX "/.sndio/cookie"
21059d9d051Sratchov #define TEMPL_SUFFIX ".XXXXXXXX"
211edc7bd3bSratchov struct stat sb;
2129790449aSratchov char *home, *path = NULL, *tmp = NULL;
2139790449aSratchov size_t home_len, path_len;
2149790449aSratchov int fd, len;
2159790449aSratchov
2169790449aSratchov /* please gcc */
2179790449aSratchov path_len = 0xdeadbeef;
218edc7bd3bSratchov
219edc7bd3bSratchov /*
220edc7bd3bSratchov * try to load the cookie
221edc7bd3bSratchov */
222217983a9Sratchov home = issetugid() ? NULL : getenv("HOME");
223217983a9Sratchov if (home == NULL)
224edc7bd3bSratchov goto bad_gen;
2259790449aSratchov home_len = strlen(home);
2269790449aSratchov path = malloc(home_len + sizeof(COOKIE_SUFFIX));
2279790449aSratchov if (path == NULL)
2289790449aSratchov goto bad_gen;
2299790449aSratchov memcpy(path, home, home_len);
2309790449aSratchov memcpy(path + home_len, COOKIE_SUFFIX, sizeof(COOKIE_SUFFIX));
2319790449aSratchov path_len = home_len + sizeof(COOKIE_SUFFIX) - 1;
232edc7bd3bSratchov fd = open(path, O_RDONLY);
23395d14907Sratchov if (fd == -1) {
234edc7bd3bSratchov if (errno != ENOENT)
235edc7bd3bSratchov DPERROR(path);
236edc7bd3bSratchov goto bad_gen;
237edc7bd3bSratchov }
23895d14907Sratchov if (fstat(fd, &sb) == -1) {
239edc7bd3bSratchov DPERROR(path);
240edc7bd3bSratchov goto bad_close;
241edc7bd3bSratchov }
242edc7bd3bSratchov if (sb.st_mode & 0077) {
243edc7bd3bSratchov DPRINTF("%s has wrong permissions\n", path);
244edc7bd3bSratchov goto bad_close;
245edc7bd3bSratchov }
246edc7bd3bSratchov len = read(fd, cookie, AMSG_COOKIELEN);
24795d14907Sratchov if (len == -1) {
248edc7bd3bSratchov DPERROR(path);
249edc7bd3bSratchov goto bad_close;
250edc7bd3bSratchov }
251edc7bd3bSratchov if (len != AMSG_COOKIELEN) {
252edc7bd3bSratchov DPRINTF("%s: short read\n", path);
253edc7bd3bSratchov goto bad_close;
254edc7bd3bSratchov }
255edc7bd3bSratchov close(fd);
2569790449aSratchov goto done;
257edc7bd3bSratchov bad_close:
258edc7bd3bSratchov close(fd);
259edc7bd3bSratchov bad_gen:
260edc7bd3bSratchov /*
261edc7bd3bSratchov * generate a new cookie
262edc7bd3bSratchov */
263edc7bd3bSratchov arc4random_buf(cookie, AMSG_COOKIELEN);
264edc7bd3bSratchov
265edc7bd3bSratchov /*
266edc7bd3bSratchov * try to save the cookie
267edc7bd3bSratchov */
268096da69dSratchov
269217983a9Sratchov if (home == NULL)
2709790449aSratchov goto done;
2719790449aSratchov tmp = malloc(path_len + sizeof(TEMPL_SUFFIX));
2729790449aSratchov if (tmp == NULL)
2739790449aSratchov goto done;
274096da69dSratchov
275096da69dSratchov /* create ~/.sndio directory */
276096da69dSratchov memcpy(tmp, home, home_len);
277096da69dSratchov memcpy(tmp + home_len, COOKIE_DIR, sizeof(COOKIE_DIR));
27895d14907Sratchov if (mkdir(tmp, 0755) == -1 && errno != EEXIST)
279096da69dSratchov goto done;
280096da69dSratchov
281096da69dSratchov /* create cookie file in it */
2829790449aSratchov memcpy(tmp, path, path_len);
2839790449aSratchov memcpy(tmp + path_len, TEMPL_SUFFIX, sizeof(TEMPL_SUFFIX));
284edc7bd3bSratchov fd = mkstemp(tmp);
285c2d43ecaSderaadt if (fd == -1) {
286edc7bd3bSratchov DPERROR(tmp);
2879790449aSratchov goto done;
288edc7bd3bSratchov }
28995d14907Sratchov if (write(fd, cookie, AMSG_COOKIELEN) == -1) {
290edc7bd3bSratchov DPERROR(tmp);
291edc7bd3bSratchov unlink(tmp);
292edc7bd3bSratchov close(fd);
2939790449aSratchov goto done;
294edc7bd3bSratchov }
295edc7bd3bSratchov close(fd);
29695d14907Sratchov if (rename(tmp, path) == -1) {
297edc7bd3bSratchov DPERROR(tmp);
298edc7bd3bSratchov unlink(tmp);
299edc7bd3bSratchov }
3009790449aSratchov done:
3019790449aSratchov free(tmp);
3029790449aSratchov free(path);
303edc7bd3bSratchov return 1;
304edc7bd3bSratchov }
305edc7bd3bSratchov
306122afd1dSderaadt static int
aucat_connect_tcp(struct aucat * hdl,char * host,unsigned int unit)3077207b069Sratchov aucat_connect_tcp(struct aucat *hdl, char *host, unsigned int unit)
308edc7bd3bSratchov {
309ad38cbdeSratchov int s, error, opt;
310edc7bd3bSratchov struct addrinfo *ailist, *ai, aihints;
311edc7bd3bSratchov char serv[NI_MAXSERV];
312edc7bd3bSratchov
313b3956098Sratchov snprintf(serv, sizeof(serv), "%u", unit + AUCAT_PORT);
314edc7bd3bSratchov memset(&aihints, 0, sizeof(struct addrinfo));
315edc7bd3bSratchov aihints.ai_socktype = SOCK_STREAM;
316edc7bd3bSratchov aihints.ai_protocol = IPPROTO_TCP;
317edc7bd3bSratchov error = getaddrinfo(host, serv, &aihints, &ailist);
318edc7bd3bSratchov if (error) {
319edc7bd3bSratchov DPRINTF("%s: %s\n", host, gai_strerror(error));
320edc7bd3bSratchov return 0;
321edc7bd3bSratchov }
322edc7bd3bSratchov s = -1;
323edc7bd3bSratchov for (ai = ailist; ai != NULL; ai = ai->ai_next) {
32480ae68c4Sguenther s = socket(ai->ai_family, ai->ai_socktype | SOCK_CLOEXEC,
32580ae68c4Sguenther ai->ai_protocol);
32695d14907Sratchov if (s == -1) {
327edc7bd3bSratchov DPERROR("socket");
328edc7bd3bSratchov continue;
329edc7bd3bSratchov }
330900581e9Sratchov restart:
33195d14907Sratchov if (connect(s, ai->ai_addr, ai->ai_addrlen) == -1) {
332900581e9Sratchov if (errno == EINTR)
333900581e9Sratchov goto restart;
334edc7bd3bSratchov DPERROR("connect");
335edc7bd3bSratchov close(s);
336edc7bd3bSratchov s = -1;
337edc7bd3bSratchov continue;
338edc7bd3bSratchov }
339edc7bd3bSratchov break;
340edc7bd3bSratchov }
341edc7bd3bSratchov freeaddrinfo(ailist);
34295d14907Sratchov if (s == -1)
343edc7bd3bSratchov return 0;
344ad38cbdeSratchov opt = 1;
34595d14907Sratchov if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(int)) == -1) {
346ad38cbdeSratchov DPERROR("setsockopt");
347ad38cbdeSratchov close(s);
348ad38cbdeSratchov return 0;
349ad38cbdeSratchov }
350edc7bd3bSratchov hdl->fd = s;
351edc7bd3bSratchov return 1;
352edc7bd3bSratchov }
353edc7bd3bSratchov
354122afd1dSderaadt static int
aucat_connect_un(struct aucat * hdl,unsigned int unit)3557207b069Sratchov aucat_connect_un(struct aucat *hdl, unsigned int unit)
35682bfc72bSratchov {
35782bfc72bSratchov struct sockaddr_un ca;
35882bfc72bSratchov socklen_t len = sizeof(struct sockaddr_un);
35982bfc72bSratchov uid_t uid;
3607e5c91daSratchov int s;
3617e5c91daSratchov
3627e5c91daSratchov uid = geteuid();
3637e5c91daSratchov snprintf(ca.sun_path, sizeof(ca.sun_path),
364dadd32d9Sratchov SOCKPATH_DIR "-%u/" SOCKPATH_FILE "%u", uid, unit);
3657e5c91daSratchov ca.sun_family = AF_UNIX;
36680ae68c4Sguenther s = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
36795d14907Sratchov if (s == -1)
3687e5c91daSratchov return 0;
36995d14907Sratchov while (connect(s, (struct sockaddr *)&ca, len) == -1) {
3707e5c91daSratchov if (errno == EINTR)
3717e5c91daSratchov continue;
3727e5c91daSratchov DPERROR(ca.sun_path);
3737e5c91daSratchov /* try shared server */
3747e5c91daSratchov snprintf(ca.sun_path, sizeof(ca.sun_path),
375dadd32d9Sratchov SOCKPATH_DIR "/" SOCKPATH_FILE "%u", unit);
37695d14907Sratchov while (connect(s, (struct sockaddr *)&ca, len) == -1) {
3777e5c91daSratchov if (errno == EINTR)
3787e5c91daSratchov continue;
3797e5c91daSratchov DPERROR(ca.sun_path);
3807e5c91daSratchov close(s);
3817e5c91daSratchov return 0;
3827e5c91daSratchov }
3837e5c91daSratchov break;
3847e5c91daSratchov }
3857e5c91daSratchov hdl->fd = s;
386e791f9efSratchov DPRINTFN(2, "%s: connected\n", ca.sun_path);
3877e5c91daSratchov return 1;
3887e5c91daSratchov }
3897e5c91daSratchov
390b3956098Sratchov static const char *
parsestr(const char * str,char * rstr,unsigned int max)3917207b069Sratchov parsestr(const char *str, char *rstr, unsigned int max)
392b3956098Sratchov {
393b3956098Sratchov const char *p = str;
394b3956098Sratchov
395b3956098Sratchov while (*p != '\0' && *p != ',' && *p != '/') {
396b3956098Sratchov if (--max == 0) {
397b3956098Sratchov DPRINTF("%s: string too long\n", str);
398b3956098Sratchov return NULL;
399b3956098Sratchov }
400b3956098Sratchov *rstr++ = *p++;
401b3956098Sratchov }
402b3956098Sratchov if (str == p) {
403b3956098Sratchov DPRINTF("%s: string expected\n", str);
404b3956098Sratchov return NULL;
405b3956098Sratchov }
406b3956098Sratchov *rstr = '\0';
407b3956098Sratchov return p;
408b3956098Sratchov }
409b3956098Sratchov
4107e5c91daSratchov int
_aucat_open(struct aucat * hdl,const char * str,unsigned int mode)411163486f8Sratchov _aucat_open(struct aucat *hdl, const char *str, unsigned int mode)
4127e5c91daSratchov {
4137e5c91daSratchov extern char *__progname;
414b3956098Sratchov int eof;
415b3956098Sratchov char host[NI_MAXHOST], opt[AMSG_OPTMAX];
416163486f8Sratchov const char *p;
417163486f8Sratchov unsigned int unit, devnum, type;
41882bfc72bSratchov
419163486f8Sratchov if ((p = _sndio_parsetype(str, "snd")) != NULL)
420163486f8Sratchov type = 0;
421163486f8Sratchov else if ((p = _sndio_parsetype(str, "midithru")) != NULL)
422163486f8Sratchov type = 1;
423163486f8Sratchov else if ((p = _sndio_parsetype(str, "midi")) != NULL)
424163486f8Sratchov type = 2;
425163486f8Sratchov else {
426163486f8Sratchov DPRINTF("%s: unsupported device type\n", str);
427163486f8Sratchov return -1;
428163486f8Sratchov }
429b3956098Sratchov if (*p == '@') {
430b3956098Sratchov p = parsestr(++p, host, NI_MAXHOST);
431b3956098Sratchov if (p == NULL)
432b3956098Sratchov return 0;
433b3956098Sratchov } else
434b3956098Sratchov *host = '\0';
435b3956098Sratchov if (*p == ',') {
4368522ebc4Smiko p = _sndio_parsenum(++p, &unit, 15);
437b3956098Sratchov if (p == NULL)
438b3956098Sratchov return 0;
439b3956098Sratchov } else
440b3956098Sratchov unit = 0;
441163486f8Sratchov if (*p != '/') {
442b3956098Sratchov DPRINTF("%s: '/' expected\n", str);
443edc7bd3bSratchov return 0;
444edc7bd3bSratchov }
44536355b88Sratchov p++;
44636355b88Sratchov if (type == 0) {
447*79256953Sratchov if (*p < '0' || *p > '9') {
44836355b88Sratchov devnum = AMSG_NODEV;
44936355b88Sratchov p = parsestr(p, opt, AMSG_OPTMAX);
450b3956098Sratchov if (p == NULL)
451b3956098Sratchov return 0;
45236355b88Sratchov } else {
45336355b88Sratchov p = _sndio_parsenum(p, &devnum, 15);
454b3956098Sratchov if (p == NULL)
455b3956098Sratchov return 0;
456*79256953Sratchov if (*p == '.') {
457*79256953Sratchov p = parsestr(++p, opt, AMSG_OPTMAX);
458*79256953Sratchov if (p == NULL)
459*79256953Sratchov return 0;
460*79256953Sratchov } else
461*79256953Sratchov strlcpy(opt, "default", AMSG_OPTMAX);
462*79256953Sratchov }
463*79256953Sratchov } else {
464*79256953Sratchov p = _sndio_parsenum(p, &devnum, 15);
465*79256953Sratchov if (p == NULL)
466*79256953Sratchov return 0;
46736355b88Sratchov memset(opt, 0, sizeof(opt));
46836355b88Sratchov }
469b3956098Sratchov if (*p != '\0') {
470b3956098Sratchov DPRINTF("%s: junk at end of dev name\n", p);
47182bfc72bSratchov return 0;
47282bfc72bSratchov }
47387bc9f6aSratchov devnum += type * 16; /* XXX */
474e791f9efSratchov DPRINTFN(2, "_aucat_open: host=%s unit=%u devnum=%u opt=%s\n",
475b3956098Sratchov host, unit, devnum, opt);
476b3956098Sratchov if (host[0] != '\0') {
4778825335aSratchov if (!aucat_connect_tcp(hdl, host, unit))
478edc7bd3bSratchov return 0;
479edc7bd3bSratchov } else {
4808825335aSratchov if (!aucat_connect_un(hdl, unit))
48182bfc72bSratchov return 0;
482edc7bd3bSratchov }
48382bfc72bSratchov hdl->rstate = RSTATE_MSG;
48482bfc72bSratchov hdl->rtodo = sizeof(struct amsg);
48582bfc72bSratchov hdl->wstate = WSTATE_IDLE;
48682bfc72bSratchov hdl->wtodo = 0xdeadbeef;
4875ffd5747Sratchov hdl->maxwrite = 0;
48882bfc72bSratchov
48982bfc72bSratchov /*
49082bfc72bSratchov * say hello to server
49182bfc72bSratchov */
49282bfc72bSratchov AMSG_INIT(&hdl->wmsg);
493b5fec221Sratchov hdl->wmsg.cmd = htonl(AMSG_AUTH);
494edc7bd3bSratchov if (!aucat_mkcookie(hdl->wmsg.u.auth.cookie))
495edc7bd3bSratchov goto bad_connect;
496edc7bd3bSratchov hdl->wtodo = sizeof(struct amsg);
497d418f94bSratchov if (!_aucat_wmsg(hdl, &eof))
498edc7bd3bSratchov goto bad_connect;
499edc7bd3bSratchov AMSG_INIT(&hdl->wmsg);
500b5fec221Sratchov hdl->wmsg.cmd = htonl(AMSG_HELLO);
50182bfc72bSratchov hdl->wmsg.u.hello.version = AMSG_VERSION;
502b5fec221Sratchov hdl->wmsg.u.hello.mode = htons(mode);
503b3956098Sratchov hdl->wmsg.u.hello.devnum = devnum;
5042988007fSratchov hdl->wmsg.u.hello.id = htonl(getpid());
50582bfc72bSratchov strlcpy(hdl->wmsg.u.hello.who, __progname,
50682bfc72bSratchov sizeof(hdl->wmsg.u.hello.who));
50782bfc72bSratchov strlcpy(hdl->wmsg.u.hello.opt, opt,
50882bfc72bSratchov sizeof(hdl->wmsg.u.hello.opt));
50982bfc72bSratchov hdl->wtodo = sizeof(struct amsg);
510d418f94bSratchov if (!_aucat_wmsg(hdl, &eof))
51182bfc72bSratchov goto bad_connect;
51282bfc72bSratchov hdl->rtodo = sizeof(struct amsg);
513d418f94bSratchov if (!_aucat_rmsg(hdl, &eof)) {
51482bfc72bSratchov DPRINTF("aucat_init: mode refused\n");
51582bfc72bSratchov goto bad_connect;
51682bfc72bSratchov }
517b5fec221Sratchov if (ntohl(hdl->rmsg.cmd) != AMSG_ACK) {
51882bfc72bSratchov DPRINTF("aucat_init: protocol err\n");
51982bfc72bSratchov goto bad_connect;
52082bfc72bSratchov }
52182bfc72bSratchov return 1;
52282bfc72bSratchov bad_connect:
52395d14907Sratchov while (close(hdl->fd) == -1 && errno == EINTR)
52482bfc72bSratchov ; /* retry */
52582bfc72bSratchov return 0;
52682bfc72bSratchov }
52782bfc72bSratchov
52882bfc72bSratchov void
_aucat_close(struct aucat * hdl,int eof)529d418f94bSratchov _aucat_close(struct aucat *hdl, int eof)
53082bfc72bSratchov {
53114d6d4a2Sratchov char dummy[sizeof(struct amsg)];
53214d6d4a2Sratchov ssize_t n;
53382bfc72bSratchov
53482bfc72bSratchov if (!eof) {
53582bfc72bSratchov AMSG_INIT(&hdl->wmsg);
536b5fec221Sratchov hdl->wmsg.cmd = htonl(AMSG_BYE);
53782bfc72bSratchov hdl->wtodo = sizeof(struct amsg);
538d418f94bSratchov if (!_aucat_wmsg(hdl, &eof))
53982bfc72bSratchov goto bad_close;
54014d6d4a2Sratchov
54114d6d4a2Sratchov /*
54214d6d4a2Sratchov * block until the peer disconnects
54314d6d4a2Sratchov */
54414d6d4a2Sratchov while (1) {
54514d6d4a2Sratchov n = read(hdl->fd, dummy, sizeof(dummy));
54695d14907Sratchov if (n == -1) {
54714d6d4a2Sratchov if (errno == EINTR)
54814d6d4a2Sratchov continue;
54914d6d4a2Sratchov break;
55014d6d4a2Sratchov }
55114d6d4a2Sratchov if (n == 0)
55214d6d4a2Sratchov break;
55314d6d4a2Sratchov }
55482bfc72bSratchov }
55582bfc72bSratchov bad_close:
55695d14907Sratchov while (close(hdl->fd) == -1 && errno == EINTR)
55782bfc72bSratchov ; /* nothing */
55882bfc72bSratchov }
55982bfc72bSratchov
56082bfc72bSratchov int
_aucat_setfl(struct aucat * hdl,int nbio,int * eof)561d418f94bSratchov _aucat_setfl(struct aucat *hdl, int nbio, int *eof)
56282bfc72bSratchov {
56395d14907Sratchov if (fcntl(hdl->fd, F_SETFL, nbio ? O_NONBLOCK : 0) == -1) {
564d418f94bSratchov DPERROR("_aucat_setfl: fcntl");
56582bfc72bSratchov *eof = 1;
56682bfc72bSratchov return 0;
56782bfc72bSratchov }
56882bfc72bSratchov return 1;
56982bfc72bSratchov }
57082bfc72bSratchov
57182bfc72bSratchov int
_aucat_pollfd(struct aucat * hdl,struct pollfd * pfd,int events)572d418f94bSratchov _aucat_pollfd(struct aucat *hdl, struct pollfd *pfd, int events)
57382bfc72bSratchov {
57482bfc72bSratchov if (hdl->rstate == RSTATE_MSG)
57582bfc72bSratchov events |= POLLIN;
57682bfc72bSratchov pfd->fd = hdl->fd;
57782bfc72bSratchov pfd->events = events;
57882bfc72bSratchov return 1;
57982bfc72bSratchov }
58082bfc72bSratchov
58182bfc72bSratchov int
_aucat_revents(struct aucat * hdl,struct pollfd * pfd)582d418f94bSratchov _aucat_revents(struct aucat *hdl, struct pollfd *pfd)
58382bfc72bSratchov {
58482bfc72bSratchov int revents = pfd->revents;
58582bfc72bSratchov
586d418f94bSratchov DPRINTFN(2, "_aucat_revents: revents: %x\n", revents);
58782bfc72bSratchov return revents;
58882bfc72bSratchov }
589