1 /*
2  * sqsh_job.c - Functions for launching commands
3  *
4  * Copyright (C) 1995, 1996 by Scott C. Gray
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, write to the Free Software
18  * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19  *
20  * You may contact the author :
21  *   e-mail:  gray@voicenet.com
22  *            grays@xtend-tech.com
23  *            gray@xenotropic.com
24  */
25 #include <stdio.h>
26 #include <sys/param.h>
27 #include <ctype.h>
28 #include <setjmp.h>
29 #include "sqsh_config.h"
30 #include "sqsh_error.h"
31 #include "sqsh_fd.h"
32 #include "sqsh_init.h"
33 #include "sqsh_tok.h"
34 #include "sqsh_cmd.h"
35 #include "sqsh_global.h"
36 #include "sqsh_fork.h"
37 #include "sqsh_expand.h"
38 #include "sqsh_strchr.h"
39 #include "sqsh_alias.h"
40 #include "sqsh_getopt.h"
41 #include "sqsh_sig.h"
42 #include "sqsh_job.h"
43 
44 /*-- Current Version --*/
45 #if !defined(lint) && !defined(__LINT__)
46 static char RCS_Id[] = "$Id: sqsh_job.c,v 1.11 2014/03/14 17:24:53 mwesdorp Exp $";
47 USE(RCS_Id)
48 #endif /* !defined(lint) */
49 
50 /*-- Local Prototypes --*/
51 static job_t*     job_alloc         _ANSI_ARGS((jobset_t*));
52 static int        job_free          _ANSI_ARGS((job_t*));
53 static job_id_t   jobset_wait_all   _ANSI_ARGS((jobset_t*, int*, int));
54 static int        jobset_parse      _ANSI_ARGS((jobset_t*, job_t*, char*,
55 						varbuf_t*, int));
56 static job_t*     jobset_get        _ANSI_ARGS((jobset_t*, job_id_t));
57 static void       jobset_run_sigint _ANSI_ARGS((int, void*));
58 static int        jobset_get_cmd    _ANSI_ARGS((jobset_t*, char*, cmd_t**));
59 static int        jobset_global_init _ANSI_ARGS((void));
60 
61 /*-- Status Globals --*/
62 static JMP_BUF sg_jmp_buf ;           /* Where to go on SIGINT */
63 
64 /*
65  * sg_cmd_buf: Variable length buffer to be used to expand the
66  *             current command to be executed.
67  */
68 static varbuf_t *sg_cmd_buf = NULL ;    /* Expanded command buffer */
69 
70 /*
71  * sg_alias_buf: Variable length buffer to be used when performing
72  *               alias expansion (prior to command expansion).
73  */
74 static varbuf_t *sg_alias_buf = NULL ;  /* Expanded alias buffer */
75 
76 /*
77  * sg_while_buf: Variable length buffer used to hold the (mostly)
78  *               unparsed command line for a \while statement.
79  */
80 static varbuf_t *sg_while_buf = NULL ;  /* Expanded alias buffer */
81 
82 /*
83  * jobset_create():
84  *
85  * Creates and attaches a new jobset structure to the current context.
86  * Upon success, True is return, otherwise False is returned.
87  */
jobset_create(hsize)88 jobset_t* jobset_create( hsize )
89 	int hsize;
90 {
91 	jobset_t   *js;
92 	int         i;
93 
94 	/*-- Always check your parameters --*/
95 	if( hsize < 1 ) {
96 		sqsh_set_error( SQSH_E_BADPARAM, NULL );
97 		return False;
98 	}
99 
100 	/*-- Attempt to allocate a new jobset structure --*/
101 	if( (js = (jobset_t*)malloc( sizeof( jobset_t ) )) == NULL ) {
102 		sqsh_set_error( SQSH_E_NOMEM, NULL );
103 		return NULL;
104 	}
105 
106 	/*-- Allocate the hash table for the job_id's --*/
107 	if( (js->js_jobs = (job_t**)malloc(sizeof(job_t*)*hsize)) == NULL ){
108 		free( js );
109 		sqsh_set_error( SQSH_E_NOMEM, NULL );
110 		return NULL;
111 	}
112 
113 	/*
114 	 * Allocate a sigcld_t.  This will act as a handle on all of the
115 	 * SIGCHLD events that will be received due to background jobs
116 	 * completing.
117 	 * sqsh-2.1.7 - Logical fix: Free js->js_jobs before a free of js.
118 	 */
119 	if( (js->js_sigcld = sigcld_create()) == NULL ) {
120 		free( js->js_jobs );
121 		free( js );
122 		sqsh_set_error( sqsh_get_error(), "sigcld_create: %s", sqsh_get_errstr() );
123 		return NULL;
124 	}
125 
126 	/*-- Initialize our command structure --*/
127 	js->js_hsize   = hsize;
128 
129 	for( i = 0; i < hsize; i++ )
130 		js->js_jobs[i] = NULL;
131 
132 	sqsh_set_error( SQSH_E_NONE, NULL );
133 	return js;
134 }
135 
136 
137 /*
138  * jobset_is_cmd():
139  *
140  * Returns a positive value of cmd_line is a command string, 0 if it
141  * does not, and a negative value upon error.
142  */
jobset_is_cmd(js,cmd_line)143 int jobset_is_cmd( js, cmd_line )
144 	jobset_t   *js;
145 	char       *cmd_line;
146 {
147 	cmd_t  *cmd;
148 
149 	sqsh_set_error( SQSH_E_NONE, NULL );
150 
151 	/*-- Alias' are assumed to be commands --*/
152 	if( alias_test( g_alias, cmd_line ) > 0 )
153 		return 1;
154 	return jobset_get_cmd( js, cmd_line, &cmd );
155 }
156 
157 /*
158  * jobset_get_cmd():
159  *
160  */
jobset_get_cmd(js,cmd_line,cmd)161 static int jobset_get_cmd( js, cmd_line, cmd )
162 	jobset_t   *js;
163 	char       *cmd_line;
164 	cmd_t     **cmd;
165 {
166 	tok_t      *tok ;               /* Token returned from sqsh_tok() */
167 
168 	/*
169 	 * The jobset_global_init() initializes the sg_cmd_buf global
170 	 * variable which we will need to expand the command name
171 	 * into.
172 	 */
173 	if( jobset_global_init() == False )
174 		return -1;
175 
176 	/*
177 	 * Now we want to expand just the first syntactical word of
178 	 * the command line into a temporary buffer.  If there is an
179 	 * error then we just assume that it is bad quoting or something
180 	 * like that and therefore this isn't a valid command line.
181 	 */
182 	if( sqsh_nexpand( cmd_line, sg_cmd_buf, 0, EXP_WORD ) == False )
183 		return 0;
184 
185 	DBG(sqsh_debug( DEBUG_JOB, "jobset_get_cmd: Testing '%s'\n",
186 			varbuf_getstr(sg_cmd_buf) );)
187 
188 	/*
189 	 * Try to grab the first token from the command line.  If there
190 	 * is any error while tokenizing, then we will assume that this
191 	 * isn't a command.
192 	 */
193 	if( sqsh_tok( varbuf_getstr( sg_cmd_buf ), &tok, 0 ) == False )
194 		return 0;
195 
196 	/*
197 	 * So, if the first token we retrieve isn't a word (i.e. it is a
198 	 * pipe, or a redirection or something), or we couldn't find the
199 	 * command in our global set of commands, then we don't have a
200 	 * command.
201 	 */
202 	if( tok->tok_type != SQSH_T_WORD ||
203 		(*cmd = cmdset_get(g_cmdset, sqsh_tok_value(tok))) == NULL )
204 		return 0;
205 
206 	return 1;
207 }
208 
209 /*
210  * jobset_run():
211  *
212  * Attempts to run the command contained in cmd_line.  If the job is
213  * succesfully run and completes (i.e. wasn't a background job), then
214  * 0 is returned and exit_status will contained the exit status of the
215  * command. If the job was succesfully started and is still running then
216  * a valid job_id_t is returned as a handle to the running job.
217  * If there was any sort of error then -1 is returned.
218  */
jobset_run(js,cmd_line,exit_status)219 job_id_t jobset_run( js, cmd_line, exit_status )
220 	jobset_t      *js;
221 	char          *cmd_line;
222 	int           *exit_status;
223 {
224 	cmd_t      *cmd ;                  /* The command being run */
225 	char        msg[512];
226 	int         error;
227 	pid_t       child_pid;
228 	int         ret;
229 	int         hval;
230 	job_t      *job;
231 	int         tok_flags;
232 
233 #if defined(USE_AIX_FIX)
234 	int          old_flags = (int)-1;
235 #endif /* USE_AIX_FIX */
236 	varbuf_t    *while_buf;
237 
238 	/*-- Check the arguments --*/
239 	if( js == NULL || cmd_line == NULL ) {
240 		sqsh_set_error( SQSH_E_BADPARAM, NULL );
241 		return -1;
242 	}
243 
244 	/*
245 	 * We need to initialize a couple of global buffers that
246 	 * we are going to use, namely sg_cmd_buf.
247 	 */
248 	if( jobset_global_init() == False )
249 		return -1;
250 
251 	/*
252 	 * Perform alias expansion.
253 	 */
254 	if( alias_expand( g_alias, cmd_line, sg_alias_buf ) > 0 )
255 		cmd_line = varbuf_getstr( sg_alias_buf );
256 
257 	/*
258 	 * Attempt to retrieve the command from the command line.  If it
259 	 * wasn't a command, then return an error condition.  jobset_get_cmd
260 	 * should take care of setting the appropriate error message for
261 	 * us.
262 	 */
263 	if( (ret = jobset_get_cmd( js, cmd_line, &cmd )) <= 0 ) {
264 		if( ret == 0 )
265 			sqsh_set_error( SQSH_E_EXIST, "Invalid command" );
266 		return -1;
267 	}
268 
269 	/*
270 	 * Token flags control how the tokenizer behaves.
271 	 */
272 	tok_flags = 0;
273 
274 	/*
275 	 * When parsing the \if expression, expand '[' and ']' to be
276 	 * a test statement.
277 	 */
278 	if (strcmp( cmd->cmd_name, "\\if" ) == 0)
279 	{
280 		tok_flags |= TOK_F_TEST;
281 	}
282 
283 	/*
284 	 * \while is a special case. We need to make sure that
285 	 * we do *not* expand the command line of variables and
286 	 * that the command line is passed in as a single string.
287 	 */
288 	if (strcmp( cmd->cmd_name, "\\while" ) == 0)
289 	{
290 		/*
291 		 * Leave all quotes and other goodies in-tact.
292 		 */
293 		tok_flags |= TOK_F_LEAVEQUOTE|TOK_F_TEST;
294 
295 		/*
296 		 * We'll pass this varbuf_t into the tokenizer to fill with
297 		 * the unparsed command line.
298 		 */
299 		while_buf = sg_while_buf;
300 		varbuf_clear( while_buf );
301 	}
302 	else
303 	{
304 		/*
305 		 * Let the parser know that this isn't a \while statement.
306 		 */
307 		while_buf = NULL;
308 
309 		/*
310 		 * Expand the command line of any tokens, keeping quotes and stripping
311 		 * escape characters (i.e. removing them as part of the expansion
312 		 * process).
313 		 * sqsh-2.5 : Implemented tilde expansion in sqsh_expand().
314 		 */
315 		if (sqsh_expand( cmd_line, sg_cmd_buf, EXP_COLUMNS | EXP_TILDE ) == False)
316 		{
317 			sqsh_set_error( sqsh_get_error(), "sqsh_expand: %s", sqsh_get_errstr() );
318 			return -1;
319 		}
320 
321 		/*
322 		 * The "actual" command line is the version that we just expanded
323 		 * of any variables.
324 		 */
325 		cmd_line = varbuf_getstr( sg_cmd_buf );
326 	}
327 
328 	/*
329 	 * It was a command, so lets allocate a new job structure for
330 	 * the duration of the command.
331 	 */
332 	if ((job = job_alloc(js)) == NULL)
333 	{
334 		sqsh_set_error( SQSH_E_NOMEM, NULL );
335 		return -1;
336 	}
337 
338 	/*
339 	 * Well, it looks like we are in for the long hual.  The first
340 	 * thing we need to do at this point is back up the state of all
341 	 * of our file descriptors. This allows jobset_parse() to do all
342 	 * of the opening and closing of file descriptors that it could
343 	 * possible want to and a single call to sqsh_frestore() will
344 	 * restore the current state.
345 	 */
346 	if (sqsh_fsave() == -1)
347 	{
348 		sqsh_set_error( sqsh_get_error(), "sqsh_fsave: %s", sqsh_get_errstr() );
349 		job_free( job );
350 		return -1;
351 	}
352 
353 #if defined(USE_AIX_FIX)
354 	old_flags = stdout->_flag;
355 #endif /* USE_AIX_FIX */
356 
357 	/*
358 	 * Now, let jobset_parse() do its thing and redirect stdout
359 	 * and the such as necessary.  If it has an error then we just send
360 	 * it back with an error code.
361 	 */
362 	if (jobset_parse( js, job, cmd_line, while_buf, tok_flags ) == False)
363 	{
364 		goto jobset_abort;
365 	}
366 
367 	/*
368 	 * If this was a \while statement, then the sole argument to the
369 	 * function will be the unexpanded command line stripped of
370 	 * all redirection crap.
371 	 */
372 	if (while_buf != NULL)
373 	{
374 		if (args_add(job->job_args, "\\while" ) == False ||
375 			args_add(job->job_args, varbuf_getstr(while_buf)) == False)
376 		{
377 			sqsh_set_error( sqsh_get_error(), "args_add: %s", sqsh_get_errstr() );
378 			goto jobset_abort;
379 		}
380 	}
381 
382 	/*
383 	 * It parsed OK, so now job->job_args contains the arguments to
384 	 * the command and stdout and stderr are redirected as needed.
385 	 * the only thing left is to fork if necessary and call the
386 	 * appropriate function.
387 	 */
388 	if (job->job_flags & JOB_BG)
389 	{
390 		/*
391 		 * Perform the actual fork().  Sqsh_fork() will take care of
392 		 * resetting certain global variables in the context of the
393 		 * child.
394 		 */
395 		switch ((child_pid = sqsh_fork()))
396 		{
397 			case -1 :  /* Error */
398 				goto jobset_abort;
399 
400 			/*
401 			 * Child process.  There really isn't much to do here other
402 			 * than call the requested cmd and exit with the return value
403 			 * of that command. The parent will receive the exit status.
404 			 */
405 			case  0 :  /* Child process */
406 				/*
407 				 * We want the child to process signals a little differently
408 				 * from the parent.  There is no real need for graceful re-
409 				 * covery within the child.
410 				 * sqsh-2.1.7 - Ignore SIGINT (Ctrl-C) interrupts from parent.
411 				 *              Let the child perform a proper sqsh_exit().
412 				 *              Return code is not very relevant here.
413 				 */
414 				while (sig_restore() >= 0);
415 				sig_install ( SIGINT, SIG_H_IGN, NULL, 0 );
416 				sqsh_getopt_reset();
417 				ret = cmd->cmd_ptr( args_argc(job->job_args),
418 					args_argv(job->job_args) );
419 				sqsh_exit(0);
420 
421 			/*
422 			 * Parent process. All we need to do is record the pid of the
423 			 * child in the job structure, restore the  file descriptors
424 			 * and return.
425 			 * sqsh-2.1.7 - Make sure SIGCHLD signals are unblocked.
426 			 */
427 			default :  /* Parent */
428 
429 				job->job_pid = child_pid;
430 				sigcld_watch( js->js_sigcld, job->job_pid );
431 				sigcld_unblock () ;
432 		}
433 
434 		/*
435 		 * Restore the file descriptors redirected during jobset_parse()
436 		 * I should probably do something if this fails, but I can't think
437 		 * of any way to recover.
438 		 */
439 		sqsh_frestore();
440 
441 		/*
442 		 * The following is required on those systems in which EOF isn't
443 		 * checked each time a read operation is attempted on a FILE
444 		 * structure.  This is in case stdin was temporarily redirected
445 		 * from a file that has reached EOF.
446 		 */
447 		clearerr( stdin );
448 
449 		/*
450 		 * Link the new job into our hash table.
451 		 */
452 		hval = job->job_id % js->js_hsize;
453 		job->job_nxt = js->js_jobs[hval];
454 		js->js_jobs[hval] = job;
455 
456 		/*-- And return it --*/
457 		return job->job_id;
458 	}
459 
460 	/*
461 	 * This isn't a background job, so we just need to call the command
462 	 * and return its exit status. We need to install a SIGINT handle
463 	 * to capture any ^C's from a user.  In this case we want the
464 	 * ^C to return immediately before the sqsh_restore() call below.
465 	 */
466 	sig_save();
467 
468 	sig_install( SIGINT, jobset_run_sigint, (void*)NULL, 0 );
469 	if (SETJMP( sg_jmp_buf ) == 0)
470 	{
471 		/*
472 		 * Since most commands use sqsh_getopt() to parse the command
473 		 * line options, we want to ensure that sqsh_getopt() will
474 		 * know that it is receiving a new argv[] and argc so we want
475 		 * to reset its existing impression of these two arguments.
476 		 */
477 		sqsh_getopt_reset();
478 
479 		/*
480 		 * If this is a pipe-line then we want to ignore the SIGPIPE.  Let the
481 		 * command itself set up a handler for it if it wishes.
482 		 */
483 		if (job->job_flags & JOB_PIPE)
484 		{
485 
486 			sig_install( SIGPIPE, SIG_H_POLL, (void*)NULL, 0 );
487 			*exit_status = cmd->cmd_ptr( args_argc(job->job_args), args_argv(job->job_args) );
488 
489 		}
490 		else
491 		{
492 			*exit_status = cmd->cmd_ptr( args_argc(job->job_args), args_argv(job->job_args) );
493 		}
494 	}
495 
496 	/*-- Restore signal handlers --*/
497 	sig_restore();
498 
499 	/*
500 	 * Restore the file descriptors that were redirected during the
501 	 * process of parsing the command line.
502 	 */
503 	sqsh_frestore();
504 
505 	/*
506 	 * As with before, the following is required just in case stdin
507 	 * hit EOF while redirected from a file or some other file
508 	 * descriptor.  On some systems EOF sticks with the FILE structure
509 	 * until clearerr() is called.
510 	 */
511 	clearerr( stdin );
512 
513 #if defined(USE_AIX_FIX)
514 	stdout->_flag = old_flags;
515 #endif /* USE_AIX_FIX */
516 
517 	/*
518 	 * Lastly, destroy the job structure that we allocated.  It almost
519 	 * makes you wonder why it was created at all.
520 	 */
521 	job_free( job );
522 
523 	/*
524 	 * Note, reguardless of the exit status of the command, we return 0
525 	 * (i.e. the command was called succesfully reguarldess of whether
526 	 * or not the job performed its task.
527 	 */
528 	sqsh_set_error( SQSH_E_NONE, NULL );
529 	return 0;
530 
531 jobset_abort:
532 	/*
533 	 * This sucks.  Unfortunately I want to return the error condition
534 	 * returned by jobset_parse(), but the functions sqsh_frestore() and
535 	 * job_free() probably set the sqsh error condition, so we need to
536 	 * save the current error condition prior to calling them.
537 	 * There must be a better way of doing this.
538 	 */
539 	strcpy( msg, sqsh_get_errstr() );
540 	error = sqsh_get_error();
541 	sqsh_frestore();
542 	clearerr( stdin );
543 #if defined(USE_AIX_FIX)
544 	if (old_flags != (int)-1)
545 	{
546 		stdout->_flag = old_flags;
547 	}
548 #endif /* USE_AIX_FIX */
549 	job_free( job );
550 	sqsh_set_error( error, msg );
551 	return -1;
552 }
553 
554 /*
555  * jobset_run_sigint():
556  *
557  * This little stub is used by jobset_run() to capture SIGINT from a
558  * user and return to the point immedately after the command was executed..
559  */
jobset_run_sigint(sig,user_data)560 static void jobset_run_sigint( sig, user_data )
561 	int sig;
562 	void *user_data;
563 {
564 	LONGJMP( sg_jmp_buf, 1 );
565 }
566 
567 /*
568  * jobset_wait():
569  *
570  * Waits for job given by job_id to complete execution (if job_id is
571  * a negative value then jobset_wait() waits for any pending job to
572  * complete).  If block_type is JOBSET_NONBLOCK and the job has not
573  * completed then 0 is returned and exit_status contains an undefined
574  * value, otherwise, if block_type is JOBSET_BLOCK, the job_id of
575  * the completed job is returned with exit_status containing the exit
576  * value of the job. If an error condition ocurres, or job_id is not a
577  * valid pending job then -1 is returned.
578  */
jobset_wait(js,job_id,exit_status,block_type)579 job_id_t jobset_wait( js, job_id, exit_status, block_type )
580 	jobset_t   *js;
581 	job_id_t    job_id;
582 	int        *exit_status;
583 	int         block_type;
584 {
585 	job_t      *j;
586 	int         wait_type;
587 	pid_t       pid;
588 
589 	/*-- Always check your parameters --*/
590 	if( js == NULL || exit_status == NULL || (block_type != JOB_BLOCK && block_type != JOB_NONBLOCK) ) {
591 		sqsh_set_error( SQSH_E_BADPARAM, NULL );
592 		return -1;
593 	}
594 
595 	if( job_id < 0 )
596 		return jobset_wait_all( js, exit_status, block_type );
597 
598 	/*-- Find out if the job exists --*/
599 	/* (sqsh-2.1.7 - Bug fix for bucket calculation) --*/
600 	for( j = js->js_jobs[job_id % js->js_hsize];
601 		j != NULL && j->job_id != job_id ; j = j->job_nxt );
602 
603 	/*-- If we can't, error --*/
604 	if( j == NULL ) {
605 		sqsh_set_error( SQSH_E_EXIST, NULL );
606 		return -1;
607 	}
608 
609 	/*
610 	 * If the job has already been recorded as being complete then
611 	 * just return the necessary information to the caller.
612 	 */
613 	if( j->job_flags & JOB_DONE ) {
614 		*exit_status = j->job_status;
615 		return j->job_id;
616 	}
617 
618 	/*
619 	 * The type of sigcld wait that will be performed depends on
620 	 * what kind of blocking is requested by the caller.
621 	 */
622 	wait_type = (block_type == JOB_BLOCK) ? SIGCLD_BLOCK : SIGCLD_NONBLOCK;
623 
624 	/*
625 	 * Go ahead and wait for the completion of the job.
626 	 */
627 	pid = sigcld_wait( js->js_sigcld, j->job_pid, exit_status, wait_type );
628 
629 	/*
630 	 * If block_type was JOB_NONBLOCK and there were no jobs pending,
631 	 * then return 0.
632 	 */
633 	if( pid == 0 ) {
634 		sqsh_set_error( SQSH_E_NONE, NULL );
635 		return 0;
636 	}
637 
638 	/*
639 	 * If we have reached this point, then the job has completed so
640 	 * it is just a matter of recording the exit status and returning.
641 	 */
642 	j->job_flags  |= JOB_DONE;
643 	j->job_end     = time(NULL);
644 	j->job_status  = *exit_status;
645 
646 	/*
647 	 * Propagate any errors back to the caller.
648 	 * sqsh-2.1.7 - If we missed a SIGCLD signal and the pid is already
649 	 * finished, then remove the pid from the watch list, report an error
650 	 * and continue as normal. Then you can use \show to check the deferred
651 	 * output so far and get the job out of the queue.
652 	 */
653 	if( pid == -1 ) {
654 		sigcld_unwatch ( js->js_sigcld, j->job_pid );
655 		sqsh_set_error( sqsh_get_error(), "sigcld_wait: %s", sqsh_get_errstr() );
656 	}
657 	else {
658 		sqsh_set_error( SQSH_E_NONE, NULL );
659 	}
660 
661 	return j->job_id;
662 }
663 
jobset_end(js,job_id)664 int jobset_end( js, job_id )
665 	jobset_t   *js;
666 	job_id_t    job_id;
667 {
668 	job_t      *j, *j_prv;
669 	int         hval;
670 	int         exit_status;
671 
672 	/*-- Always check your parameters --*/
673 	if( js == NULL || job_id <= 0 ) {
674 		sqsh_set_error( SQSH_E_BADPARAM, NULL );
675 		return False;
676 	}
677 
678 	/*-- Calculate which bucket job_id is in --*/
679 	hval = job_id % js->js_hsize;
680 
681 	/*-- Search for the job --*/
682 	j_prv = NULL;
683 	for( j = js->js_jobs[hval]; j != NULL && j->job_id != job_id;
684 		j = j->job_nxt )
685 		j_prv = j;
686 
687 	if( j == NULL ) {
688 		sqsh_set_error( SQSH_E_EXIST, "Invalid job_id %d", job_id );
689 		return False;
690 	}
691 
692 	/*
693 	 * If the job hasn't completed yet, then we need to kill the process
694 	 * associated with the job and wait for it to complete.
695 	 */
696 	if( !(j->job_flags & JOB_DONE) ) {
697 
698 		/*-- Kill the job --*/
699 		if( kill( j->job_pid, SIGTERM ) == -1 ) {
700 			sqsh_set_error( errno, "Unable to SIGTERM pid %d: %s", j->job_pid, strerror( errno ) );
701 			return False;
702 		}
703 
704 		/*-- Wait for it to die. --*/
705 		if( sigcld_wait( js->js_sigcld, j->job_pid, &exit_status, SIGCLD_BLOCK ) == -1 ) {
706 			sqsh_set_error( sqsh_get_error(), "sigcld_wait: %s", sqsh_get_errstr() );
707 			return False;
708 		}
709 	}
710 
711 	/*
712 	 * Finally unlink it from the list of jobs that are running.
713 	 */
714 	if( j_prv != NULL )
715 		j_prv->job_nxt = j->job_nxt;
716 	else
717 		js->js_jobs[hval] = j->job_nxt;
718 
719 	job_free( j );
720 	sqsh_set_error( SQSH_E_NONE, NULL );
721 	return True;
722 }
723 
724 
725 /*
726  * jobset_clear():
727  *
728  * Clears out all references to currently running jobs without
729  * actually terminating the jobs.
730  */
jobset_clear(js)731 int jobset_clear( js )
732 	jobset_t    *js;
733 {
734 	job_t     *j, *j_nxt;
735 	int        i;
736 
737 	/*-- Check your parameters --*/
738 	if( js == NULL ) {
739 		sqsh_set_error( SQSH_E_BADPARAM, NULL );
740 		return False;
741 	}
742 
743 	/*-- Blast through every bucket in hash table --*/
744 	for( i = 0; i < js->js_hsize; i++ ) {
745 
746 		/*-- Blast through every job in bucket --*/
747 		j = js->js_jobs[i];
748 		while( j != NULL ) {
749 
750 			/*-- Keep pointer to next job in bucket --*/
751 			j_nxt = j->job_nxt;
752 
753 			/*-- We no longer care when this job terminates --*/
754 			sigcld_unwatch( js->js_sigcld, j->job_pid );
755 
756 			/*-- Destroy the job --*/
757 			job_free( j );
758 
759 			/*-- Move on --*/
760 			j = j_nxt;
761 		}
762 	}
763 
764 	sqsh_set_error( SQSH_E_NONE, NULL );
765 	return True;
766 }
767 
jobset_is_done(js,job_id)768 int jobset_is_done( js, job_id )
769 	jobset_t   *js;
770 	job_id_t    job_id;
771 {
772 	job_t  *j;
773 
774 	if( (j = jobset_get( js, job_id )) == NULL )
775 		return -1;
776 
777 	sqsh_set_error( SQSH_E_NONE, NULL );
778 
779 	if( j->job_flags & JOB_DONE )
780 		return 1;
781 	return 0;
782 }
783 
jobset_get_defer(js,job_id)784 char* jobset_get_defer( js, job_id )
785 	jobset_t   *js;
786 	job_id_t    job_id;
787 {
788 	job_t  *j;
789 
790 	if( (j = jobset_get( js, job_id )) == NULL )
791 		return NULL;
792 
793 	sqsh_set_error( SQSH_E_NONE, NULL );
794 	return j->job_output;
795 }
796 
jobset_get_pid(js,job_id)797 pid_t jobset_get_pid( js, job_id )
798 	jobset_t   *js;
799 	job_id_t    job_id;
800 {
801 	job_t  *j;
802 
803 	if( (j = jobset_get( js, job_id )) == NULL )
804 		return -1;
805 
806 	sqsh_set_error( SQSH_E_NONE, NULL );
807 	return j->job_pid;
808 }
809 
jobset_get_status(js,job_id)810 int jobset_get_status( js, job_id )
811 	jobset_t   *js;
812 	job_id_t    job_id;
813 {
814 	job_t  *j;
815 
816 	if( (j = jobset_get( js, job_id )) == NULL )
817 		return -1;
818 
819 	sqsh_set_error( SQSH_E_NONE, NULL );
820 	return j->job_status;
821 }
822 
823 /*
824  * jobset_destroy():
825  *
826  * Destroys a jobset structure, sending a SIGTERM signal to all
827  * child jobs within it that have not yet completed.  Compare and
828  * contrast to jobset_clear().  True is returned upon success, False
829  * upon failure.
830  */
jobset_destroy(js)831 int jobset_destroy( js )
832 	jobset_t     *js;
833 {
834 	int     i;
835 	job_t  *j, *j_nxt;
836 	int     exit_status;
837 
838 	/*-- Stupid programmers --*/
839 	if( js == NULL ) {
840 		sqsh_set_error( SQSH_E_BADPARAM, NULL );
841 		return False;
842 	}
843 
844 	for( i = 0; i < js->js_hsize; i++ ) {
845 		j = js->js_jobs[i];
846 		while( j != NULL ) {
847 			j_nxt = j->job_nxt;
848 
849 			/*
850 			 * If the job hasn't completed yet, then we need to kill the process
851 			 * associated with the job and wait for it to complete.
852 			 */
853 			if( !(j->job_flags & JOB_DONE) ) {
854 
855 				/*
856 				 * Well, I should probably be paying attention to the return
857 				 * values of the next two calls, but if they fail I really
858 				 * don't know what to do about it anyway.  Either way the
859 				 * child process is going down.
860 				 */
861 				kill( j->job_pid, SIGTERM );
862 				sigcld_wait( js->js_sigcld, j->job_pid, &exit_status, SIGCLD_BLOCK );
863 			}
864 
865 			job_free( j );
866 			j = j_nxt;
867 		}
868 	}
869 
870 	free( js->js_jobs );
871 
872 	/*-- Destroy sigcld context structure --*/
873 	sigcld_destroy( js->js_sigcld );
874 
875 	/*-- Finally, free the jobset --*/
876 	free( js );
877 
878 	sqsh_set_error( SQSH_E_NONE, NULL );
879 	return True;
880 }
881 
882 /*****************************************************************
883  ** INTERNAL FUNTIONS
884  *****************************************************************/
885 
886 /*
887  * jobset_wait_all():
888  */
jobset_wait_all(js,exit_status,block_type)889 static job_id_t jobset_wait_all( js, exit_status, block_type )
890 	jobset_t   *js;
891 	int        *exit_status;
892 	int         block_type;
893 {
894 	job_t      *j;
895 	pid_t       pid;
896 	int         wait_type;
897 	int         i;
898 
899 	/*
900 	 * The type of sigcld wait that will be performed depends on
901 	 * what kind of blocking is requested by the caller.
902 	 */
903 	wait_type = (block_type == JOB_BLOCK) ? SIGCLD_BLOCK : SIGCLD_NONBLOCK;
904 
905 	/*
906 	 * Go ahead and wait for the completion of the job.
907 	 */
908 	pid = sigcld_wait( js->js_sigcld, -1, exit_status, wait_type );
909 
910 	/*
911 	 * Propagate any errors back to the caller.
912 	 */
913 	if( pid == -1 ) {
914 		sqsh_set_error( sqsh_get_error(), "sigcld_wait: %s", sqsh_get_errstr() );
915 		return -1;
916 	}
917 
918 	/*
919 	 * If block_type was JOB_NONBLOCK and there were no jobs pending,
920 	 * then return 0.
921 	 */
922 	if( pid == 0 ) {
923 		sqsh_set_error( SQSH_E_NONE, NULL );
924 		return 0;
925 	}
926 
927 	/*
928 	 * The following two loops rather inneficiently looks up the
929 	 * job_t structure that represents the completed pid.
930 	 */
931 	for( i = 0; i < js->js_hsize; i++ ) {
932 
933 		for( j = js->js_jobs[i]; j != NULL && j->job_pid != pid;
934 			j = j->job_nxt );
935 
936 		if( j != NULL ) {
937 
938 			/*
939 			 * If we have reached this point, then the job has completed so
940 			 * it is just a matter of recording the exit status and returning.
941 			 */
942 			j->job_flags  |= JOB_DONE;
943 			j->job_end     = time(NULL);
944 			j->job_status  = *exit_status;
945 
946 			sqsh_set_error( SQSH_E_NONE, NULL );
947 			return j->job_id;
948 		}
949 	}
950 
951 	/*
952 	 * If we get here then we have trouble...we have received a sigcld
953 	 * for a pid that we don't have registered as belonging to a job.
954 	 * Hmmm...what to do?
955 	 */
956 	sqsh_set_error( SQSH_E_EXIST, "Received SIGCHLD for unknown pid!" );
957 	return -1;
958 }
959 
960 /*
961  * jobset_parse():
962  *
963  * This giant beastie of a function is responsible for parsing the
964  * command line for a job function.  While it proceeds it redirects
965  * any file descriptors as requested by the command line.  Upon success
966  * various fields of job will be set indicating option and arguments
967  * on the command line and true is returned.  Upon failure the status
968  * of all file descriptors and the contents of the job structure
969  * is undefined, False is returned.
970  */
jobset_parse(js,job,cmd_line,while_buf,tok_flags)971 static int jobset_parse( js, job, cmd_line, while_buf, tok_flags )
972 	jobset_t    *js;
973 	job_t       *job;
974 	char        *cmd_line;
975 	varbuf_t    *while_buf;
976 	int          tok_flags;
977 {
978 	tok_t             *tok ;               /* Token read with sqsh_tok() */
979 	int                flag ;              /* Flag to be passed to open() */
980 	char              *defer_bg ;          /* Results from env_get() */
981 	char              *tmp_dir ;           /* Results from env_get() */
982 	char               defer_path[SQSH_MAXPATH+1];
983 	int                defer_fd;
984 	int                fd;
985 	char              *bg_ptr   = NULL ; /* Location of & */
986 	char              *pipe_ptr = NULL ; /* Location of | */
987 	char              *cp;
988 	/* sqsh-2.1.6 - New variables */
989 	varbuf_t	  *exp_buf  = NULL ;
990 
991 
992 	/*
993 	 * In a "real" shell both the | pipeline(s) and the & background
994 	 * characters take presedence during command line parsing.  In
995 	 * order to simulate this we first must determine if we have either
996 	 * of these characters in our command line.
997 	 */
998 	pipe_ptr = sqsh_strchr( cmd_line, '|' );
999 
1000 	/*
1001 	 * Look for a & that isn't preceded by > (which would be an ocurrance
1002 	 * of the &> token).
1003 	 */
1004 	cp = cmd_line;
1005 	while( (bg_ptr = sqsh_strchr( cp, '&' )) != NULL &&
1006 		bg_ptr != cmd_line && *(bg_ptr - 1) == '>' )
1007 		cp = bg_ptr + 1;
1008 
1009 	/*
1010 	 * Now, verify that the background character is the last character
1011 	 * on the line.
1012 	 */
1013 	if( bg_ptr != NULL ) {
1014 		/*-- Skip trailing white space --*/
1015 		for( cp = bg_ptr + 1; *cp != '\0' && isspace((int)*cp); ++cp );
1016 
1017 		if( *cp != '\0' ) {
1018 			sqsh_set_error( SQSH_E_SYNTAX, "& must be last token on line" );
1019 			return False;
1020 		}
1021 	}
1022 
1023 	/*
1024 	 * All-righty then.  Now, the first thing we need to do is, if we
1025 	 * are to be run in the background the create the necessary defer
1026 	 * file and attach it to our stdout and stderr.  If, later during
1027 	 * the parsing the user redirects one of these streams it won't
1028 	 * really hurt anything.
1029 	 */
1030 	if( bg_ptr != NULL ) {
1031 
1032 		/*-- Mark this as a background job --*/
1033 		job->job_flags |= JOB_BG;
1034 
1035 		env_get( g_env, "defer_bg", &defer_bg );
1036 
1037 		if( defer_bg != NULL && *defer_bg != '0' ) {
1038 
1039 			/*-- Mark this job as having deferred data --*/
1040 			job->job_flags |= JOB_DEFER;
1041 
1042 			/*-- Check to see if the user wants to override --*/
1043 			env_get( g_env, "tmp_dir", &tmp_dir );
1044 
1045 			if( tmp_dir == NULL || *tmp_dir == '\0' )
1046 				tmp_dir = SQSH_TMP;
1047 			else /* sqsh-2.1.6 feature - Expand tmp_dir variable */
1048 			{
1049 				if ((exp_buf = varbuf_create( 512 )) == NULL)
1050 				{
1051 					sqsh_set_error( sqsh_get_error(), "varbuf: %s", sqsh_get_errstr() );
1052 					return False;
1053 				}
1054 				if (sqsh_expand( tmp_dir, exp_buf, 0 ) == False)
1055 					tmp_dir = SQSH_TMP;
1056 				else
1057 					tmp_dir = varbuf_getstr( exp_buf );
1058 			}
1059 
1060 			/*-- Create the defer file --*/
1061 			sprintf( defer_path, "%s/sqsh-dfr.%d-%d", tmp_dir, (int) getpid(), job->job_id );
1062 			if ( exp_buf != NULL )
1063 				varbuf_destroy( exp_buf ); /* sqsh-2.1.6 feature */
1064 
1065 			/*-- Let the job structure know where it is --*/
1066 			if( (job->job_output = sqsh_strdup( defer_path )) == NULL ) {
1067 				sqsh_set_error( SQSH_E_NOMEM, NULL );
1068 				return False;
1069 			}
1070 
1071 			/*-- Now, open the file --*/
1072 			if( (defer_fd = sqsh_open(defer_path,O_CREAT|O_WRONLY|O_TRUNC,0600)) == -1 ) {
1073 				sqsh_set_error( sqsh_get_error(), "Unable to open %s: %s", defer_path, sqsh_get_errstr() );
1074 				return False;
1075 			}
1076 
1077 			/*
1078 			 * Now, simply attach the new file descriptor onto
1079 			 * stdout and stderr.
1080 			 */
1081 			if( sqsh_dup2( defer_fd, fileno(stdout) ) == -1 ||
1082 				sqsh_dup2( defer_fd, fileno(stderr) ) == -1 ) {
1083 				sqsh_set_error( sqsh_get_error(), "sqsh_dup2: %s", sqsh_get_errstr() );
1084 				sqsh_close( defer_fd );
1085 				return False;
1086 			}
1087 
1088 			/*-- No longer needed --*/
1089 			sqsh_close( defer_fd );
1090 		}
1091 	}
1092 
1093 	/*
1094 	 * Ok, now we need to deal with the pipe-line.  If there is one
1095 	 * to be had, we need to crank the sucker up and attach it to
1096 	 * our stdout.  If this is a background process, the pipeline
1097 	 * will inherit our deferred stdout and stderr.
1098 	 */
1099 	if( pipe_ptr != NULL ) {
1100 
1101 		/*
1102 		 * Temporarily, if the bg_ptr is pointing to the trailing
1103 		 * '&' character then turn it to a '\0'.  We'll restore it
1104 		 * later.  General note:  Because we know that cmd_line is
1105 		 * actually a varbuf_t (behaps a rash assumption), we also
1106 		 * know that it is alterable.
1107 		 */
1108 		if( bg_ptr != NULL )
1109 			*bg_ptr = '\0';
1110 
1111 		/*-- Mark this as having a pipe-line --*/
1112 		job->job_flags |= JOB_PIPE;
1113 
1114 		/*
1115 		 * Check to see if this is an empty pipe-line.  That is,
1116 		 * if anything actually follow the | character.
1117 		 */
1118 		for( cp = pipe_ptr + 1; *cp != '\0' && isspace((int)*cp); ++cp );
1119 		if( *cp == '\0' ) {
1120 			sqsh_set_error( SQSH_E_SYNTAX, "Nothing following |" );
1121 			return False;
1122 		}
1123 
1124 		/*
1125 		 * Now, go ahead and crank up the process that we want to
1126 		 * communicate with.
1127 		 */
1128 		if( (fd = sqsh_popen( pipe_ptr+1, "w", NULL, NULL )) == -1 )
1129 		{
1130 			env_set( g_internal_env, "?", "-255" );
1131 			sqsh_set_error( sqsh_get_error(), "sqsh_popen: %s", sqsh_get_errstr());
1132 			return False;
1133 		}
1134 
1135 		/*
1136 		 * Now attempt to attach the file descriptor that we just
1137 		 * created to our stdout.
1138 		 */
1139 		if( sqsh_dup2( fd, fileno(stdout) ) == -1 ) {
1140 			sqsh_set_error( sqsh_get_error(), "sqsh_dup2: %s", sqsh_get_errstr() );
1141 			sqsh_close( fd );
1142 			return False;
1143 		}
1144 
1145 		/*-- Don't need this any more --*/
1146 		sqsh_close( fd );
1147 
1148 		/*
1149 		 * If necessary, restore the background character to where
1150 		 * we found it.  This is supposed to be a non-destructive
1151 		 * function call.
1152 		 */
1153 		if( bg_ptr != NULL )
1154 			*bg_ptr = '&';
1155 	}
1156 
1157 	/*
1158 	 * Retrieve the first token from the cmd_line.  This really shouldn't
1159 	 * fail, since we have already done it in jobset_run(), above.
1160 	 */
1161 	if( sqsh_tok( cmd_line, &tok, tok_flags ) == False ) {
1162 		sqsh_set_error( sqsh_get_error(), "sqsh_tok: %s", sqsh_get_errstr() );
1163 		return False;
1164 	}
1165 
1166 	/*
1167 	 * We are going to continue looking at tokens until we reach the
1168 	 * end of the command line.  It should be noted that according to
1169 	 * the logic for sqsh_tok(), the SQSH_T_EOF is considered either
1170 	 * when we actually reach the end of the line or we hit a |
1171 	 * or the & (background) character.
1172 	 */
1173 	while( tok->tok_type != SQSH_T_EOF ) {
1174 
1175 		switch( tok->tok_type ) {
1176 
1177 			case SQSH_T_REDIR_OUT :      /* [n]> file, or [n]>> file */
1178 
1179 				/*
1180 				 * Attempt to retrieve the name of the file to redirect
1181 				 * the file descriptor to.
1182 				 */
1183 				if( sqsh_tok( NULL, &tok, tok_flags ) == False ) {
1184 					sqsh_set_error( sqsh_get_error(), "sqsh_tok: %s", sqsh_get_errstr() );
1185 					return False;
1186 				}
1187 
1188 				/*
1189 				 * If the next token we retrieve isn't the name of a file, then
1190 				 * we have a syntax error.
1191 				 */
1192 				if( tok->tok_type != SQSH_T_WORD ) {
1193 					sqsh_set_error( SQSH_E_SYNTAX, "Expected file following >" );
1194 					return False;
1195 				}
1196 
1197 				/*
1198 				 * Figure out which flags we need to supply to open, if the
1199 				 * file is to be appended to, or created/overwritten.
1200 				 */
1201 				if( tok->tok_append )
1202 					flag = O_WRONLY | O_CREAT | O_APPEND;
1203 				else
1204 					flag = O_WRONLY | O_CREAT | O_TRUNC;
1205 
1206 				/*-- Now, open the file --*/
1207 				cp = sqsh_tok_value(tok);
1208 				if( (fd = sqsh_open( cp, flag, 0 )) == -1 ) {
1209 					sqsh_set_error( sqsh_get_error(), "Unable to open %s: %s", cp, sqsh_get_errstr() );
1210 					return False;
1211 				}
1212 
1213 				/*
1214 				 * Now, redirect the file descriptor requested by the user
1215 				 * to the newly opened file and close the file.
1216 				 */
1217 				if( sqsh_dup2( fd, tok->tok_fd_left ) == -1 ) {
1218 					sqsh_set_error( sqsh_get_error(), "sqsh_dup2: %s", sqsh_get_errstr() );
1219 					return False;
1220 				}
1221 				sqsh_close( fd );
1222 
1223 				break;
1224 
1225 			case SQSH_T_REDIR_IN :  /* '< filename' */
1226 				/*
1227 				 * Attempt to retrieve the name of the file to redirect
1228 				 * in from.
1229 				 */
1230 				if( sqsh_tok( NULL, &tok, tok_flags ) == False ) {
1231 					sqsh_set_error( sqsh_get_error(), "sqsh_tok: %s", sqsh_get_errstr() );
1232 					return False;
1233 				}
1234 
1235 				/*
1236 				 * If the next token we retrieve isn't the name of a file, then
1237 				 * we have a syntax error.
1238 				 */
1239 				if( tok->tok_type != SQSH_T_WORD ) {
1240 					sqsh_set_error( SQSH_E_SYNTAX, "Expected file following <" );
1241 					return False;
1242 				}
1243 
1244 				/*-- Now, open the file --*/
1245 				cp = sqsh_tok_value(tok);
1246 				if( (fd = sqsh_open( cp, O_RDONLY, 0 )) == -1 ) {
1247 					sqsh_set_error( sqsh_get_error(), "%s: %s", cp, sqsh_get_errstr() );
1248 					return False;
1249 				}
1250 
1251 				if( (sqsh_dup2( fd, fileno(stdin) )) == -1 ) {
1252 					sqsh_set_error( sqsh_get_error(), "%s: %s", cp, sqsh_get_errstr() );
1253 					return False;
1254 				}
1255 				sqsh_close( fd );
1256 				break;
1257 
1258 			case SQSH_T_REDIR_DUP :   /* '[m]>&[n]' */
1259 				/*
1260 				 * This one is easy.  Simply call sqsh_dup2() to do the
1261 				 * dirty work.
1262 				 */
1263 				if( sqsh_dup2( tok->tok_fd_right, tok->tok_fd_left ) == -1 ) {
1264 					sqsh_set_error( sqsh_get_error(), "sqsh_dup2: %s", sqsh_get_errstr() );
1265 					return False;
1266 				}
1267 
1268 				break;
1269 
1270 			case SQSH_T_WORD :
1271 				/*
1272 				 * If the token is a generic word on the command line then
1273 				 * we add it to the list of arguments to be passed to the
1274 				 * function.  For a \while loop, we'll just tack it on to
1275 				 * the single parameter.
1276 				 */
1277 				if (while_buf != NULL)
1278 				{
1279 					/*
1280 					 * If this is the first item on the list, then we
1281 					 * don't need to stick a space between the previous
1282 					 * one.
1283 					 */
1284 					if (varbuf_getlen(while_buf) > 0)
1285 					{
1286 						varbuf_charcat( while_buf, ' ' );
1287 					}
1288 					varbuf_strcat( while_buf, sqsh_tok_value(tok) );
1289 				}
1290 				else
1291 				{
1292 					if (args_add(job->job_args, sqsh_tok_value( tok )) == False)
1293 					{
1294 						sqsh_set_error( sqsh_get_error(), "args_add: %s", sqsh_get_errstr() );
1295 						return False;
1296 					}
1297 				}
1298 				break;
1299 
1300 			default :
1301 				sqsh_set_error( SQSH_E_SYNTAX, "Unknown token %d", tok->tok_type );
1302 				return False;
1303 		}  /* switch */
1304 
1305 		/*
1306 		 * Retrieve the next token from the command line.
1307 		 */
1308 		if( sqsh_tok( NULL, &tok, tok_flags ) == False ) {
1309 			sqsh_set_error( sqsh_get_error(), "sqsh_tok: %s", sqsh_get_errstr() );
1310 			return False;
1311 		}
1312 
1313 	} /* while */
1314 
1315 	sqsh_set_error( SQSH_E_NONE, NULL );
1316 	return True;
1317 }
1318 
jobset_get(js,job_id)1319 static job_t* jobset_get( js, job_id )
1320 	jobset_t   *js;
1321 	job_id_t    job_id;
1322 {
1323 	job_t      *j;
1324 	int         hval;
1325 
1326 	/*-- Always check your parameters --*/
1327 	if( js == NULL ) {
1328 		sqsh_set_error( SQSH_E_BADPARAM, NULL );
1329 		return NULL;
1330 	}
1331 
1332 	/*-- Calculate which bucket job_id is in --*/
1333 	hval = job_id % js->js_hsize;
1334 
1335 	/*-- Search for the job --*/
1336 	for( j = js->js_jobs[hval];
1337 		j != NULL && j->job_id != job_id; j = j->job_nxt );
1338 
1339 	/*-- ESTUPID --*/
1340 	if( j == NULL ) {
1341 		sqsh_set_error( SQSH_E_EXIST, NULL );
1342 		return NULL;
1343 	}
1344 
1345 	return j;
1346 }
1347 
1348 
1349 /*************************************************************/
1350 
1351 /*
1352  * job_alloc():
1353  *
1354  * Internal function to allocate and initialize a new job within
1355  * a job set.  Returns a valid pointer on success and NULL upon
1356  * a memory allocation failure.
1357  */
job_alloc(js)1358 static job_t* job_alloc( js )
1359 	jobset_t  *js;
1360 {
1361 	job_id_t  max_jobid = 0;
1362 	job_t     *new_job, *job;
1363 	int       i;
1364 
1365 	/*-- Create the command structure --*/
1366 	if( (new_job = (job_t*)malloc(sizeof( job_t ))) == NULL )
1367 		return NULL;
1368 
1369 	/*-- Rather inneficiently calculate the next available job_id --*/
1370 	for( i = 0; i < js->js_hsize; i++ ) {
1371 		for( job = js->js_jobs[i]; job != NULL; job = job->job_nxt )
1372 			max_jobid = max(max_jobid, job->job_id);
1373 	}
1374 
1375 	/*-- Create space to hold command-line arguments --*/
1376 	if( (new_job->job_args = args_create( 16 )) == NULL ) {
1377 		free( new_job );
1378 		return NULL;
1379 	}
1380 
1381 	/*-- Initialize all fields --*/
1382 	new_job->job_id     = max_jobid + 1;
1383 	new_job->job_flags  = 0;
1384 	new_job->job_start  = time(NULL);
1385 	new_job->job_end    = 0;
1386 	new_job->job_status = 0;
1387 	new_job->job_pid    = 0;
1388 	new_job->job_output = NULL;
1389 	new_job->job_nxt    = NULL;
1390 
1391 	return new_job;
1392 }
1393 
1394 /*
1395  * job_free():
1396  *
1397  * If command given by job_id exists within the jobset, js, then
1398  * it is removed and all space allocated to it is reclaimed.  True
1399  * is returned upon success, False is returned if job_id does not
1400  * exist within js.
1401  */
job_free(j)1402 static int job_free( j )
1403 	job_t     *j;
1404 {
1405 	if( j == NULL )
1406 		return False;
1407 
1408 	/*
1409 	 * I don't know if this should go here, but.. if the job may
1410 	 * have deferred output and we have the name of the defer
1411 	 * file, then attempt to unlink the file.  Ignore the
1412 	 * results of unlink.
1413 	 */
1414 	if( (j->job_flags & JOB_DEFER) && j->job_output != NULL )
1415 		unlink( j->job_output );
1416 
1417 	if( j->job_args != NULL )
1418 		args_destroy( j->job_args );
1419 	if( j->job_output != NULL )
1420 		free( j->job_output );
1421 	free( j );
1422 
1423 	return True;
1424 }
1425 
1426 /*
1427  * jobset_global_init():
1428  *
1429  * Initialize any global variables that may be required by the various
1430  * jobset routines..
1431  */
jobset_global_init()1432 static int jobset_global_init()
1433 {
1434 	static int init_done = False;
1435 
1436 	if( init_done == False ) {
1437 		/*
1438 		 * We need a buffer in which to place the expanded command line
1439 		 * perior to parsing.
1440 		 */
1441 		if( sg_cmd_buf == NULL ) {
1442 			if( (sg_cmd_buf = varbuf_create( 128 )) == NULL ) {
1443 				sqsh_set_error( sqsh_get_error(), "varbuf: %s", sqsh_get_errstr() );
1444 				return False;
1445 			}
1446 		}
1447 
1448 		if( sg_alias_buf == NULL ) {
1449 			if( (sg_alias_buf = varbuf_create( 128 )) == NULL ) {
1450 				sqsh_set_error( sqsh_get_error(), "varbuf: %s", sqsh_get_errstr() );
1451 				return False;
1452 			}
1453 		}
1454 
1455 		if (sg_while_buf == NULL)
1456 		{
1457 			if ((sg_while_buf = varbuf_create( 128 )) == NULL)
1458 			{
1459 				sqsh_set_error( sqsh_get_error(), "varbuf: %s", sqsh_get_errstr() );
1460 				return False;
1461 			}
1462 		}
1463 
1464 		init_done = True;
1465 	}
1466 	return True;
1467 }
1468