1 /*
2  * Copyright (c) 2002, 2003 MATPOCKuH.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24 
25 #include <sys/types.h>
26 #include <sys/cdio.h>
27 #include <sys/ioctl.h>
28 #include <sys/stat.h>
29 #include <sys/socket.h>
30 #include <sys/un.h>
31 #include <sys/wait.h>
32 #include <sys/param.h>
33 #include <sys/ucred.h>
34 #include <sys/mount.h>
35 #include <errno.h>
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <fcntl.h>
40 #include <fstab.h>
41 #include <unistd.h>
42 #include <pwd.h>
43 #include <grp.h>
44 #include <signal.h>
45 #include <stdarg.h>
46 #include <ctype.h>
47 #include <sysexits.h>
48 
49 #ifdef HAVE_CONFIG_H
50 #include <config.h>
51 #endif
52 
53 #ifdef HAVE_SYS_CDRIO_H
54 #include <sys/cdrio.h>
55 #endif
56 
57 #include "settings.h"
58 #include "mqueue.h"
59 #include "logger.h"
60 #include "acd.h"
61 #include "version.h"
62 
63 #define autocd_opendev(_dev) (_dev->dev = _dev->dev == -1 ?\
64     open(_dev->device, O_RDONLY) : _dev->dev)
65 
66 typedef struct {
67     void *next;
68     void *prev;
69     char *device, *mnt_point, *mnt_fstype, *mnt_opts;
70     int dev, ejected, disabled;
71 #ifdef HAVE_SYS_CDRIO_H
72     int locked_speed;
73 #endif
74     int first_audio_track, last_audio_track;
75     u_int cdid;
76     struct cd_sub_channel_info sub_channel_info;
77     struct ioc_toc_header toc_header;
78     struct cd_toc_entry toc_entry[MAXTRACKS];
79     struct ioc_read_subchannel read_subchannel;
80     struct ioc_read_toc_entry read_toc_entry;
81     struct ioc_play_msf play_msf;
82 } device_t;
83 
84 typedef struct {
85     void *next;
86     void *prev;
87     int fd;
88     device_t *device;
89 } client_t;
90 
91 typedef struct {
92     void *next;
93     void *prev;
94     u_int cdid;
95     struct ioc_play_msf play_msf;
96 } saved_msf_t;
97 
98 typedef int handler_t(client_t *);
99 typedef int handler_wp_t(client_t *, char *);
100 
101 int autocd_close(client_t *);
102 int autocd_cdid(client_t *);
103 int autocd_disable(client_t *);
104 int autocd_die(client_t *);
105 int autocd_eject(client_t *);
106 int autocd_enable(client_t *);
107 int autocd_help(client_t *);
108 int autocd_info(client_t *);
109 int autocd_listdevices(client_t *);
110 int autocd_listsaved(client_t *);
111 int autocd_load(client_t *);
112 int autocd_next(client_t *);
113 int autocd_play(client_t *);
114 int autocd_pause(client_t *);
115 int autocd_previous(client_t *);
116 int autocd_closeclient(client_t *);
117 int autocd_stat(client_t *);
118 int autocd_save(client_t *);
119 int autocd_stop(client_t *);
120 int autocd_version(client_t *);
121 int autocd_volume(client_t *);
122 int autocd_changedevice(client_t *, char *);
123 int autocd_play_with_params(client_t *, char *);
124 #ifdef HAVE_SYS_CDRIO_H
125 int autocd_speed(client_t *, char *);
126 int autocd_lockspeed(client_t *, char *);
127 #endif
128 int autocd_changevolume(client_t *, char *);
129 
130 struct {
131     char *cmd, *params;
132     handler_t *handler;
133     int need_opendev;
134     int need_disc;
135     int need_play;
136 } cmds[] = {
137     {"Close", NULL, &autocd_close, 1, 0, 0},
138     {"CDid", NULL, &autocd_cdid, 0, 1, 0},
139     {"Disable", NULL, &autocd_disable, 0, 0, 0},
140     {"DIE", NULL, &autocd_die, 0, 0, 0},
141     {"Eject", NULL, &autocd_eject, 0, 0, 0},
142     {"Enable", NULL, &autocd_enable, 0, 0, 0},
143     {"Help", NULL, &autocd_help, 0, 0, 0},
144     {"Info", NULL, &autocd_info, 0, 1, 0},
145     {"List", NULL, &autocd_listdevices, 0, 0, 0},
146     {"LISTSaved", NULL, &autocd_listsaved, 0, 0, 0},
147     {"LOad", NULL, &autocd_load, 1, 1, 0},
148     {"Next", NULL, &autocd_next, 1, 1, 1},
149     {"Play", NULL, &autocd_play, 1, 1, 0},
150     {"PAuse", NULL, &autocd_pause, 1, 1, 1},
151     {"PRevious", NULL, &autocd_previous, 1, 1, 1},
152     {"Quit", NULL, &autocd_closeclient, 0, 0, 0},
153     {"Status", NULL, &autocd_stat, 0, 0, 0},
154     {"SAve", NULL, &autocd_save, 0, 1, 1},
155     {"STOp", NULL, &autocd_stop, 1, 1, 1},
156     {"Version", NULL, &autocd_version, 0, 0, 0},
157     {"VOlume", NULL, &autocd_volume, 1, 0, 0},
158     {"Device", "device", (handler_t *) &autocd_changedevice, 0, 0, 0},
159 #ifdef HAVE_SYS_CDRIO_H
160     {"Lockspeed", "speed", (handler_t *) &autocd_lockspeed, 0, 0, 0},
161 #endif
162     {"Play", "first_track [last_track]",
163 	(handler_t *) &autocd_play_with_params, 1, 1, 0},
164 #ifdef HAVE_SYS_CDRIO_H
165     {"Speed", "speed", (handler_t *) &autocd_speed, 1, 0, 0},
166 #endif
167     {"Volume", "[+|-]volume", (handler_t *) &autocd_changevolume, 1, 0, 0},
168     {NULL, NULL, NULL, 0, 0, 0}
169 };
170 
171 mq_queue_t *devices, *clients, *saved_msfs;
172 fd_set read_fds;
173 
174 uid_t socket_owner = SOCKET_OWNER;
175 gid_t socket_group = SOCKET_GROUP;
176 mode_t socket_mode = SOCKET_MODE;
177 int socket_fd, dont_close_while_playing = 0;
178 
179 char *audio_status[] =
180     {"invalid",
181 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
182 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
183 	"playing", "paused", "completed",
184 	"error", "no status"};
185 
186 static volatile sig_atomic_t nobreak = 1;
187 
188 /*
189  * dbprog_sum
190  *      Convert an integer to its text string representation, and
191  *      compute its checksum.  Used by dbprog_discid to derive the
192  *      disc ID.
193  *
194  * Args:
195  *      n - The integer value.
196  *
197  * Return:
198  *      The integer checksum.
199  */
200 int
dbprog_sum(int n)201 dbprog_sum(int n)
202 {
203     char buf[12], *p;
204     int ret = 0;
205 
206     /* For backward compatibility this algorithm must not change */
207     sprintf(buf, "%u", n);
208     for (p = buf; *p != '\0'; p++)
209 	ret += (*p - '0');
210 
211     return(ret);
212 }
213 
214 /*
215  * dbprog_discid
216  *      Compute a magic disc ID based on the number of tracks,
217  *      the length of each track, and a checksum of the string
218  *      that represents the offset of each track.
219  *
220  * Args:
221  *      s - Pointer to the curstat_t structure.
222  *
223  * Return:
224  *      The integer disc ID.
225  */
226 u_int
dbprog_discid(device_t * device)227 dbprog_discid(device_t *device)
228 {
229     int i, ntr, t = 0, n = 0;
230 
231     ntr = device->toc_header.ending_track -
232 	device->toc_header.starting_track + 1;
233 
234     /* For backward compatibility this algorithm must not change */
235     for (i = 0; i < ntr; i++) {
236 #define TC_MM(a) device->toc_entry[a].addr.msf.minute
237 #define TC_SS(a) device->toc_entry[a].addr.msf.second
238 	n += dbprog_sum((TC_MM(i) * 60) + TC_SS(i));
239 
240 	t += ((TC_MM(i+1) * 60) + TC_SS(i+1)) - ((TC_MM(i) * 60) + TC_SS(i));
241     }
242 
243     return((n % 0xff) << 24 | t << 8 | ntr);
244 }
245 
246 void
autocd_init()247 autocd_init()
248 {
249     devices = mq_makequeue(sizeof(device_t));
250     clients = mq_makequeue(sizeof(client_t));
251     saved_msfs = mq_makequeue(sizeof(saved_msf_t));
252     FD_ZERO(&read_fds);
253 }
254 
255 void
autocd_setsocketpath(const char * spath)256 autocd_setsocketpath(const char *spath)
257 {
258     if(socket_path != SOCKET_PATH)
259 	free(socket_path);
260 
261     socket_path = strdup(spath);
262 }
263 
264 int
autocd_setsocketowner(const char * owner)265 autocd_setsocketowner(const char *owner)
266 {
267     if(sscanf(owner, "%i", &socket_owner) == 0) {
268 	struct passwd *passwd;
269 
270 	if(!(passwd = getpwnam(owner))) {
271 	    logger("Failed to get uid for %s: %s\n", owner, strerror(errno));
272 	    return -1;
273 	}
274 
275 	socket_owner = passwd->pw_uid;
276 
277 	endpwent();
278     }
279 
280     return 0;
281 }
282 
283 int
autocd_setsocketgroup(const char * group)284 autocd_setsocketgroup(const char *group)
285 {
286     if(sscanf(group, "%i", &socket_group) == 0) {
287 	struct group *gr;
288 
289 	if(!(gr = getgrnam(group))) {
290 	    logger("Failed to get gid for %s: %s\n", group, strerror(errno));
291 	    return -1;
292 	}
293 
294 	socket_group = gr->gr_gid;
295 
296 	endgrent();
297     }
298 
299     return 0;
300 }
301 
302 int
autocd_setsocketmode(const char * mode)303 autocd_setsocketmode(const char *mode)
304 {
305     unsigned int tmp;
306 
307     sscanf(mode, "%o", &tmp);
308     socket_mode = tmp;
309 
310     return 0;
311 }
312 
313 int
autocd_initdevice(char * device)314 autocd_initdevice(char *device)
315 {
316     device_t *tmp;
317     struct stat st;
318     char *dev;
319 
320     if(device[0] != '/')
321 	asprintf(&dev, "/dev/%s", device);
322     else
323 	dev = strdup(device);
324 
325     if(stat(dev, &st) == -1) {
326 	logger("Failed to use device %s: %s\n", dev, strerror(errno));
327 	free(dev);
328 	return -1;
329     }
330 
331     if(!(st.st_mode & S_IFBLK) && !(st.st_mode & S_IFCHR)) {
332 	logger("%s is not a character or block special file\n", dev);
333 	free(dev);
334 	return -1;
335     }
336 
337     tmp = malloc(sizeof(device_t));
338     bzero(tmp, sizeof(*tmp));
339 
340     tmp->read_subchannel.address_format = CD_MSF_FORMAT;
341     tmp->read_subchannel.data_format = CD_CURRENT_POSITION;
342     tmp->read_subchannel.data_len = sizeof(struct cd_sub_channel_info);
343     tmp->read_subchannel.data = &tmp->sub_channel_info;
344 
345     tmp->read_toc_entry.address_format = CD_MSF_FORMAT;
346     tmp->read_toc_entry.starting_track = 1;
347     tmp->read_toc_entry.data_len = sizeof(tmp->toc_entry);
348     tmp->read_toc_entry.data = tmp->toc_entry;
349 
350     if((tmp->dev = open(dev, O_RDONLY)) == -1) {
351 	if((errno != ENXIO) && (errno != EIO) && (errno != ENODEV)) {
352 	    logger("Failed to open device %s: %s\n", dev, strerror(errno));
353 	    free(dev);
354 	    free(tmp);
355 	    return -1;
356 	}
357     } else {
358 	if(ioctl(tmp->dev, CDIOCREADSUBCHANNEL, &tmp->read_subchannel) == -1) {
359 	    if((errno != EBUSY) && (errno != EIO) && (errno != ENXIO)) {
360 		logger("Failed to use device %s: %s\n", dev, strerror(errno));
361 		free(dev);
362 		free(tmp);
363 		return -1;
364 	    }
365 	}
366 
367 	close(tmp->dev);
368 	tmp->dev = -1;
369     }
370 
371     if(geteuid()) {
372 	logger("Not running from root. Automount disabled for %s.\n", dev);
373 	tmp->mnt_point = NULL;
374     } else {
375 	struct fstab *fstab;
376 
377 	if(!(fstab = getfsspec(dev))) {
378 	    tmp->mnt_point = NULL;
379 	    tmp->mnt_fstype = FSTYPE;
380 	   tmp->mnt_opts = MNTOPTS;
381 	} else {
382 	    tmp->mnt_point = strdup(fstab->fs_file);
383 	    tmp->mnt_fstype = strdup(fstab->fs_vfstype);
384 	    tmp->mnt_opts = strdup(fstab->fs_mntops);
385 	}
386 
387 	endfsent();
388     }
389 
390     tmp->device = dev;
391     tmp->ejected = 1;
392     tmp->disabled = 0;
393 
394 #ifdef HAVE_SYS_CDRIO_H
395     tmp->locked_speed = 0;
396 #endif
397 
398     mq_addrecord(devices, (mq_record_t *) tmp);
399 
400     return 0;
401 }
402 
403 int
autocd_noclose()404 autocd_noclose()
405 {
406     dont_close_while_playing = 1;
407 
408     return 0;
409 }
410 
411 int
autocd_set_mnt_point(const char * mnt_point)412 autocd_set_mnt_point(const char *mnt_point)
413 {
414     device_t *d;
415 
416     if(geteuid()) {
417 	logger("Not running from root. Mount is not avaible.\n");
418 	return -1;
419     }
420 
421     if(!(d = (device_t *) mq_gettail(devices))) {
422 	logger("Strange call autocd_set_mnt_point()\n");
423 	return -1;
424     }
425 
426     if(d->mnt_point)
427 	free(d->mnt_point);
428 
429     d->mnt_point = strdup(mnt_point);
430 
431     return 0;
432 }
433 
434 int
autocd_set_mnt_fstype(const char * mnt_fstype)435 autocd_set_mnt_fstype(const char *mnt_fstype)
436 {
437     device_t *d;
438 
439     if(!(d = (device_t *) mq_gettail(devices))) {
440 	logger("Strange call autocd_set_mnt_fstype()\n");
441 	return -1;
442     }
443 
444     if(!d->mnt_point) {
445 	logger("No mount point for %s. fstype ignored.\n", d->device);
446 	return -1;
447     }
448 
449     if(d->mnt_fstype != FSTYPE)
450 	free(d->mnt_fstype);
451 
452     d->mnt_fstype = strdup(mnt_fstype);
453 
454     return 0;
455 }
456 
457 int
autocd_set_mnt_opts(const char * mnt_opts)458 autocd_set_mnt_opts(const char *mnt_opts)
459 {
460     device_t *d;
461 
462     if(!(d = (device_t *) mq_gettail(devices))) {
463 	logger("Strange call autocd_set_mnt_opts()\n");
464 	return -1;
465     }
466 
467     if(!d->mnt_point) {
468 	logger("No mount point for %s. Mount options ignored.\n", d->device);
469 	return -1;
470     }
471 
472     if(d->mnt_opts != MNTOPTS)
473 	free(d->mnt_opts);
474 
475     d->mnt_opts = strdup(mnt_opts);
476 
477     return 0;
478 }
479 
480 int
autocd_initsocket()481 autocd_initsocket()
482 {
483     struct sockaddr_un unix_addr;
484     struct stat st;
485     int fd;
486 
487     if(stat(socket_path, &st)) {
488 	if(errno != ENOENT) {
489 	    logger("Failed to create socket: %s\n", strerror(errno));
490 	    return -1;
491 	}
492     } else {
493 	if((st.st_mode & S_IFSOCK) != S_IFSOCK) {
494 	    logger("Failed to create socket: %s is not socket\n", socket_path);
495 	    return -1;
496 	}
497 
498 	if((fd = acd_connect()) == -1) {
499 	    if(errno != ECONNREFUSED) {
500 		logger("Failed to connect to another daemon: %s\n",
501 		    strerror(errno));
502 		return -1;
503 	    }
504 	} else {
505 	    logger("Found running copy of daemon\n");
506 	    close(fd);
507 	    return -1;
508 	}
509 
510 	if(unlink(socket_path) == -1) {
511 	    logger("Failed to remove stale socket: %s\n", strerror(errno));
512 	    return -1;
513 	}
514 
515 	logger("Removed stale socket.\n");
516     }
517 
518     if((socket_fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
519 	return -1;
520 
521     memset(&unix_addr, 0, sizeof(unix_addr));
522     unix_addr.sun_family = AF_UNIX;
523     strncpy(unix_addr.sun_path, socket_path, sizeof(unix_addr.sun_path));
524     unix_addr.sun_len = sizeof(unix_addr.sun_len) +
525 	sizeof(unix_addr.sun_family) + strlen(unix_addr.sun_path) + 1;
526 
527     if(bind(socket_fd, (struct sockaddr *) &unix_addr, unix_addr.sun_len) < 0) {
528 	logger("Failed to create socket: %s\n", strerror(errno));
529 	close(socket_fd);
530 	unlink(socket_path);
531 	return -1;
532     }
533 
534     if(listen(socket_fd, 5) < 0) {
535 	logger("Failed to create socket: %s\n", strerror(errno));
536 	close(socket_fd);
537 	unlink(socket_path);
538 	return -1;
539     }
540 
541     if(!geteuid()) {
542 	chown(socket_path, socket_owner, socket_group);
543     }
544 
545     chmod(socket_path, socket_mode);
546 
547     FD_SET(socket_fd, &read_fds);
548 
549     return 0;
550 }
551 
552 void
autocd_accept()553 autocd_accept()
554 {
555     int fd;
556     client_t *client;
557     struct sockaddr_un unix_addr;
558     size_t len;
559 
560     len = sizeof(unix_addr);
561     if ((fd = accept(socket_fd, (struct sockaddr *) &unix_addr, &len)) < 0) {
562 	logger("accept() failed: %s\n", strerror(errno));
563 	return;
564     }
565 
566     client = (client_t *) mq_addnewrecord(clients);
567     client->fd = fd;
568     client->device = (device_t *) mq_gethead(devices);
569 
570     FD_SET(fd, &read_fds);
571 }
572 
573 int
autocd_play_msf(device_t * device)574 autocd_play_msf(device_t *device)
575 {
576     if(ioctl(device->dev, CDIOCPLAYMSF, &device->play_msf) == -1) {
577 	logger("CDIOCPLAYMSF failed: %s\n", strerror(errno));
578 	return -1;
579     }
580 
581     device->sub_channel_info.header.audio_status = CD_AS_PLAY_IN_PROGRESS;
582 
583     return 0;
584 }
585 
586 int
autocd_playtracks(device_t * device,int ftrack,int ltrack)587 autocd_playtracks(device_t *device, int ftrack, int ltrack)
588 {
589     union msf_lba *msf_lba;
590 
591     msf_lba = &device->toc_entry[ftrack - 1].addr;
592     device->play_msf.start_m = msf_lba->msf.minute;
593     device->play_msf.start_s = msf_lba->msf.second;
594     device->play_msf.start_f = msf_lba->msf.frame;
595 
596     msf_lba = &device->toc_entry[ltrack].addr;
597     device->play_msf.end_m = msf_lba->msf.minute;
598     device->play_msf.end_s = msf_lba->msf.second;
599     device->play_msf.end_f = msf_lba->msf.frame;
600 
601     return autocd_play_msf(device);
602 }
603 
604 void
autocd_closedev(device_t * device)605 autocd_closedev(device_t *device)
606 {
607     if(device->dev == -1)
608 	return;
609 
610     if(dont_close_while_playing &&
611 	((device->sub_channel_info.header.audio_status ==
612 	    CD_AS_PLAY_IN_PROGRESS) ||
613 	(device->sub_channel_info.header.audio_status == CD_AS_PLAY_PAUSED)))
614 	return;
615 
616     close(device->dev);
617     device->dev = -1;
618 }
619 
620 void
autocd_doinsert(device_t * device)621 autocd_doinsert(device_t *device)
622 {
623     device->read_toc_entry.data_len =
624 	(device->toc_header.ending_track + 1) * sizeof(struct cd_toc_entry);
625 
626     if(ioctl(device->dev, CDIOREADTOCENTRYS, &device->read_toc_entry) == -1) {
627 	logger("CDIOREADTOCENTRYS failed: %s\n", strerror(errno));
628 	return;
629     }
630 
631     for(device->first_audio_track = 1;
632 	(device->toc_entry[device->first_audio_track - 1].control & 4) &&
633 	(device->first_audio_track < device->toc_header.ending_track);
634 	device->first_audio_track++);
635 
636     for(device->last_audio_track = device->toc_header.ending_track;
637 	(device->toc_entry[device->last_audio_track - 1].control & 4) &&
638 	(device->first_audio_track < device->last_audio_track);
639 	device->last_audio_track--);
640 
641     if(device->toc_entry[device->first_audio_track - 1].control & 4) {
642 	if(device->mnt_point) {
643 	    int status;
644 
645 	    autocd_closedev(device);
646 
647 	    switch(fork()) {
648 	    case 0:
649 		execl(MOUNT, MOUNT, "-o", device->mnt_opts,
650 		    "-t", device->mnt_fstype,
651 		    device->device, device->mnt_point, NULL);
652 
653 
654 		logger("Can't run "MOUNT": %s\n", strerror(errno));
655 
656 		exit(EX_OSERR);
657 
658 	    case -1:
659 		logger("fork() failed: %s\n", strerror(errno));
660 		break;
661 
662 	    default:
663 		wait(&status);
664 	    }
665 
666 	    autocd_opendev(device);
667 	}
668     } else {
669 	if(device->sub_channel_info.header.audio_status !=
670 	    CD_AS_PLAY_IN_PROGRESS) {
671 	    autocd_playtracks(device,
672 		device->first_audio_track, device->last_audio_track);
673 	}
674     }
675 
676     device->cdid = dbprog_discid(device);
677 
678 #ifdef HAVE_SYS_CDRIO_H
679     if(device->locked_speed != 0)
680 	if(ioctl(device->dev, CDRIOCREADSPEED, &device->locked_speed) == -1)
681 	    device->locked_speed = 0;
682 #endif
683 
684 }
685 
686 void
autocd_checkdevice(device_t * dev)687 autocd_checkdevice(device_t *dev)
688 {
689     if(dev->disabled)
690 	return;
691 
692     if(autocd_opendev(dev) == -1) {
693 	dev->ejected = 1;
694 	return;
695     }
696 
697     if(ioctl(dev->dev, CDIOCREADSUBCHANNEL, &dev->read_subchannel) == -1) {
698 	dev->ejected = 1;
699     } else {
700 	if(ioctl(dev->dev, CDIOREADTOCHEADER, &dev->toc_header) == -1) {
701 	    dev->ejected = 1;
702 	} else {
703 	    if(dev->ejected) {
704 		dev->ejected = 0;
705 		bzero(&dev->play_msf, sizeof(dev->play_msf));
706 		autocd_doinsert(dev);
707 	    }
708 	}
709     }
710 
711     autocd_closedev(dev);
712 }
713 
714 void
autocd_vcprintf(client_t * client,char * fmt,va_list ap)715 autocd_vcprintf(client_t *client, char *fmt, va_list ap)
716 {
717     char *buf;
718 
719     vasprintf(&buf, fmt, ap);
720     write(client->fd, buf, strlen(buf));
721     free(buf);
722 }
723 
724 void
autocd_cprintf(client_t * client,char * fmt,...)725 autocd_cprintf(client_t *client, char *fmt, ...)
726 {
727     va_list ap;
728 
729     va_start(ap, fmt);
730     autocd_vcprintf(client, fmt, ap);
731     va_end(ap);
732 }
733 
734 void
autocd_ERROR(client_t * client,char * fmt,...)735 autocd_ERROR(client_t *client, char *fmt, ...)
736 {
737     va_list ap;
738 
739     va_start(ap, fmt);
740     vlogger(fmt, ap);
741     autocd_vcprintf(client, fmt, ap);
742     va_end(ap);
743 }
744 
745 int
autocd_eject(client_t * client)746 autocd_eject(client_t *client)
747 {
748     device_t *device = client->device;
749     int rc = 0;
750 
751     if(!geteuid()) {
752 	int i, size;
753 	struct statfs *fs;
754 
755 	size = sizeof(struct statfs) * (i = getfsstat(NULL, 0, MNT_NOWAIT));
756 	fs = malloc(size);
757 	getfsstat(fs, size, MNT_NOWAIT);
758 
759 	for(; i; i--) {
760 	    if(!strcmp(device->device, fs[i - 1].f_mntfromname)) {
761 		if(unmount(fs[i - 1].f_mntonname, 0) == -1) {
762 		    autocd_ERROR(client, "ERROR: unmount() failed: %s\n",
763 			strerror(errno));
764 		    return -2;
765 		}
766 		break;
767 	    }
768 	}
769 
770 	free(fs);
771     }
772 
773     if(autocd_opendev(client->device) == -1)
774 	return -1;
775 
776     if(ioctl(device->dev, CDIOCALLOW) == -1)
777 	logger("ioctl(CDIOCALLOW) failed: %s\n", strerror(errno));
778 
779     if(ioctl(device->dev, CDIOCEJECT) == -1) {
780 	autocd_ERROR(client, "ERROR: ioctl(CDIOCEJECT) failed: %s\n",
781 	    strerror(errno));
782 	rc = -2;
783     }
784 
785     autocd_closedev(client->device);
786 
787     return rc;
788 }
789 
790 int
autocd_pause(client_t * client)791 autocd_pause(client_t *client)
792 {
793     device_t *device = client->device;
794     int rcode = 0;
795 
796     switch(device->sub_channel_info.header.audio_status) {
797     case CD_AS_PLAY_IN_PROGRESS:
798 	rcode = ioctl(device->dev, CDIOCPAUSE);
799 	break;
800 
801     case CD_AS_PLAY_PAUSED:
802 	rcode = ioctl(device->dev, CDIOCRESUME);
803 	break;
804     }
805 
806     if(rcode == -1)
807 	return -1;
808 
809     return 0;
810 }
811 
812 int
autocd_stop(client_t * client)813 autocd_stop(client_t *client)
814 {
815     device_t *device = client->device;
816 
817     if(ioctl(device->dev, CDIOCSTOP) == -1)
818 	return -1;
819 
820     return 0;
821 }
822 
823 int
autocd_close(client_t * client)824 autocd_close(client_t *client)
825 {
826     device_t *device = client->device;
827 
828     if(ioctl(device->dev, CDIOCALLOW) == -1)
829 	logger("ioctl(CDIOCALLOW) failed: %s\n", strerror(errno));
830 
831     if(ioctl(device->dev, CDIOCCLOSE) == -1)
832 	return -1;
833 
834     return 0;
835 }
836 
837 int
autocd_stat(client_t * client)838 autocd_stat(client_t *client)
839 {
840     struct cd_sub_channel_info *s_ch_info = &client->device->sub_channel_info;
841     u_char audiostatus = s_ch_info->header.audio_status;
842 
843     if(client->device->disabled) {
844 	autocd_cprintf(client, "Device disabled\n");
845 	return 0;
846     }
847 
848     if(client->device->ejected) {
849 	autocd_cprintf(client, "No current status info available\n");
850 	return 0;
851     }
852 
853     autocd_cprintf(client,
854 	"Audio status = %i<%s>, current track = %i,\
855  current position = %i:%02i.%02i\n",
856 	audiostatus,
857 	audiostatus <= CD_AS_NO_STATUS ? audio_status[audiostatus] : NULL,
858 	s_ch_info->what.position.track_number,
859 	s_ch_info->what.position.reladdr.msf.minute,
860 	s_ch_info->what.position.reladdr.msf.second,
861 	s_ch_info->what.position.reladdr.msf.frame);
862 
863     return 0;
864 }
865 
866 int
autocd_info(client_t * client)867 autocd_info(client_t *client)
868 {
869     device_t *dev = client->device;
870     int i, ftrack, ltrack;
871     int trackb, tracke, len;
872     char slen[11], blen[9], stype[8];
873 
874     autocd_cprintf(client,
875 	"Starting track = %i, ending track = %i, TOC size = %i bytes\n\
876 track     start  duration   block  length   type\n\
877 -------------------------------------------------\n",
878 	ftrack = dev->toc_header.starting_track,
879 	ltrack = dev->toc_header.ending_track, dev->toc_header.len);
880 
881     trackb = tracke = 75 * (60 * dev->toc_entry[0].addr.msf.minute +
882 	dev->toc_entry[0].addr.msf.second) +
883 	dev->toc_entry[0].addr.msf.frame;
884 
885     for(i = 0; i <= ltrack; i++) {
886 	if(dev->toc_entry[i].track != MAXTRACKS) {
887 	    len = (tracke = 75 * (60 * dev->toc_entry[i + 1].addr.msf.minute +
888 		dev->toc_entry[i + 1].addr.msf.second) +
889 		dev->toc_entry[i + 1].addr.msf.frame) - trackb;
890 	    snprintf(slen, sizeof(slen), "%4i:%02i.%02i",
891 		len / (75 * 60), (len / 75) % 60, len % 75);
892 	    snprintf(blen, sizeof(blen), "%8i", len);
893 	    snprintf(stype, sizeof(stype), "%7s",
894 		(dev->toc_entry[i].control & 4) ? "data" : "audio");
895 	} else {
896 	    snprintf(slen, sizeof(slen), "%10s", "-");
897 	    snprintf(blen, sizeof(blen), "%8s", "-");
898 	    snprintf(stype, sizeof(stype), "%7s", "-");
899 	}
900 
901 	autocd_cprintf(client,
902 	    "%5i%4i:%02i.%02i%s%8i%s%s\n",
903 	    dev->toc_entry[i].track,
904 	    dev->toc_entry[i].addr.msf.minute,
905 	    dev->toc_entry[i].addr.msf.second,
906 	    dev->toc_entry[i].addr.msf.frame,
907 	    slen, trackb - 2 * 75, blen, stype);
908 
909 	trackb = tracke;
910     }
911 
912     return 0;
913 }
914 
915 int
autocd_play(client_t * client)916 autocd_play(client_t *client)
917 {
918     device_t *device = client->device;
919 
920     if(autocd_playtracks(device,
921 	device->first_audio_track, device->last_audio_track) == -1)
922 	return -1;
923 
924     return 0;
925 }
926 
927 int
autocd_next(client_t * client)928 autocd_next(client_t *client)
929 {
930     device_t *device = client->device;
931     u_char track;
932 
933     if((track = device->sub_channel_info.what.position.track_number) >=
934 	device->last_audio_track) {
935 	autocd_cprintf(client, "ERROR: this is last track\n");
936 	return -2;
937     }
938 
939     device->play_msf.start_m = device->toc_entry[track].addr.msf.minute;
940     device->play_msf.start_s = device->toc_entry[track].addr.msf.second;
941     device->play_msf.start_f = device->toc_entry[track].addr.msf.frame;
942 
943     if(autocd_play_msf(device) == -1)
944 	return -1;
945 
946     return 0;
947 }
948 
949 int
autocd_previous(client_t * client)950 autocd_previous(client_t *client)
951 {
952     device_t *device = client->device;
953     int track;
954 
955     if((track = device->sub_channel_info.what.position.track_number - 2) <
956 	(device->first_audio_track - 1)) {
957 	autocd_cprintf(client, "ERROR: this is first track\n");
958 	return -2;
959     }
960 
961     device->play_msf.start_m = device->toc_entry[track].addr.msf.minute;
962     device->play_msf.start_s = device->toc_entry[track].addr.msf.second;
963     device->play_msf.start_f = device->toc_entry[track].addr.msf.frame;
964 
965     if(autocd_play_msf(device) == -1)
966 	return -1;
967 
968     return 0;
969 }
970 
971 int
autocd_play_with_params(client_t * client,char * params)972 autocd_play_with_params(client_t *client, char *params)
973 {
974     device_t *device = client->device;
975     int ftrack, ltrack;
976     char *param2;
977 
978     if(!isdigit(params[0])) {
979 	autocd_cprintf(client, "ERROR: invalid parameters for play\n");
980 	return -2;
981     }
982 
983     if((param2 = strchr(params, ' '))) {
984 	param2++[0] = 0;
985 	sscanf(param2, "%i", &ltrack);
986     } else
987 	ltrack = device->last_audio_track;
988 
989     sscanf(params, "%i", &ftrack);
990 
991     if(autocd_playtracks(device, ftrack, ltrack) == -1)
992 	return -1;
993 
994     return 0;
995 }
996 
997 int
autocd_volume(client_t * client)998 autocd_volume(client_t *client)
999 {
1000     device_t *device = client->device;
1001     struct ioc_vol volume;
1002 
1003     if(ioctl(device->dev, CDIOCGETVOL, &volume) == -1)
1004 	return -1;
1005 
1006     autocd_cprintf(client, "Left volume = %i, right volume = %i\n",
1007 	volume.vol[0], volume.vol[1]);
1008 
1009     return 0;
1010 }
1011 
1012 int
autocd_changevolume(client_t * client,char * params)1013 autocd_changevolume(client_t *client, char *params)
1014 {
1015     device_t *device = client->device;
1016     struct ioc_vol volume;
1017     char *s;
1018 
1019     if(params[0] != '+' && params[0] != '-') {
1020 	if((s = strchr(params, ':')) == NULL) {
1021 	    volume.vol[0] = volume.vol[1] = atoi(params);
1022 	} else {
1023 	    s[0] = 0;
1024 	    volume.vol[0] = atoi(params);
1025 	    volume.vol[1] = atoi(++s);
1026 	}
1027     } else {
1028 	int offset[2];
1029 
1030 	if(ioctl(device->dev, CDIOCGETVOL, &volume) == -1)
1031 	    return -1;
1032 
1033 	if((s = strchr(params, ':')) == NULL) {
1034 	    offset[0] = offset[1] = atoi(params);
1035 	} else {
1036 	    s[0] = 0;
1037 	    offset[0] = atoi(params);
1038 	    offset[1] = atoi(++s);
1039 	}
1040 
1041 	if((offset[0] + (int)volume.vol[0]) > 255)
1042 	    offset[0] = 255 - volume.vol[0];
1043 
1044 	if((offset[0] + (int)volume.vol[0]) < 0)
1045 	    offset[0] = - (int)volume.vol[0];
1046 
1047 	if((offset[1] + (int)volume.vol[1]) > 255)
1048 	    offset[1] = 255 - volume.vol[1];
1049 
1050 	if((offset[1] + (int)volume.vol[1]) < 0)
1051 	    offset[1] = - (int)volume.vol[1];
1052 
1053 	volume.vol[0] += offset[0];
1054 	volume.vol[1] += offset[1];
1055     }
1056 
1057     if(ioctl(device->dev, CDIOCSETVOL, &volume) == -1)
1058 	return -1;
1059 
1060     return 0;
1061 }
1062 
1063 #ifdef HAVE_SYS_CDRIO_H
1064 int
autocd_speed(client_t * client,char * params)1065 autocd_speed(client_t *client, char *params)
1066 {
1067     device_t *device = client->device;
1068     int speed;
1069 
1070     speed = atoi(params);
1071 
1072     if(speed <= 0)
1073 	speed = CDR_MAX_SPEED;
1074 
1075     if(ioctl(device->dev, CDRIOCREADSPEED, &speed) == -1)
1076 	return -1;
1077 
1078     return 0;
1079 }
1080 
1081 int
autocd_lockspeed(client_t * client,char * params)1082 autocd_lockspeed(client_t *client, char *params)
1083 {
1084     device_t *device = client->device;
1085     int speed;
1086 
1087     speed = atoi(params);
1088 
1089     if(speed <= 0 || speed > CDR_MAX_SPEED)
1090 	speed = CDR_MAX_SPEED;
1091 
1092     device->locked_speed = speed == CDR_MAX_SPEED ? 0 : speed;
1093 
1094     if(!device->ejected)
1095 	if(autocd_opendev(device) != -1) {
1096 	    if(ioctl(device->dev, CDRIOCREADSPEED, &speed) == -1) {
1097 		autocd_closedev(device);
1098 		device->locked_speed = 0;
1099 		return -1;
1100 	    }
1101 	    autocd_closedev(device);
1102 	}
1103 
1104     return 0;
1105 }
1106 #endif
1107 
1108 int
autocd_listdevices(client_t * client)1109 autocd_listdevices(client_t *client)
1110 {
1111     device_t *device;
1112     int i;
1113 
1114     for(device = (device_t *)mq_gethead(devices), i = 0; device;
1115 	device = device->next, i++) {
1116 	autocd_cprintf(client, "%i %s\n", i, device->device);
1117     }
1118 
1119     return 0;
1120 }
1121 
1122 int
autocd_listsaved(client_t * client)1123 autocd_listsaved(client_t *client)
1124 {
1125     saved_msf_t *msf;
1126 
1127     autocd_cprintf(client, "cdid\t\tposition\n");
1128 
1129     for(mq_resetcurrent(saved_msfs);
1130 	(msf = (saved_msf_t *) mq_nextrecord(saved_msfs));) {
1131 	autocd_cprintf(client, "%08x\t%2i:%02i.%02i - %2i:%02i.%02i\n",
1132 	    msf->cdid, msf->play_msf.start_m, msf->play_msf.start_s,
1133 	    msf->play_msf.start_f, msf->play_msf.end_m, msf->play_msf.end_s,
1134 	    msf->play_msf.end_f);
1135     }
1136 
1137     return 0;
1138 }
1139 
1140 int
autocd_changedevice(client_t * client,char * dev)1141 autocd_changedevice(client_t *client, char *dev)
1142 {
1143     int i;
1144     device_t *device = NULL;
1145 
1146     if(sscanf(dev, "%i", &i) == 0) {
1147 	for(device = (device_t *)mq_gethead(devices);
1148 	    device; device = device->next)
1149 	    if(!strcmp(device->device, dev))
1150 		break;
1151     } else {
1152 	for(device = (device_t *)mq_gethead(devices);
1153 	    device && i; i--, device = device->next);
1154     }
1155 
1156     if(!device) {
1157 	autocd_ERROR(client, "ERROR: no such device: %s\n", dev);
1158 	return -2;
1159     }
1160 
1161     client->device = device;
1162 
1163     return 0;
1164 }
1165 
1166 int
autocd_cdid(client_t * client)1167 autocd_cdid(client_t *client)
1168 {
1169     autocd_cprintf(client, "%08x\n", client->device->cdid);
1170 
1171     return 0;
1172 }
1173 
1174 int
autocd_disable(client_t * client)1175 autocd_disable(client_t *client)
1176 {
1177     client->device->disabled = 1;
1178 
1179     return 0;
1180 }
1181 
1182 int
autocd_enable(client_t * client)1183 autocd_enable(client_t *client)
1184 {
1185     client->device->disabled = 0;
1186 
1187     return 0;
1188 }
1189 
1190 int
autocd_die(client_t * client)1191 autocd_die(client_t *client)
1192 {
1193     uid_t my_euid, client_euid;
1194     gid_t client_egid;
1195 
1196     if(getpeereid(client->fd, &client_euid, &client_egid) < 0)
1197 	return -1;
1198 
1199     my_euid = geteuid();
1200 
1201     if((client_euid != 0) && (client_euid != my_euid)) {
1202 	autocd_ERROR(client, "ERROR: permission denied\n");
1203 	return -2;
1204     }
1205 
1206     nobreak = 0;
1207 
1208     return 0;
1209 }
1210 
1211 int
autocd_help(client_t * client)1212 autocd_help(client_t *client)
1213 {
1214     int i;
1215 
1216     for(i = 0; cmds[i].cmd; i++) {
1217 	if(cmds[i].params)
1218 	    autocd_cprintf(client, "%s %s\n", cmds[i].cmd, cmds[i].params);
1219 	else
1220 	    autocd_cprintf(client, "%s\n", cmds[i].cmd);
1221     }
1222 
1223     return 0;
1224 }
1225 
1226 int
autocd_version(client_t * client)1227 autocd_version(client_t *client)
1228 {
1229     autocd_cprintf(client, "%s\n", ver_getversionstring());
1230 
1231     return 0;
1232 }
1233 
1234 int
autocd_save(client_t * client)1235 autocd_save(client_t *client)
1236 {
1237     device_t *device = client->device;
1238     saved_msf_t *msf;
1239 
1240     for(mq_resetcurrent(saved_msfs);
1241 	(msf = (saved_msf_t *) mq_nextrecord(saved_msfs));) {
1242 	if(device->cdid == msf->cdid)
1243 	    break;
1244     }
1245 
1246     if(!msf)
1247 	msf = (saved_msf_t *)mq_addnewrecord(saved_msfs);
1248 
1249     msf->play_msf.start_m =
1250 	device->sub_channel_info.what.position.absaddr.msf.minute;
1251     msf->play_msf.start_s =
1252 	device->sub_channel_info.what.position.absaddr.msf.second;
1253     msf->play_msf.start_f =
1254 	device->sub_channel_info.what.position.absaddr.msf.frame;
1255     msf->cdid = device->cdid;
1256 
1257     msf->play_msf.end_m = device->play_msf.end_m;
1258     msf->play_msf.end_s = device->play_msf.end_s;
1259     msf->play_msf.end_f = device->play_msf.end_f;
1260 
1261     return 0;
1262 }
1263 
1264 int
autocd_load(client_t * client)1265 autocd_load(client_t *client)
1266 {
1267     device_t *device = client->device;
1268     saved_msf_t *msf;
1269 
1270     for(mq_resetcurrent(saved_msfs);
1271 	(msf = (saved_msf_t *) mq_nextrecord(saved_msfs));) {
1272 	if(device->cdid == msf->cdid) {
1273 	    device->play_msf = msf->play_msf;
1274 
1275 	    mq_removecurrent(saved_msfs);
1276 
1277 	    if(autocd_play_msf(device) == -1)
1278 		return -1;
1279 
1280 	    return 0;
1281 	}
1282     }
1283 
1284     autocd_ERROR(client, "ERROR: no saved position for cdid = %u\n",
1285 	device->cdid);
1286     return -2;
1287 }
1288 
1289 int
autocd_closeclient(client_t * client)1290 autocd_closeclient(client_t *client)
1291 {
1292     int fd;
1293 
1294     if(((client_t *) mq_getcurrent(clients)) != client) {
1295 	logger("Strange autocd_closeclient() call\n");
1296 	return -1;
1297     }
1298 
1299     fd = client->fd;
1300     FD_CLR(fd, &read_fds);
1301     close(fd);
1302 
1303     mq_removecurrent(clients);
1304 
1305     return -2;
1306 }
1307 
1308 void
autocd_execmd(char * cmd)1309 autocd_execmd(char *cmd)
1310 {
1311     client_t *client;
1312     char *params;
1313     int slen, i;
1314 
1315     client = (client_t *)mq_getcurrent(clients);
1316 
1317     slen = strlen(cmd);
1318 
1319     if((params = strchr(cmd, ' '))) {
1320 	params[0] = 0;
1321 	slen = params++ - cmd;
1322     }
1323 
1324     for(i = 0; cmds[i].cmd; i++) {
1325 	if(!strncasecmp(cmd, cmds[i].cmd, slen)) {
1326 	    int rcode;
1327 
1328 	    if((params == NULL) != (cmds[i].params == NULL))
1329 		continue;
1330 
1331 	    if(cmds[i].need_disc) {
1332 		if(client->device->ejected) {
1333 		    autocd_cprintf(client, "ERROR: no disc in drive\n");
1334 		    return;
1335 		}
1336 
1337 		if(cmds[i].need_play) {
1338 		    u_char audio_status =
1339 			client->device->sub_channel_info.header.audio_status;
1340 
1341 		    if(audio_status != CD_AS_PLAY_IN_PROGRESS &&
1342 			audio_status != CD_AS_PLAY_PAUSED) {
1343 			autocd_cprintf(client,
1344 			    "ERROR: no playing in progress\n");
1345 			return;
1346 		    }
1347 		}
1348 	    }
1349 
1350 	    if(cmds[i].need_opendev) {
1351 		if(autocd_opendev(client->device) == -1) {
1352 		    autocd_cprintf(client, "ERROR: %s\n", strerror(errno));
1353 		    return;
1354 		}
1355 	    }
1356 
1357 	    if(params) {
1358 		handler_wp_t *handler = (handler_wp_t *)(*cmds[i].handler);
1359 		rcode = (*handler)(client, params);
1360 	    } else {
1361 		rcode = (*cmds[i].handler)(client);
1362 	    }
1363 
1364 	    switch(rcode) {
1365 	    case -1:
1366 		autocd_cprintf(client, "ERROR: %s\n", strerror(errno));
1367 		break;
1368 
1369 	    case 0:
1370 		autocd_cprintf(client, "OK\n");
1371 		break;
1372 	    }
1373 
1374 	    if(cmds[i].need_opendev)
1375 		autocd_closedev(client->device);
1376 
1377 	    return;
1378 	}
1379     }
1380 
1381     autocd_ERROR(client, "ERROR: unknown command: %s\n", cmd);
1382 }
1383 
1384 void
autocd_serveclient()1385 autocd_serveclient()
1386 {
1387     client_t *client;
1388     int i, begin, len, fd;
1389     char buf[BUFSIZE];
1390 
1391     fd = (client = (client_t *) mq_getcurrent(clients))->fd;
1392 
1393     if(!(len = read(fd, buf, sizeof(buf) - sizeof(char)))) {
1394 	autocd_closeclient(client);
1395 	return;
1396     }
1397 
1398     while((len > 0) && ((buf[len - 1] == 13) || (buf[len - 1] == 10)))
1399 	len--;
1400     buf[len] = 0;
1401 
1402     if(!len)
1403 	return;
1404 
1405     for(begin = i = 0; i < len; i++) {
1406 	if((buf[i] == 13) || (buf[i] == 10)) {
1407 	    buf[i] = 0;
1408 	    autocd_execmd(buf + begin);
1409 
1410 	    for(i++; (i < len) && ((buf[i] == 13) || (buf[i] == 10)); i++);
1411 
1412 	    begin = i;
1413 	}
1414     }
1415 
1416     if(begin != i)
1417 	autocd_execmd(buf + begin);
1418 }
1419 
1420 void
autocd_loop()1421 autocd_loop()
1422 {
1423     device_t *dev;
1424     fd_set fds;
1425     struct timeval tv = {1, 0};
1426     client_t *client;
1427 
1428     for(mq_resetcurrent(devices);
1429     (dev = (device_t *) mq_nextrecord(devices));) {
1430 	autocd_checkdevice(dev);
1431 
1432 	if(dev->sub_channel_info.header.audio_status ==
1433 	    CD_AS_PLAY_IN_PROGRESS) {
1434 		union msf_lba *msf_lba;
1435 
1436 		msf_lba = &dev->toc_entry[dev->first_audio_track].addr;
1437 		dev->play_msf.start_m = msf_lba->msf.minute;
1438 		dev->play_msf.start_s = msf_lba->msf.second;
1439 		dev->play_msf.start_f = msf_lba->msf.frame;
1440 
1441 		msf_lba = &dev->toc_entry[dev->last_audio_track].addr;
1442 		dev->play_msf.end_m = msf_lba->msf.minute;
1443 		dev->play_msf.end_s = msf_lba->msf.second;
1444 		dev->play_msf.end_f = msf_lba->msf.frame;
1445 	}
1446     }
1447 
1448     while(nobreak) {
1449 	int rcode;
1450 
1451 	fds = read_fds;
1452 
1453 	if((rcode = select(FD_SETSIZE, &fds, NULL, NULL, &tv)) < 0) {
1454 	    if(errno != EINTR) {
1455 		logger("select() failed: %s\n", strerror(errno));
1456 		break;
1457 	    }
1458 	    rcode = 0;
1459 	}
1460 
1461 	for(mq_resetcurrent(devices);
1462 	    (dev = (device_t *) mq_nextrecord(devices));)
1463 	    autocd_checkdevice(dev);
1464 
1465 	if(rcode == 0)
1466 	    continue;
1467 
1468 	if(FD_ISSET(socket_fd, &fds)) {
1469 	    autocd_accept();
1470 	}
1471 
1472 	for(mq_resetcurrent(clients);
1473 	    (client = (client_t *) mq_nextrecord(clients));) {
1474 	    if(FD_ISSET(client->fd, &fds)) {
1475 		autocd_serveclient();
1476 	    }
1477 	}
1478     }
1479 
1480     close(socket_fd);
1481     unlink(socket_path);
1482 
1483     for(mq_resetcurrent(clients);
1484 	(client = (client_t *) mq_nextrecord(clients));)
1485 	autocd_closeclient(client);
1486 
1487     exit(EX_OK);
1488 }
1489 
1490 void
sigterm_handler()1491 sigterm_handler()
1492 {
1493     nobreak = 0;
1494 }
1495 
1496 int
autocd_daemon()1497 autocd_daemon()
1498 {
1499     if(daemon(0, 1) == -1) {
1500 	logger("daemon() failed: %s\n", strerror(errno));
1501 	return -1;
1502     }
1503 
1504     set_daemon_mode("autocd");
1505 
1506     signal(SIGHUP, SIG_IGN);
1507     signal(SIGTERM, &sigterm_handler);
1508     signal(SIGINT, &sigterm_handler);
1509 
1510     autocd_loop();
1511 
1512     return 0;
1513 }
1514