2 * UCW Library Utilities -- A Simple Logger for use in shell scripts
4 * (c) 2001--2009 Martin Mares <mj@ucw.cz>
5 * (c) 2011 Tomas Ebenlendr <ebik@ucw.cz>
7 * This software may be freely distributed and used according to the terms
8 * of the GNU Lesser General Public License.
15 #include "ucw/mainloop.h"
16 #include "ucw/clists.h"
17 #include "ucw/getopt.h"
24 #include <sys/types.h>
28 static uns max_line = 1024;
30 static struct cf_section cfsec_logoutput = {
32 CF_UNS("LineMax", &max_line),
37 static clist filedescriptors;
45 struct main_rec_io rio;
49 close_fd(struct fds *fd)
53 clist_remove(&fd->node);
54 if (clist_empty(&filedescriptors))
60 do_msg (struct fds *fd, char *l_msg, int long_continue)
62 msg(fd->level, "%s%s", (fd->long_continue ? "... " : ""), l_msg);
63 fd->long_continue = long_continue;
67 handle_read(struct main_rec_io *r)
69 char buf[max_line + 5];
70 byte *eol = memchr((char *)r->read_rec_start + r->read_prev_avail, '\n', r->read_avail - r->read_prev_avail);
72 if (r->read_avail >= max_line) {
73 memcpy(buf, r->read_rec_start, max_line);
74 memcpy(buf + max_line, " ...", 5);
75 do_msg(r->data, buf, 1);
81 byte *b = r->read_rec_start;
82 while ((uns)(eol - b) > max_line) {
83 char cc = b[max_line];
85 do_msg(r->data, b, 1);
89 do_msg(r->data, (char *)b, 0);
90 return eol - r->read_rec_start + 1;
94 handle_notify(struct main_rec_io *r, int status)
96 struct fds *fd = r->data;
101 char buf[max_line + 10];
102 memcpy(buf, r->read_rec_start, r->read_avail);
103 memcpy(buf + r->read_avail, " [no eol]", 10);
104 do_msg(r->data, buf, 0);
105 } else if (fd->long_continue) {
106 do_msg(r->data, "[no eol]", 0);
118 add_level_fd(int fdnum, int level)
120 struct fds *fd = xmalloc_zero(sizeof(*fd));
125 fd->rio.read_handler = handle_read;
127 fd->rio.notify_handler = handle_notify;
128 fd->long_continue = 0;
129 clist_add_tail(&filedescriptors, &fd->node);
137 die("Cannot dup(): %m");
138 DBG(" Dup(%i) -> %i", fd, rfd);
143 xdup2(int fd1, int fd2)
145 int rfd = dup2(fd1, fd2);
147 die("Cannot dup2(): %m");
148 DBG(" Dup2(%i, %i) -> %i", fd1, fd2, rfd);
153 xdupavoid(int *fd1, int fd2)
155 DBG("Dupavoid: !%i -> %i", fd2, *fd1);
159 DBG(" Close: %i", ofd);
165 xdupto(int *fd1, int fd2)
167 DBG("Dupto: %i -> %i", *fd1, fd2);
170 DBG(" Close: %i", fd2);
173 DBG(" Close: %i", *fd1);
179 set_cloexec_flag(int fd, int value)
181 int flags = fcntl(fd, F_GETFD, 0);
183 die("fcntl(..., F_GETFD, ...) : %m");
184 flags = (value) ? flags | FD_CLOEXEC : flags & ~FD_CLOEXEC;
185 if (fcntl(fd, F_SETFD, flags) < 0)
186 die("fcntl(..., F_SETFD, ...) : %m");
189 /* The "+" stands for end at first argument (i.e. do not parse options after
191 #define MY_SHORT_OPTS "+" CF_SHORT_OPTS "f:n:l:ih"
192 const struct option my_long_opts[] = {
194 { "help", 0, 0, 'h'},
195 { "input", 0, 0, 'i'},
196 { "logfile", 1, 0, 'f'},
197 { "logname", 1, 0, 'n'},
198 { "descriptor", 1, 0, 'l'},
203 #define CF_USAGE_TAB "\t\t"
204 static char usage[] =
206 "logoutput -h|--help\t\tThis help.\n"
207 "logoutput <options> -i|--input\tRead filedescriptors and log them.\n"
208 "\t\t\t\tdefault: stdin at level I.\n"
209 "logoutput <opts> [--] <cmd> [arguments for cmd ...]\n"
210 "\t\t\t\tOpen filedescriptors for writing for\n"
211 "\t\t\t\tcommand <cmd> and log them.\n"
212 "\t\t\t\tdefault: stdout:I, stderr:W.\n"
215 "-n, --logname <name>\t\t\tUse <name> as program name in logs.\n"
216 "-l, --descriptor <fdnum>:<level>\tOpen filedescriptor <fdnum> and log it\n"
217 "\t\t\t\t\tat level <level> (discards defaults).\n"
218 "-f, --logfile <logfile>\t\t\tLog to file <logfile>.\n";
221 main(int argc, char **argv)
223 int register_default = 1;
225 char *logfile = NULL;
226 char *logname = NULL;
227 struct fds *stderrfd = NULL;
230 log_init("logoutput");
231 clist_init(&filedescriptors);
232 cf_declare_section("LogOutput", &cfsec_logoutput, 0);
235 int opt = cf_getopt(argc, argv, MY_SHORT_OPTS, my_long_opts, NULL);
260 register_default = 0;
263 if ( (c[0]<'0') || (c[0] > '9') )
265 while ( (!parseerror) && (c[0] >= '0') && (c[0] <= '9') )
266 { fdnum = fdnum*10 + c[0] - '0'; c++; }
267 if ( (!parseerror) && (c[0] != ':') )
270 if ( (!parseerror) && (c[0] == 0) )
272 if ( (!parseerror) && (c[1] != 0) )
274 if (parseerror) die("Bad argument `%s' to -l, expects number:letter.", optarg);
277 while (level < L_MAX && LS_LEVEL_LETTER(level) != c[0])
280 die("Unknown logging level `%s'", c);
282 add_level_fd(fdnum, level);
294 if (loginput && (optind < argc))
295 die("No cmd is allowed for -i. Use -h for help.");
297 if ((!loginput) && (optind >= argc)) {
298 msg(L_FATAL, "Either command or --input expected.");
303 write(2, usage, sizeof(usage));
304 return (help == 1) ? 0 : 1;
307 if (register_default) {
309 add_level_fd(0, L_INFO);
311 add_level_fd(1, L_INFO);
312 add_level_fd(2, L_WARN);
317 /* Just check, that we don't want open stderr for reading. */
318 CLIST_FOR_EACH(struct fds *, fd, filedescriptors) {
320 die("Stderr is reserved for output");
323 /* Open all filedescriptors and their duplicates. */
324 CLIST_FOR_EACH(struct fds *, fd, filedescriptors) {
325 CLIST_FOR_EACH(struct fds *, fdcheck, filedescriptors) {
326 /* We do a dummy check for collisions of filedescriptors. */
329 if (fdcheck->fdnum == fd->fdnum) {
330 die("Duplicate filedescriptor %i", fd->fdnum);
332 xdupavoid(fdcheck->pipe + 0, fd->fdnum);
333 xdupavoid(fdcheck->pipe + 1, fd->fdnum);
335 if (pipe(fd->pipe) == -1)
336 die("Cannot create pipe: %m");
337 DBG("Pipe [%i, %i] for %i", fd->pipe[0], fd->pipe[1], fd->fdnum);
338 xdupavoid(fd->pipe + 0, fd->fdnum);
340 if (fd->fdnum == 2) {
341 stderrfd = fd; //We need to redirect stderr later.
343 xdupto(fd->pipe + 1, fd->fdnum);
345 DBG("---> [%i, %i] for %i", fd->pipe[0], fd->pipe[1], fd->fdnum);
346 set_cloexec_flag(fd->pipe[0], 1);
347 set_cloexec_flag(fd->pipe[1], 0);
348 /* Our pipe is created, let fd->fdnum be the reading end. */
349 fd->fdnum = fd->pipe[0];
353 /* Initialize main loop. */
356 CLIST_FOR_EACH(struct fds *, fd, filedescriptors) {
357 fd->rio.read_rec_max = max_line + 1;
358 rec_io_add(&fd->rio, fd->fdnum);
363 /* Launch the child and close filedescriptors. */
366 die("Cannot fork: %m");
370 /* Move stderr where it should be. */
372 xdupto(stderrfd->pipe + 1, 2);
374 execvp(argv[optind], argv + optind);
376 /* We translate stderr, just print. */
377 perror("Cannot exec child");
380 /* No stderr translation: use logging function. */
381 die("Cannot exec child: %m");
384 /* Close writing filedescriptors. */
385 CLIST_FOR_EACH(struct fds *, fd, filedescriptors) {
390 /* Open logfile or stderr. */
397 /* Inform about launching of the command. */
399 for (int i = optind; i < argc; i++) buflen += strlen(argv[i]) + 1;
400 char *buf = xmalloc(buflen);
402 for (int i = optind; i < argc; i++) {
403 strcpy(buf2, argv[i]);
404 buf2 += strlen(argv[i]);
410 msg(L_INFO, "Launching command: %s", buf);
417 log_init(argv[optind]);
419 /* Start reading from pipes. */
420 CLIST_FOR_EACH(struct fds *, fd, filedescriptors)
421 rec_io_start_read(&fd->rio);
426 log_init("logoutput");
428 /* Wait for status of the child and inform about finish. */
432 while (waitpid(pid, &status, 0) == -1) {
434 die("Cannot wait for child: %m");
437 if (format_exit_status(buf, status)) {
438 msg(L_WARN, "Child %s", buf);
439 return WIFEXITED(status) ? WEXITSTATUS(status) : 127;
441 msg(L_INFO, "Child terminated successfully.");