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