commit - 494d8ca239824a4809e171c484f2a9518a0dc6a1
commit + 1d6739509f8059b45ef1c1eb4f06d4596fc46984
blob - 74d82695ab2684ecccc3055fe046818b830b2728
blob + 4eae07ace3e8e07427b651f85dce5b6ad032aad4
--- amused.1
+++ amused.1
.Nm
.Op Fl dv
.Op Fl s Ar socket
+.Oo
.Ar command
.Op Ar argument ...
+.Oc
.Sh DESCRIPTION
.Nm
is a music player daemon and command-line utility that plays music
in the background.
The server is automatically started when the user interacts with
-it, but it can be started manually by running
-.Nm
-without any arguments.
+it.
.Pp
The following options are available:
.Bl -tag -width 12m
.It Fl d
-Do not daemonize.
-If this option is specified,
+Do not daemonize if starting the daemon:
.Nm
will run in the foreground and log to standard error.
-It's ignored if any commands are given on the command line.
+It's ignored if any commands are given on the command line or if the
+server was already running.
.It Fl v
Produce more verbose output.
.It Fl s Ar socket
instead of the default
.Pa /tmp/amused-$UID
to communicate with the daemon.
+.It Ar command Op Ar argument ...
+Specify the command to run.
+If no commands are specified, the
+.Ic status
+command is assumed.
.El
.Pp
The following commands are available:
blob - 0356b1c0e2de75a68ee3e88790c20939b4e8e37a
blob + 300ba43879584007e51ad4ee42fe73e3f6b8ab3e
--- amused.c
+++ amused.c
static pid_t
start_child(enum amused_process proc, int fd)
{
- const char *argv[6];
+ const char *argv[7];
int argc = 0;
pid_t pid;
+ if (fd == -1 && debug)
+ goto exec;
+
switch (pid = fork()) {
case -1:
fatal("cannot fork");
} else if (fcntl(F_SETFD, 0) == -1)
fatal("cannot setup imsg fd");
+exec:
argv[argc++] = argv0;
switch (proc) {
case PROC_MAIN:
argv[argc++] = "-s";
argv[argc++] = csock;
+ argv[argc++] = "-Tm";
break;
case PROC_PLAYER:
argv[argc++] = "-Tp";
int
main(int argc, char **argv)
{
- int ch, proc = PROC_MAIN;
+ int ch, proc = -1;
log_init(1, LOG_DAEMON); /* Log to stderr until daemonized */
log_setverbose(1);
break;
case 'T':
switch (*optarg) {
+ case 'm':
+ proc = PROC_MAIN;
+ break;
case 'p':
proc = PROC_PLAYER;
break;
argv += optind;
argc -= optind;
+ if (proc == PROC_MAIN)
+ amused_main();
if (proc == PROC_PLAYER)
exit(player(debug, verbose));
if (csock == NULL)
xasprintf(&csock, "/tmp/amused-%d", getuid());
- if (argc == 0)
- amused_main();
- else
- ctl(argc, argv);
- return 0;
+ if (argc > 0)
+ debug = 0;
+
+ ctl(argc, argv);
}
void
spawn_daemon(void)
{
- debug = 0;
start_child(PROC_MAIN, -1);
}
blob - d59f7739bf42943ea1e737d7260b70a88477c0ab
blob + 66425e68fc874dbedd997780e157dd44f6757cf6
--- ctl.c
+++ ctl.c
#include <errno.h>
#include <event.h>
+#include <fcntl.h>
#include <limits.h>
#include <sndio.h>
#include <stdio.h>
{
struct ctl_command *ctl = NULL;
struct parse_result res;
+ const char *argv0;
int i, status;
memset(&res, 0, sizeof(res));
+ if ((argv0 = argv[0]) == NULL)
+ argv0 = "status";
+
for (i = 0; ctl_commands[i].name != NULL; ++i) {
- if (strncmp(ctl_commands[i].name, argv[0], strlen(argv[0]))
+ if (strncmp(ctl_commands[i].name, argv0, strlen(argv0))
== 0) {
if (ctl != NULL) {
fprintf(stderr,
- "ambiguous argument: %s\n", argv[0]);
+ "ambiguous argument: %s\n", argv0);
usage();
}
ctl = &ctl_commands[i];
}
static int
-sockconn(void)
+ctl_get_lock(const char *lockfile)
{
- struct sockaddr_un sun;
- int sock, saved_errno;
+ int lockfd;
- if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
- fatal("socket");
+ if ((lockfd = open(lockfile, O_WRONLY|O_CREAT, 0600)) == -1) {
+ log_debug("open failed: %s", strerror(errno));
+ return -1;
+ }
- memset(&sun, 0, sizeof(sun));
- sun.sun_family = AF_UNIX;
- strlcpy(sun.sun_path, csock, sizeof(sun.sun_path));
- if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
- saved_errno = errno;
- close(sock);
- errno = saved_errno;
+ if (flock(lockfd, LOCK_EX|LOCK_NB) == -1) {
+ log_debug("flock failed: %s", strerror(errno));
+ if (errno != EAGAIN)
+ return -1;
+ while (flock(lockfd, LOCK_EX) == -1 && errno == EINTR)
+ /* nop */;
+ close(lockfd);
+ return -2;
+ }
+ log_debug("flock succeeded");
+
+ return lockfd;
+}
+
+static int
+ctl_connect(void)
+{
+ struct timespec ts = { 0, 50000000 }; /* 0.05 seconds */
+ struct sockaddr_un sa;
+ size_t size;
+ int fd, lockfd = -1, locked = 0, spawned = 0;
+ char *lockfile = NULL;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sun_family = AF_UNIX;
+ size = strlcpy(sa.sun_path, csock, sizeof(sa.sun_path));
+ if (size >= sizeof(sa.sun_path)) {
+ errno = ENAMETOOLONG;
return -1;
}
- return sock;
+retry:
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+ return -1;
+
+ if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
+ log_debug("connection failed: %s", strerror(errno));
+ if (errno != ECONNREFUSED && errno != ENOENT)
+ goto failed;
+ close(fd);
+
+ if (!locked) {
+ xasprintf(&lockfile, "%s.lock", csock);
+ if ((lockfd = ctl_get_lock(lockfile)) < 0) {
+ log_debug("didn't get the lock (%d)", lockfd);
+
+ free(lockfile);
+ lockfile = NULL;
+
+ if (lockfd == -1)
+ goto retry;
+ }
+
+ /*
+ * Always retry at least once, even if we got
+ * the lock, because another client could have
+ * taken the lock, started the server and released
+ * the lock between our connect() and flock()
+ */
+ locked = 1;
+ goto retry;
+ }
+
+ if (!spawned) {
+ log_debug("spawning the daemon");
+ spawn_daemon();
+ spawned = 1;
+ }
+
+ nanosleep(&ts, NULL);
+ goto retry;
+ }
+
+ if (locked && lockfd >= 0) {
+ unlink(lockfile);
+ free(lockfile);
+ close(lockfd);
+ }
+ return fd;
+
+failed:
+ if (locked) {
+ free(lockfile);
+ close(lockfd);
+ }
+ close(fd);
+ return -1;
}
__dead void
ctl(int argc, char **argv)
{
- int ctl_sock, i = 0;
+ int ctl_sock;
log_init(1, LOG_DAEMON);
log_setverbose(verbose);
- do {
- struct timespec ts = { 0, 50000000 }; /* 0.05 seconds */
+ if ((ctl_sock = ctl_connect()) == -1)
+ fatal("can't connect");
- if ((ctl_sock = sockconn()) != -1)
- break;
- if (errno != ENOENT && errno != ECONNREFUSED)
- fatal("connect %s", csock);
-
- if (i == 0)
- spawn_daemon();
-
- nanosleep(&ts, NULL);
- } while (++i < 20);
-
if (ctl_sock == -1)
fatalx("failed to connect to the daemon");