*
* Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
*
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
*/
/*
* mv -f ./"$DATAFILE" save/
*/
+//usage:#define lpd_trivial_usage
+//usage: "SPOOLDIR [HELPER [ARGS]]"
+//usage:#define lpd_full_usage "\n\n"
+//usage: "SPOOLDIR must contain (symlinks to) device nodes or directories"
+//usage: "\nwith names matching print queue names. In the first case, jobs are"
+//usage: "\nsent directly to the device. Otherwise each job is stored in queue"
+//usage: "\ndirectory and HELPER program is called. Name of file to print"
+//usage: "\nis passed in $DATAFILE variable."
+//usage: "\nExample:"
+//usage: "\n tcpsvd -E 0 515 softlimit -m 999999 lpd /var/spool ./print"
+
#include "libbb.h"
// strip argument of bad chars
return str;
}
-// we can use leaky setenv since we are about to exec or exit
-static void exec_helper(char **filenames, char **argv) ATTRIBUTE_NORETURN;
-static void exec_helper(char **filenames, char **argv)
-{
- char *p, *q;
- char var[2];
-
- var[1] = '\0';
-
- // read and delete ctrlfile
- q = xmalloc_open_read_close(filenames[0], NULL);
- unlink(filenames[0]);
- // provide datafile name
- xsetenv("DATAFILE", filenames[1]);
- // parse control file by "\n"
- while ((p = strchr(q, '\n')) != NULL
- && isalpha(*q)
- ) {
- *p++ = '\0';
- // q is a line of <SYM><VALUE>,
- // we are setting environment string <SYM>=<VALUE>.
- // Ignoring "l<datafile>", exporting others:
- if (*q != 'l') {
- var[0] = *q++;
- xsetenv(var, q);
- }
- // next line, plz!
- q = p;
- }
- // we are the helper, we wanna be silent.
- // this call reopens stdio fds to "/dev/null"
- // (no daemonization is done)
- bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO | DAEMON_ONLY_SANITIZE, NULL);
- BB_EXECVP(*argv, argv);
- exit(127); // it IS error if helper cannot be executed!
-}
-
static char *xmalloc_read_stdin(void)
{
// SECURITY:
size_t max = 4 * 1024; // more than enough for commands!
- return xmalloc_reads(STDIN_FILENO, NULL, &max);
+ return xmalloc_reads(STDIN_FILENO, &max);
}
int lpd_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE;
-int lpd_main(int argc ATTRIBUTE_UNUSED, char *argv[])
+int lpd_main(int argc UNUSED_PARAM, char *argv[])
{
int spooling = spooling; // for compiler
char *s, *queue;
while (1) {
char *fname;
int fd;
- // int is easier than ssize_t: can use xatoi_u,
+ // int is easier than ssize_t: can use xatoi_positive,
// and can correctly display error returns (-1)
int expected_len, real_len;
// N.B. we bail out on any error
s = xmalloc_read_stdin();
if (!s) { // (probably) EOF
- if (spooling /* && 7 != spooling - always true */) {
- // we didn't see both ctrlfile & datafile!
- goto err_exit;
+ char *p, *q, var[2];
+
+ // non-spooling mode or no spool helper specified
+ if (!spooling || !*argv)
+ return EXIT_SUCCESS; // the only non-error exit
+ // spooling mode but we didn't see both ctrlfile & datafile
+ if (spooling != 7)
+ goto err_exit; // reject job
+
+ // spooling mode and spool helper specified -> exec spool helper
+ // (we exit 127 if helper cannot be executed)
+ var[1] = '\0';
+ // read and delete ctrlfile
+ q = xmalloc_xopen_read_close(filenames[0], NULL);
+ unlink(filenames[0]);
+ // provide datafile name
+ // we can use leaky setenv since we are about to exec or exit
+ xsetenv("DATAFILE", filenames[1]);
+ // parse control file by "\n"
+ while ((p = strchr(q, '\n')) != NULL && isalpha(*q)) {
+ *p++ = '\0';
+ // q is a line of <SYM><VALUE>,
+ // we are setting environment string <SYM>=<VALUE>.
+ // Ignoring "l<datafile>", exporting others:
+ if (*q != 'l') {
+ var[0] = *q++;
+ xsetenv(var, q);
+ }
+ q = p; // next line
}
- // one of only two non-error exits
- return EXIT_SUCCESS;
+ // helper should not talk over network.
+ // this call reopens stdio fds to "/dev/null"
+ // (no daemonization is done)
+ bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO | DAEMON_ONLY_SANITIZE, NULL);
+ BB_EXECVP_or_die(argv);
}
// validate input.
fd = xopen(queue, O_RDWR | O_APPEND);
}
+ // signal OK
+ safe_write(STDOUT_FILENO, "", 1);
+
// copy the file
real_len = bb_copyfd_size(STDIN_FILENO, fd, expected_len);
if (real_len != expected_len) {
expected_len, real_len);
goto err_exit;
}
- // get ACK and see whether it is NUL (ok)
+ // get EOF indicator, see whether it is NUL (ok)
// (and don't trash s[0]!)
if (safe_read(STDIN_FILENO, &s[1], 1) != 1 || s[1] != 0) {
// don't send error msg to peer - it obviously
free(s);
close(fd); // NB: can do close(-1). Who cares?
- // spawn spool helper and exit if all files are dumped
- if (7 == spooling && *argv) {
- // signal OK
- safe_write(STDOUT_FILENO, "", 1);
- // does not return (exits 0)
- exec_helper(filenames, argv);
- }
+ // NB: don't do "signal OK" write here, it will be done
+ // at the top of the loop
} // while (1)
err_exit: