change license from GPLv3 to GPLv3 or later
[mmq.git] / mmq-enq.c
blobb6ed6eb23bba481f77a66e0765dc7d0c3049d714
1 /* command-line utility to enqueue tracks for mmq-player */
3 #define _XOPEN_SOURCE 600
4 #define _GNU_SOURCE
6 #include "compat/cc.h"
7 #include <assert.h>
8 #include <unistd.h>
9 #include <fcntl.h>
10 #include <sys/stat.h>
11 #include <mqueue.h>
12 #include <limits.h>
13 #include <stdlib.h>
14 #include <errno.h>
15 #include <string.h>
16 #include <sched.h>
17 #include <sys/time.h>
18 #include <sys/resource.h>
20 extern NORETURN void die(const char *tmp, ...) PRINTF;
21 extern void emit(const char *fmt, ...) PRINTF;
22 extern void warn(const char *fmt, ...) PRINTF;
24 static const char *usage = "Usage: %s [-p PRIO] [-n] [-s] [-c] [-P] FILE...\n";
25 #ifndef MMQ_MQUEUE
26 # define MMQ_MQUEUE "/mmq"
27 #endif
28 static char path[PATH_MAX+2];
29 static int peek, clear, verbose;
31 static void * xmalloc(size_t size)
33 void *rv = malloc(size);
35 if (!rv)
36 die("malloc: %s\n", strerror(errno));
37 return rv;
40 struct peek_tmp {
41 struct peek_tmp *next;
42 unsigned int prio;
43 size_t size;
44 char buf[1];
47 static void set_mq_nonblock(mqd_t mq, int nb)
49 struct mq_attr attr;
51 attr.mq_flags = nb ? O_NONBLOCK : 0;
53 if (mq_setattr(mq, &attr, NULL) < 0)
54 die("mq_setattr(nonblock=%d): %s\n", nb, strerror(errno));
57 static void clear_or_peek(mqd_t mq, struct mq_attr *attr)
59 ssize_t r;
60 unsigned int pr;
61 char *tmp = xmalloc(attr->mq_msgsize + 2);
62 struct peek_tmp *peekhead = NULL;
63 struct peek_tmp *peektail = NULL;
64 struct peek_tmp *peektmp;
65 int i;
67 if (!(attr->mq_flags & O_NONBLOCK))
68 set_mq_nonblock(mq, 1);
70 for (i = 0; i < 30; i++) {
71 while ((r = mq_receive(mq, tmp, attr->mq_msgsize, &pr)) >= 0) {
73 * successful receive, ensure we try again after
74 * we've sched_yield()-ed, below
76 i = 0;
78 if (verbose) {
79 tmp[0] = '!';
80 tmp[r] = '\n';
81 write(1, tmp, r + 1);
83 if (peek) {
84 tmp[0] = '=';
85 tmp[r] = '\n';
86 write(1, tmp, r + 1);
88 peektmp = xmalloc(sizeof(struct peek_tmp) + r);
90 tmp[0] = '=';
91 peektmp->size = r;
92 peektmp->prio = pr;
93 peektmp->next = NULL;
94 memcpy(peektmp->buf, tmp, r);
95 if (peektail)
96 peektail->next = peektmp;
97 else
98 peekhead = peektmp;
99 peektail = peektmp;
103 if (r < 0) {
105 * in case another process is blocking on mq_send(),
106 * let that process run so we can try mq_receive()
107 * what was just sent...
109 if (errno == EAGAIN) {
110 setpriority(PRIO_PROCESS, 0, 19);
111 sched_yield();
112 } else {
113 break;
118 if (peek)
119 set_mq_nonblock(mq, 0);
120 while (peekhead) {
121 if (mq_send(mq,
122 peekhead->buf,
123 peekhead->size,
124 peekhead->prio) < 0)
125 die("mq_send: %s\n", strerror(errno));
126 peektmp = peekhead->next;
127 free(peekhead);
128 peekhead = peektmp;
130 if (peek)
131 set_mq_nonblock(mq, 1);
133 if (!(attr->mq_flags & O_NONBLOCK))
134 set_mq_nonblock(mq, 0);
135 free(tmp);
138 int main(int argc, char * const argv[])
140 int opt;
141 int argi = 1;
142 int abs_path = 1;
143 char *mqueue = getenv("MQUEUE");
144 mqd_t mq;
145 int oflag = O_RDWR;
146 unsigned prio = 0;
147 struct mq_attr attr;
149 if (!mqueue)
150 mqueue = MMQ_MQUEUE;
151 while ((opt = getopt(argc, argv, "p:nvscPh")) != -1) {
152 unsigned long xprio;
153 char *err;
155 switch (opt) {
156 case 'c':
157 ++argi;
158 clear = 1;
159 break;
160 case 's':
161 ++argi;
162 abs_path = 0;
163 break;
164 case 'v':
165 ++argi;
166 ++verbose;
167 break;
168 case 'n':
169 ++argi;
170 oflag |= O_NONBLOCK;
171 break;
172 case 'p':
173 argi += 2;
174 xprio = strtoul(optarg, &err, 10);
175 if (*err || xprio < 0 || xprio > UINT_MAX)
176 die("prio must be an unsigned int\n");
177 prio = (unsigned)xprio;
178 break;
179 case 'P':
180 ++argi;
181 peek = 1;
182 break;
183 case 'h':
184 emit(usage, argv[0]);
185 return 0;
186 default:
187 fatal:
188 die(usage, argv[0]);
191 if (argi >= argc && ! clear && ! peek)
192 goto fatal;
194 if ((mq = mq_open(mqueue, oflag)) < 0)
195 die("mq_open: %s\n", strerror(errno));
196 if (mq_getattr(mq, &attr) < 0)
197 die("mq_getattr: %s\n", strerror(errno));
198 if (verbose)
199 emit("MQUEUE=%s\n", mqueue);
201 if (clear || peek)
202 clear_or_peek(mq, &attr);
204 for (; argi < argc; ++argi) {
205 size_t len;
207 if (abs_path) {
208 if (realpath(argv[argi], path + 1) == NULL) {
209 warn("realpath: %s\n", strerror(errno));
210 write(2, argv[argi], strlen(argv[argi]));
211 die("\n");
213 } else {
214 len = strlen(argv[argi]);
215 if (len > PATH_MAX) {
216 warn("longer than PATH_MAX: %d\n", PATH_MAX);
217 write(2, argv[argi], len);
218 die("\n");
220 memcpy(path + 1, argv[argi], len);
223 path[0] = '=';
224 len = strlen(path);
225 if (verbose) {
226 assert(path[len] == 0);
227 path[len] = '\n';
228 write(1, path, len + 1);
229 path[len] = '\0';
231 if (mq_send(mq, path, len, prio) < 0) {
232 warn("mq_send: %s\n", strerror(errno));
233 write(2, path, len);
234 die("\n");
238 return 0;