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 <errno.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <libgen.h>
31 #include <ctype.h>
32 #include <sysexits.h>
33
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>
36 #endif
37
38 #ifdef HAVE_HISTEDIT_H
39 #include <histedit.h>
40 #endif
41
42 #include "settings.h"
43 #include "logger.h"
44 #include "acd.h"
45 #include "mqueue.h"
46 #include "version.h"
47
48 const char *progname = "cdctl";
49
50 typedef struct {
51 void *next;
52 void *prev;
53 char *cmd, *params;
54 } cmds_t;
55
56 mq_queue_t *cmds;
57
58 void
usage(char * argv[])59 usage(char *argv[])
60 {
61 char *scmd, *bname;
62
63 bname = strdup(basename(argv[0]));
64
65 if(!strcmp(bname, "eject") || !strcmp(bname, "pause"))
66 scmd = "";
67 else {
68 scmd = "[command ...]";
69 }
70
71 logger("%s\n\
72 Usage: %s [-h] [-d device] [-s socket] %s\n\
73 -h\t\t- this help\n\
74 -d device\t- device\n\
75 -s socket\t- path to a control socket\n\
76 Use `%s help' for command list\n",
77 ver_getversionstring(), bname, scmd, bname);
78
79 free(bname);
80
81 exit(EX_USAGE);
82 }
83
84 int
opendev()85 opendev()
86 {
87 int fd;
88
89 if((fd = acd_connect()) == -1) {
90 logger("Could not connect to daemon: %s\n", strerror(errno));
91 exit(EX_UNAVAILABLE);
92 }
93
94 return fd;
95 }
96
97 void
cleanup(int fd)98 cleanup(int fd)
99 {
100 close(fd);
101 exit(EX_UNAVAILABLE);
102 }
103
104 int
execmd(int fd,char * cmd)105 execmd(int fd, char *cmd)
106 {
107 char buf[BUFSIZE], *s, *s_end;
108 int i;
109
110 if(!cmd[0])
111 return 0;
112
113 asprintf(&s, "%s\n", cmd);
114
115 if(write(fd, s, strlen(s)) < 0) {
116 free(s);
117 cleanup(fd);
118 }
119
120 free(s);
121
122 while((i = read(fd, buf, sizeof(buf) - sizeof(char))) > 0) {
123 buf[i] = 0;
124 s = buf;
125
126 while((s_end = strchr(s, '\n'))) {
127 s_end[0] = 0;
128
129 if(!strcmp(s, "OK"))
130 return 0;
131
132 if(strstr(s, "ERROR: ") == s) {
133 logger("%s\n", s);
134 return -1;
135 }
136
137 printf("%s\n", s);
138
139 s = s_end + 1;
140 }
141 }
142
143 if(i < 0)
144 cleanup(fd);
145
146 return 0;
147 }
148
149 int
loadcmds(int fd)150 loadcmds(int fd)
151 {
152 char buf[BUFSIZE], *s, *s_end;
153 int i;
154
155 asprintf(&s, "HELP\n");
156
157 if(write(fd, s, strlen(s)) < 0) {
158 free(s);
159 cleanup(fd);
160 }
161
162 free(s);
163
164 cmds = mq_makequeue(sizeof(cmds_t));
165
166 while((i = read(fd, buf, sizeof(buf) - sizeof(char))) > 0) {
167 buf[i] = 0;
168 s = buf;
169
170 while((s_end = strchr(s, '\n'))) {
171 char *space;
172 cmds_t *tmp;
173
174 s_end[0] = 0;
175
176 if(strstr(s, "ERROR: ") == s) {
177 logger("Error! Can't load list of avaible commands.\n", s);
178 exit(EX_UNAVAILABLE);
179 }
180
181 if(!strcmp(s, "OK"))
182 return 0;
183
184 tmp = (cmds_t *) mq_addnewrecord(cmds);
185
186 if((space = strchr(s, ' '))) {
187 space[0] = 0;
188 tmp->params = strdup(++space);
189 } else {
190 tmp->params = NULL;
191 }
192
193 tmp->cmd = strdup(s);
194
195 s = s_end + 1;
196 }
197 }
198
199 if(i < 0)
200 cleanup(fd);
201
202 return 0;
203 }
204
205 #ifdef HAVE_HISTEDIT_H
206 const char *
cdctl_prompt()207 cdctl_prompt()
208 {
209 return "cdctl> ";
210 }
211
212 char *
input()213 input()
214 {
215 static EditLine *el = NULL;
216 static History *hist = NULL;
217 #ifdef EL_INIT_HAVE_STDERR
218 HistEvent he;
219 #endif
220 int num = 0, slen;
221 const char *bp = NULL;
222
223 for(;;) {
224 cmds_t *cmd;
225 char *s, *params;
226
227 if (!el) {
228
229 #ifdef EL_INIT_HAVE_STDERR
230 el = el_init(progname, stdin, stdout, stderr);
231 #else
232 el = el_init(progname, stdin, stdout);
233 #endif
234
235 hist = history_init();
236
237 #ifdef EL_INIT_HAVE_STDERR
238 history(hist, &he, H_SETSIZE, 100);
239 #else
240 history(hist, H_SETSIZE, 100);
241 #endif
242
243 el_set(el, EL_HIST, history, hist);
244 el_set(el, EL_EDITOR, "emacs");
245 el_set(el, EL_PROMPT, cdctl_prompt);
246 el_set(el, EL_SIGNAL, 1);
247 el_source(el, NULL);
248 }
249
250 if((bp = el_gets(el, &num)) == NULL || num == 0) {
251 fprintf (stderr, "\r\n");
252 return NULL;
253 }
254
255 #ifdef EL_INIT_HAVE_STDERR
256 history(hist, &he, H_ENTER, bp);
257 #else
258 history(hist, H_ENTER, bp);
259 #endif
260
261 if(bp[0] == 0 || bp[0] == '\n') {
262 continue;
263 }
264
265 if((s = strdup(bp))[(slen = strlen(bp)) - 1] == '\n')
266 s[--slen] = 0;
267
268 if((params = strchr(s, ' '))) {
269 params[0] = 0;
270 slen = strlen(s);
271 }
272
273 for(mq_resetcurrent(cmds); (cmd = (cmds_t *) mq_nextrecord(cmds));) {
274 if(params) {
275 if(cmd->params) {
276 if(!strncasecmp(s, cmd->cmd, slen)) {
277 params[0] = ' ';
278 return s;
279 }
280 }
281 } else {
282 if(!cmd->params) {
283 if(!strncasecmp(s, cmd->cmd, slen)) {
284 return s;
285 }
286 }
287 }
288 }
289
290 logger("invalid command, enter ``help'' for commands\n");
291 }
292 }
293 #endif
294
295 int
main(int argc,char * argv[])296 main(int argc, char *argv[])
297 {
298 int ch, fd = -1;
299 char *bname, *s;
300
301 while((ch = getopt(argc, argv, "hd:s:")) != -1) {
302 switch(ch) {
303 case 'd':
304 if(fd == -1)
305 fd = opendev();
306
307 asprintf(&s, "device %s", optarg);
308 if(execmd(fd, s) == -1) {
309 free(s);
310 return EX_UNAVAILABLE;
311 }
312
313 free(s);
314 break;
315
316 case 's':
317 acd_setsocketpath(optarg);
318 break;
319
320 case 'h':
321 default:
322 usage(argv);
323 }
324 }
325
326 if(fd == -1)
327 fd = opendev();
328
329 bname = strdup(basename(argv[0]));
330
331 if(!strcmp(bname, "eject") || !strcmp(bname, "pause")) {
332 if(argc != optind)
333 usage(argv);
334
335 execmd(fd, bname);
336
337 close(fd);
338 return EX_OK;
339 }
340
341 loadcmds(fd);
342
343 if(argc == optind) {
344 #ifdef HAVE_HISTEDIT_H
345 char *command;
346
347 printf("%s\nType ``help'' for command list\n\n",
348 ver_getversionstring());
349
350 while((command = input())) {
351 int slen = strlen(command);
352
353 if(command[slen - 1] == '\n')
354 command[--slen] = 0;
355
356 execmd(fd, command);
357
358 if(command[0] && !strncasecmp(command, "QUIT", strlen(command))) {
359 free(command);
360 break;
361 }
362
363 free(command);
364 }
365
366 close(fd);
367
368 return EX_OK;
369 #else
370 close(fd);
371 usage(argv);
372 #endif
373 }
374
375 for(; optind < argc; optind++) {
376 cmds_t *cmd;
377 int paramc = 0, slen, notexecuted = 1;
378 char *command;
379
380 slen = strlen(argv[optind]);
381
382 if((optind + 1) < argc) {
383 if(isdigit(argv[optind + 1][0]) ||
384 argv[optind + 1][0] == '-' || argv[optind + 1][0] == '+') {
385 if((optind + 2) < argc)
386 if(isdigit(argv[optind + 2][0]))
387 paramc = 2;
388 else
389 paramc = 1;
390 else
391 paramc = 1;
392 }
393 }
394
395 switch(paramc) {
396 case 0:
397 asprintf(&command, "%s", argv[optind]);
398 break;
399
400 case 1:
401 asprintf(&command, "%s %s", argv[optind], argv[optind + 1]);
402 break;
403
404 case 2:
405 asprintf(&command, "%s %s %s",
406 argv[optind], argv[optind + 1], argv[optind + 2]);
407 break;
408 }
409
410 for(mq_resetcurrent(cmds); (cmd = (cmds_t *) mq_nextrecord(cmds));) {
411 if(paramc == 0) {
412 if(!cmd->params) {
413 if(!strncasecmp(argv[optind], cmd->cmd, slen)) {
414 execmd(fd, command);
415 notexecuted = 0;
416 break;
417 }
418 }
419 } else {
420 if(cmd->params) {
421 if(!strncasecmp(argv[optind], cmd->cmd, slen)) {
422 execmd(fd, command);
423 notexecuted = 0;
424 break;
425 }
426 }
427 }
428 }
429
430 if(notexecuted)
431 logger("Unknown command: %s\n", argv[optind]);
432 else
433 optind += paramc;
434 }
435
436 close(fd);
437
438 return EX_OK;
439 }
440