source: trunk/src/kash/shinstance.c@ 3446

Last change on this file since 3446 was 3446, checked in by bird, 5 years ago

kash: mutex fix.

  • Property svn:eol-style set to LF
  • Property svn:keywords set to Id
File size: 55.2 KB
Line 
1/* $Id: shinstance.c 3446 2020-09-10 20:48:14Z bird $ */
2/** @file
3 * The shell instance methods.
4 */
5
6/*
7 * Copyright (c) 2007-2010 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
8 *
9 *
10 * This file is part of kBuild.
11 *
12 * kBuild is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * kBuild is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with kBuild; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 *
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#include <string.h>
33#include <stdlib.h>
34#include <assert.h>
35#ifdef _MSC_VER
36# include <process.h>
37#else
38# include <unistd.h>
39# include <pwd.h>
40#endif
41#include "shinstance.h"
42
43#include "alias.h"
44#include "error.h"
45#include "memalloc.h"
46#include "redir.h"
47#include "shell.h"
48#include "trap.h"
49
50#if K_OS == K_OS_WINDOWS
51# include <Windows.h>
52# ifdef SH_FORKED_MODE
53extern pid_t shfork_do(shinstance *psh); /* shforkA-win.asm */
54# endif
55#endif
56
57
58/*********************************************************************************************************************************
59* Defined Constants And Macros *
60*********************************************************************************************************************************/
61#ifndef SH_FORKED_MODE
62/** Used by sh__exit/sh_thread_wrapper for passing zero via longjmp. */
63# define SH_EXIT_ZERO 0x0d15ea5e
64#endif
65
66
67/*********************************************************************************************************************************
68* Global Variables *
69*********************************************************************************************************************************/
70#ifndef SH_FORKED_MODE
71/** Mutex serializing exec/spawn to prevent unwanted file inherting. */
72static shmtx g_sh_exec_mtx;
73#endif
74/** The mutex protecting the the globals and some shell instance members (sigs). */
75static shmtx g_sh_mtx;
76/** The root shell instance. */
77static shinstance *g_sh_root;
78/** The first shell instance. */
79static shinstance *g_sh_head;
80/** The last shell instance. */
81static shinstance *g_sh_tail;
82/** The number of shells. */
83static int volatile g_num_shells;
84/** Per signal state for determining a common denominator.
85 * @remarks defaults and unmasked actions aren't counted. */
86struct shsigstate
87{
88 /** The current signal action. */
89#ifndef _MSC_VER
90 struct sigaction sa;
91#else
92 struct
93 {
94 void (*sa_handler)(int);
95 int sa_flags;
96 shsigset_t sa_mask;
97 } sa;
98#endif
99 /** The number of restarts (siginterrupt / SA_RESTART). */
100 int num_restart;
101 /** The number of ignore handlers. */
102 int num_ignore;
103 /** The number of specific handlers. */
104 int num_specific;
105 /** The number of threads masking it. */
106 int num_masked;
107} g_sig_state[NSIG];
108
109
110
111int shmtx_init(shmtx *pmtx)
112{
113#if K_OS == K_OS_WINDOWS
114 typedef int mtxsizecheck[sizeof(CRITICAL_SECTION) + sizeof(KU64) <= sizeof(*pmtx) ? 2 : 0];
115 InitializeCriticalSection((CRITICAL_SECTION *)pmtx);
116#else
117 pmtx->b[0] = 0;
118#endif
119 pmtx->au64[SHMTX_MAGIC_IDX] = SHMTX_MAGIC;
120 return 0;
121}
122
123/**
124 * Safe to call more than once.
125 */
126void shmtx_delete(shmtx *pmtx)
127{
128 if (pmtx->au64[SHMTX_MAGIC_IDX] != SHMTX_MAGIC)
129 {
130#if K_OS == K_OS_WINDOWS
131 DeleteCriticalSection((CRITICAL_SECTION *)pmtx);
132#else
133 pmtx->b[0] = 0;
134#endif
135 pmtx->au64[SHMTX_MAGIC_IDX] = ~SHMTX_MAGIC;
136 }
137}
138
139void shmtx_enter(shmtx *pmtx, shmtxtmp *ptmp)
140{
141#if K_OS == K_OS_WINDOWS
142 EnterCriticalSection((CRITICAL_SECTION *)pmtx);
143 ptmp->i = 0x42;
144#else
145 pmtx->b[0] = 0;
146 ptmp->i = 0;
147#endif
148}
149
150void shmtx_leave(shmtx *pmtx, shmtxtmp *ptmp)
151{
152#if K_OS == K_OS_WINDOWS
153 assert(ptmp->i == 0x42);
154 LeaveCriticalSection((CRITICAL_SECTION *)pmtx);
155 ptmp->i = 0x21;
156#else
157 pmtx->b[0] = 0;
158 ptmp->i = 432;
159#endif
160}
161
162/**
163 * Links the shell instance.
164 *
165 * @param psh The shell.
166 */
167static void sh_int_link(shinstance *psh)
168{
169 shmtxtmp tmp;
170 shmtx_enter(&g_sh_mtx, &tmp);
171
172 if (psh->rootshell)
173 g_sh_root = psh;
174
175 psh->next = NULL;
176 psh->prev = g_sh_tail;
177 if (g_sh_tail)
178 g_sh_tail->next = psh;
179 else
180 g_sh_tail = g_sh_head = psh;
181 g_sh_tail = psh;
182
183 g_num_shells++;
184
185 psh->linked = 1;
186
187 shmtx_leave(&g_sh_mtx, &tmp);
188}
189
190/**
191 * Unlink the shell instance.
192 *
193 * @param psh The shell.
194 */
195static void sh_int_unlink(shinstance *psh)
196{
197 if (psh->linked)
198 {
199 shinstance *pshcur;
200 shmtxtmp tmp;
201 shmtx_enter(&g_sh_mtx, &tmp);
202
203 g_num_shells--;
204
205 if (g_sh_tail == psh)
206 g_sh_tail = psh->prev;
207 else
208 psh->next->prev = psh->prev;
209
210 if (g_sh_head == psh)
211 g_sh_head = psh->next;
212 else
213 psh->prev->next = psh->next;
214
215 if (g_sh_root == psh)
216 g_sh_root = NULL;
217
218 /* Orphan children: */
219 for (pshcur = g_sh_head; pshcur; pshcur = pshcur->next)
220 if (pshcur->parent == psh)
221 pshcur->parent = NULL;
222
223 shmtx_leave(&g_sh_mtx, &tmp);
224 }
225}
226
227/**
228 * Frees a string vector like environ or argv.
229 *
230 * @param psh The shell to associate the deallocations with.
231 * @param vecp Pointer to the vector pointer.
232 */
233static void sh_free_string_vector(shinstance *psh, char ***vecp)
234{
235 char **vec = *vecp;
236 if (vec)
237 {
238 char *str;
239 size_t i = 0;
240 while ((str = vec[i]) != NULL)
241 {
242 sh_free(psh, str);
243 vec[i] = NULL;
244 i++;
245 }
246
247 sh_free(psh, vec);
248 *vecp = NULL;
249 }
250}
251
252
253/**
254 * Destroys the shell instance.
255 *
256 * This will work on partially initialized instances (because I'm lazy).
257 *
258 * @param psh The shell instance to be destroyed.
259 * @note invalidate thread arguments.
260 */
261static void sh_destroy(shinstance *psh)
262{
263 unsigned left, i;
264
265 sh_int_unlink(psh);
266 shfile_uninit(&psh->fdtab);
267 sh_free_string_vector(psh, &psh->shenviron);
268
269 /** @todo children. */
270 sh_free(psh, psh->threadarg);
271 psh->threadarg = NULL;
272
273 /* alias.c */
274 left = psh->aliases;
275 if (left > 0)
276 for (i = 0; i < K_ELEMENTS(psh->atab); i++)
277 {
278 struct alias *cur = psh->atab[i];
279 if (cur)
280 {
281 do
282 {
283 struct alias *next = cur->next;
284 sh_free(psh, cur->val);
285 sh_free(psh, cur->name);
286 sh_free(psh, cur);
287 cur = next;
288 left--;
289 } while (cur);
290 psh->atab[i] = NULL;
291 if (!left)
292 break;
293 }
294 }
295
296 /* cd.c */
297 sh_free(psh, psh->curdir);
298 psh->curdir = NULL;
299 sh_free(psh, psh->prevdir);
300 psh->prevdir = NULL;
301 psh->cdcomppath = NULL; /* stalloc */
302
303 /* eval.h */
304 if (psh->commandnamemalloc)
305 sh_free(psh, psh->commandname);
306 psh->commandname = NULL;
307 psh->cmdenviron = NULL;
308
309#if 0
310 /* expand.c */
311 char *expdest; /**< output of current string */
312 struct nodelist *argbackq; /**< list of back quote expressions */
313 struct ifsregion ifsfirst; /**< first struct in list of ifs regions */
314 struct ifsregion *ifslastp; /**< last struct in list */
315 struct arglist exparg; /**< holds expanded arg list */
316 char *expdir; /**< Used by expandmeta. */
317
318 /* exec.h */
319 const char *pathopt; /**< set by padvance */
320
321 /* exec.c */
322 struct tblentry *cmdtable[CMDTABLESIZE];
323 int builtinloc/* = -1*/; /**< index in path of %builtin, or -1 */
324
325 /* input.h */
326 int plinno/* = 1 */;/**< input line number */
327 int parsenleft; /**< number of characters left in input buffer */
328 char *parsenextc; /**< next character in input buffer */
329 int init_editline/* = 0 */; /**< 0 == not setup, 1 == OK, -1 == failed */
330
331 /* input.c */
332 int parselleft; /**< copy of parsefile->lleft */
333 struct parsefile basepf; /**< top level input file */
334 char basebuf[BUFSIZ];/**< buffer for top level input file */
335 struct parsefile *parsefile/* = &basepf*/; /**< current input file */
336#ifndef SMALL
337 EditLine *el; /**< cookie for editline package */
338#endif
339
340 /* jobs.h */
341 shpid backgndpid/* = -1 */; /**< pid of last background process */
342 int job_warning; /**< user was warned about stopped jobs */
343
344 /* jobs.c */
345 struct job *jobtab; /**< array of jobs */
346 int njobs; /**< size of array */
347 int jobs_invalid; /**< set in child */
348 shpid initialpgrp; /**< pgrp of shell on invocation */
349 int curjob/* = -1*/;/**< current job */
350 int ttyfd/* = -1*/;
351 int jobctl; /**< job control enabled / disabled */
352 char *cmdnextc;
353 int cmdnleft;
354
355
356 /* mail.c */
357#define MAXMBOXES 10
358 int nmboxes; /**< number of mailboxes */
359 time_t mailtime[MAXMBOXES]; /**< times of mailboxes */
360
361 /* main.h */
362 shpid rootpid; /**< pid of main shell. */
363 int rootshell; /**< true if we aren't a child of the main shell. */
364 struct shinstance *psh_rootshell; /**< The root shell pointer. (!rootshell) */
365
366 /* memalloc.h */
367 char *stacknxt/* = stackbase.space*/;
368 int stacknleft/* = MINSIZE*/;
369 int sstrnleft;
370 int herefd/* = -1 */;
371
372 /* memalloc.c */
373 struct stack_block stackbase;
374 struct stack_block *stackp/* = &stackbase*/;
375 struct stackmark *markp;
376
377 /* myhistedit.h */
378 int displayhist;
379#ifndef SMALL
380 History *hist;
381 EditLine *el;
382#endif
383
384 /* output.h */
385 struct output output;
386 struct output errout;
387 struct output memout;
388 struct output *out1;
389 struct output *out2;
390
391 /* output.c */
392#define OUTBUFSIZ BUFSIZ
393#define MEM_OUT -3 /**< output to dynamically allocated memory */
394
395 /* options.h */
396 struct optent optlist[NOPTS];
397 char *minusc; /**< argument to -c option */
398 char *arg0; /**< $0 */
399 struct shparam shellparam; /**< $@ */
400 char **argptr; /**< argument list for builtin commands */
401 char *optionarg; /**< set by nextopt */
402 char *optptr; /**< used by nextopt */
403 char **orgargv; /**< The original argument vector (for cleanup). */
404 int arg0malloc; /**< Indicates whether arg0 was allocated or is part of orgargv. */
405
406 /* parse.h */
407 int tokpushback;
408 int whichprompt; /**< 1 == PS1, 2 == PS2 */
409
410 /* parser.c */
411 int noalias/* = 0*/;/**< when set, don't handle aliases */
412 struct heredoc *heredoclist; /**< list of here documents to read */
413 int parsebackquote; /**< nonzero if we are inside backquotes */
414 int doprompt; /**< if set, prompt the user */
415 int needprompt; /**< true if interactive and at start of line */
416 int lasttoken; /**< last token read */
417 char *wordtext; /**< text of last word returned by readtoken */
418 int checkkwd; /**< 1 == check for kwds, 2 == also eat newlines */
419 struct nodelist *backquotelist;
420 union node *redirnode;
421 struct heredoc *heredoc;
422 int quoteflag; /**< set if (part of) last token was quoted */
423 int startlinno; /**< line # where last token started */
424
425 /* redir.c */
426 struct redirtab *redirlist;
427 int fd0_redirected/* = 0*/;
428
429 /* show.c */
430 char tracebuf[1024];
431 size_t tracepos;
432 int tracefd;
433
434 /* trap.h */
435 int pendingsigs; /**< indicates some signal received */
436
437 /* trap.c */
438 char gotsig[NSIG]; /**< indicates specified signal received */
439 char *trap[NSIG+1]; /**< trap handler commands */
440 char sigmode[NSIG]; /**< current value of signal */
441
442 /* var.h */
443 struct localvar *localvars;
444 struct var vatty;
445 struct var vifs;
446 struct var vmail;
447 struct var vmpath;
448 struct var vpath;
449#ifdef _MSC_VER
450 struct var vpath2;
451#endif
452 struct var vps1;
453 struct var vps2;
454 struct var vps4;
455#ifndef SMALL
456 struct var vterm;
457 struct var vhistsize;
458#endif
459 struct var voptind;
460#ifdef PC_OS2_LIBPATHS
461 struct var libpath_vars[4];
462#endif
463#ifdef SMALL
464# define VTABSIZE 39
465#else
466# define VTABSIZE 517
467#endif
468 struct var *vartab[VTABSIZE];
469
470 /* builtins.h */
471
472 /* bltin/test.c */
473 char **t_wp;
474 struct t_op const *t_wp_op;
475#endif
476
477/** @todo finish this... */
478 memset(psh, 0, sizeof(*psh));
479 sh_free(NULL, psh);
480}
481
482/**
483 * Clones a string vector like environ or argv.
484 *
485 * @returns 0 on success, -1 and errno on failure.
486 * @param psh The shell to associate the allocations with.
487 * @param dstp Where to store the clone.
488 * @param src The vector to be cloned.
489 */
490static int sh_clone_string_vector(shinstance *psh, char ***dstp, char **src)
491{
492 char **dst;
493 size_t items;
494
495 /* count first */
496 items = 0;
497 while (src[items])
498 items++;
499
500 /* alloc clone array. */
501 *dstp = dst = sh_malloc(psh, sizeof(*dst) * (items + 1));
502 if (!dst)
503 return -1;
504
505 /* copy the items */
506 dst[items] = NULL;
507 while (items-- > 0)
508 {
509 dst[items] = sh_strdup(psh, src[items]);
510 if (!dst[items])
511 {
512 /* allocation error, clean up. */
513 while (dst[++items])
514 sh_free(psh, dst[items]);
515 sh_free(psh, dst);
516 errno = ENOMEM;
517 return -1;
518 }
519 }
520
521 return 0;
522}
523
524/**
525 * Creates a shell instance, caller must link it.
526 *
527 * @param inherit The shell to inherit from, or NULL if root.
528 * @param argv The argument vector.
529 * @param envp The environment vector.
530 * @param parentfdtab File table to inherit from, NULL if root.
531 *
532 * @returns pointer to root shell on success, NULL on failure.
533 */
534static shinstance *sh_create_shell_common(char **argv, char **envp, shfdtab *parentfdtab)
535{
536 shinstance *psh;
537
538 /*
539 * The allocations.
540 */
541 psh = sh_calloc(NULL, sizeof(*psh), 1);
542 if (psh)
543 {
544 /* Init it enough for sh_destroy() to not get upset: */
545 /* ... */
546
547 /* Call the basic initializers. */
548 if ( !sh_clone_string_vector(psh, &psh->shenviron, envp)
549 && !sh_clone_string_vector(psh, &psh->orgargv, argv)
550 && !shfile_init(&psh->fdtab, parentfdtab))
551 {
552 unsigned i;
553
554 /*
555 * The special stuff.
556 */
557#ifdef _MSC_VER
558 psh->pgid = psh->pid = _getpid();
559#else
560 psh->pid = getpid();
561 psh->pgid = getpgid();
562#endif
563
564 /*sh_sigemptyset(&psh->sigrestartset);*/
565 for (i = 0; i < K_ELEMENTS(psh->sigactions); i++)
566 psh->sigactions[i].sh_handler = SH_SIG_UNK;
567#if defined(_MSC_VER)
568 sh_sigemptyset(&psh->sigmask);
569#else
570 sigprocmask(SIG_SETMASK, NULL, &psh->sigmask);
571#endif
572
573 /*
574 * State initialization.
575 */
576 /* cd.c */
577 psh->getpwd_first = 1;
578
579 /* exec */
580 psh->builtinloc = -1;
581
582 /* memalloc.c */
583 psh->stacknleft = MINSIZE;
584 psh->herefd = -1;
585 psh->stackp = &psh->stackbase;
586 psh->stacknxt = psh->stackbase.space;
587
588 /* input.c */
589 psh->plinno = 1;
590 psh->init_editline = 0;
591 psh->parsefile = &psh->basepf;
592
593 /* output.c */
594 psh->output.bufsize = OUTBUFSIZ;
595 psh->output.fd = 1;
596 psh->output.psh = psh;
597 psh->errout.bufsize = 100;
598 psh->errout.fd = 2;
599 psh->errout.psh = psh;
600 psh->memout.fd = MEM_OUT;
601 psh->memout.psh = psh;
602 psh->out1 = &psh->output;
603 psh->out2 = &psh->errout;
604
605 /* jobs.c */
606 psh->backgndpid = -1;
607#if JOBS
608 psh->curjob = -1;
609#else
610# error asdf
611#endif
612 psh->ttyfd = -1;
613
614 /* show.c */
615 psh->tracefd = -1;
616 return psh;
617 }
618
619 sh_destroy(psh);
620 }
621 return NULL;
622}
623
624/**
625 * Creates the root shell instance.
626 *
627 * @param argv The argument vector.
628 * @param envp The environment vector.
629 *
630 * @returns pointer to root shell on success, NULL on failure.
631 */
632shinstance *sh_create_root_shell(char **argv, char **envp)
633{
634 shinstance *psh;
635
636 assert(g_sh_mtx.au64[SHMTX_MAGIC_IDX] != SHMTX_MAGIC);
637 shmtx_init(&g_sh_mtx);
638#ifndef SH_FORKED_MODE
639 shmtx_init(&g_sh_exec_mtx);
640#endif
641
642 psh = sh_create_shell_common(argv, envp, NULL /*parentfdtab*/);
643 if (psh)
644 {
645 sh_int_link(psh);
646 return psh;
647 }
648 return NULL;
649}
650
651#ifndef SH_FORKED_MODE
652
653/**
654 * Does the inherting from the parent shell instance.
655 */
656static void sh_inherit_from_parent(shinstance *psh, shinstance *inherit)
657{
658 /*
659 * Do the rest of the inheriting.
660 */
661 psh->parent = inherit;
662 psh->pgid = inherit->pgid;
663
664 psh->sigmask = psh->sigmask;
665 /** @todo sigactions? */
666 /// @todo suppressint?
667
668 /* alises: */
669 subshellinitalias(psh, inherit);
670
671 /* cd.c */
672 psh->getpwd_first = inherit->getpwd_first;
673 if (inherit->curdir)
674 psh->curdir = savestr(psh, inherit->curdir);
675 if (inherit->prevdir)
676 psh->prevdir = savestr(psh, inherit->prevdir);
677
678 /* eval.h */
679 /* psh->commandname - see subshellinitoptions */
680 psh->exitstatus = inherit->exitstatus; /// @todo ??
681 psh->back_exitstatus = inherit->back_exitstatus; /// @todo ??
682 psh->funcnest = inherit->funcnest;
683 psh->evalskip = inherit->evalskip; /// @todo ??
684 psh->skipcount = inherit->skipcount; /// @todo ??
685
686 /* exec.c */
687 subshellinitexec(psh, inherit);
688
689 /* input.h/input.c - only for the parser and anyway forkchild calls closescript(). */
690
691 /* jobs.h - should backgndpid be -1 in subshells? */
692
693 /* jobs.c - */
694 psh->jobctl = inherit->jobctl; /// @todo ??
695 psh->initialpgrp = inherit->initialpgrp;
696 psh->ttyfd = inherit->ttyfd;
697 /** @todo copy jobtab so the 'jobs' command can be run in a subshell.
698 * Better, make it follow the parent chain and skip the copying. Will
699 * require some kind of job locking. */
700
701 /* mail.c - nothing (for now at least) */
702
703 /* main.h */
704 psh->rootpid = inherit->rootpid;
705 psh->psh_rootshell = inherit->psh_rootshell;
706
707 /* memalloc.h / memalloc.c - nothing. */
708
709 /* myhistedit.h */ /** @todo copy history? Do we need to care? */
710
711 /* output.h */ /** @todo not sure this is possible/relevant for subshells */
712 psh->output.fd = inherit->output.fd;
713 psh->errout.fd = inherit->errout.fd;
714 if (inherit->out1 == &inherit->memout)
715 psh->out1 = &psh->memout;
716 if (inherit->out2 == &inherit->memout)
717 psh->out2 = &psh->memout;
718
719 /* options.h */
720 subshellinitoptions(psh, inherit);
721
722 /* parse.h/parse.c */
723 psh->whichprompt = inherit->whichprompt;
724 /* tokpushback, doprompt and needprompt shouldn't really matter, parsecmd resets thems. */
725 /* The rest are internal to the parser, as I see them, and can be ignored. */
726
727 /* redir.c */
728 subshellinitredir(psh, inherit);
729
730 /* show.c */
731 psh->tracefd = inherit->tracefd;
732
733 /* trap.h / trap.c */ /** @todo we don't carry pendingsigs to the subshell, right? */
734 subshellinittrap(psh, inherit);
735
736 /* var.h */
737 subshellinitvar(psh, inherit);
738}
739
740/**
741 * Creates a child shell instance.
742 *
743 * @param inherit The shell to inherit from.
744 *
745 * @returns pointer to root shell on success, NULL on failure.
746 */
747shinstance *sh_create_child_shell(shinstance *inherit)
748{
749 shinstance *psh = sh_create_shell_common(inherit->orgargv, inherit->shenviron, &inherit->fdtab);
750 if (psh)
751 {
752 /* Fake a pid for the child: */
753 static unsigned volatile s_cShells = 0;
754 int const iSubShell = ++s_cShells;
755 psh->pid = SHPID_MAKE(SHPID_GET_PID(inherit->pid), iSubShell);
756
757 sh_inherit_from_parent(psh, inherit);
758
759 /* link it */
760 sh_int_link(psh);
761 return psh;
762 }
763 return NULL;
764}
765
766#endif /* !SH_FORKED_MODE */
767
768/** getenv() */
769char *sh_getenv(shinstance *psh, const char *var)
770{
771 size_t len;
772 int i = 0;
773
774 if (!var)
775 return NULL;
776
777 len = strlen(var);
778 i = 0;
779 while (psh->shenviron[i])
780 {
781 const char *item = psh->shenviron[i];
782 if ( !strncmp(item, var, len)
783 && item[len] == '=')
784 return (char *)item + len + 1;
785 i++;
786 }
787
788 return NULL;
789}
790
791char **sh_environ(shinstance *psh)
792{
793 return psh->shenviron;
794}
795
796const char *sh_gethomedir(shinstance *psh, const char *user)
797{
798 const char *ret = NULL;
799
800#ifdef _MSC_VER
801 ret = sh_getenv(psh, "HOME");
802 if (!ret)
803 ret = sh_getenv(psh, "USERPROFILE");
804#else
805 struct passwd *pwd = getpwnam(user); /** @todo use getpwdnam_r */
806 (void)psh;
807 ret = pwd ? pwd->pw_dir : NULL;
808#endif
809
810 return ret;
811}
812
813/**
814 * Lazy initialization of a signal state, globally.
815 *
816 * @param psh The shell doing the lazy work.
817 * @param signo The signal (valid).
818 */
819static void sh_int_lazy_init_sigaction(shinstance *psh, int signo)
820{
821 if (psh->sigactions[signo].sh_handler == SH_SIG_UNK)
822 {
823 shmtxtmp tmp;
824 shmtx_enter(&g_sh_mtx, &tmp);
825
826 if (psh->sigactions[signo].sh_handler == SH_SIG_UNK)
827 {
828 shsigaction_t shold;
829 shinstance *cur;
830#ifndef _MSC_VER
831 struct sigaction old;
832 if (!sigaction(signo, NULL, &old))
833 {
834 /* convert */
835 shold.sh_flags = old.sa_flags;
836 shold.sh_mask = old.sa_mask;
837 if (old.sa_handler == SIG_DFL)
838 shold.sh_handler = SH_SIG_DFL;
839 else
840 {
841 assert(old.sa_handler == SIG_IGN);
842 shold.sh_handler = SH_SIG_IGN;
843 }
844 }
845 else
846#endif
847 {
848 /* fake */
849#ifndef _MSC_VER
850 assert(0);
851 old.sa_handler = SIG_DFL;
852 old.sa_flags = 0;
853 sigemptyset(&shold.sh_mask);
854 sigaddset(&shold.sh_mask, signo);
855#endif
856 shold.sh_flags = 0;
857 sh_sigemptyset(&shold.sh_mask);
858 sh_sigaddset(&shold.sh_mask, signo);
859 shold.sh_handler = SH_SIG_DFL;
860 }
861
862 /* update globals */
863#ifndef _MSC_VER
864 g_sig_state[signo].sa = old;
865#else
866 g_sig_state[signo].sa.sa_handler = SIG_DFL;
867 g_sig_state[signo].sa.sa_flags = 0;
868 g_sig_state[signo].sa.sa_mask = shold.sh_mask;
869#endif
870 TRACE2((psh, "sh_int_lazy_init_sigaction: signo=%d:%s sa_handler=%p sa_flags=%#x\n",
871 signo, sys_signame[signo], g_sig_state[signo].sa.sa_handler, g_sig_state[signo].sa.sa_flags));
872
873 /* update all shells */
874 for (cur = g_sh_head; cur; cur = cur->next)
875 {
876 assert(cur->sigactions[signo].sh_handler == SH_SIG_UNK);
877 cur->sigactions[signo] = shold;
878 }
879 }
880
881 shmtx_leave(&g_sh_mtx, &tmp);
882 }
883}
884
885/**
886 * Perform the default signal action on the shell.
887 *
888 * @param psh The shell instance.
889 * @param signo The signal.
890 */
891static void sh_sig_do_default(shinstance *psh, int signo)
892{
893 /** @todo */
894}
895
896/**
897 * Deliver a signal to a shell.
898 *
899 * @param psh The shell instance.
900 * @param pshDst The shell instance to signal.
901 * @param signo The signal.
902 * @param locked Whether we're owning the lock or not.
903 */
904static void sh_sig_do_signal(shinstance *psh, shinstance *pshDst, int signo, int locked)
905{
906 shsig_t pfn = pshDst->sigactions[signo].sh_handler;
907 if (pfn == SH_SIG_UNK)
908 {
909 sh_int_lazy_init_sigaction(pshDst, signo);
910 pfn = pshDst->sigactions[signo].sh_handler;
911 }
912
913 if (pfn == SH_SIG_DFL)
914 sh_sig_do_default(pshDst, signo);
915 else if (pfn == SH_SIG_IGN)
916 /* ignore it */;
917 else
918 {
919 assert(pfn != SH_SIG_ERR);
920 pfn(pshDst, signo);
921 }
922 (void)locked;
923}
924
925/**
926 * Handler for external signals.
927 *
928 * @param signo The signal.
929 */
930static void sh_sig_common_handler(int signo)
931{
932 shinstance *psh;
933
934/* fprintf(stderr, "sh_sig_common_handler: signo=%d:%s\n", signo, sys_signame[signo]); */
935
936 /*
937 * No need to take locks if there is only one shell.
938 * Since this will be the initial case, just avoid the deadlock
939 * hell for a litte while...
940 */
941 if (g_num_shells <= 1)
942 {
943 psh = g_sh_head;
944 if (psh)
945 sh_sig_do_signal(NULL, psh, signo, 0 /* no lock */);
946 }
947 else
948 {
949 shmtxtmp tmp;
950 shmtx_enter(&g_sh_mtx, &tmp);
951
952 /** @todo signal focus chain or something? Atm there will only be one shell,
953 * so it's not really important until we go threaded for real... */
954 psh = g_sh_tail;
955 while (psh != NULL)
956 {
957 sh_sig_do_signal(NULL, psh, signo, 1 /* locked */);
958 psh = psh->prev;
959 }
960
961 shmtx_leave(&g_sh_mtx, &tmp);
962 }
963}
964
965int sh_sigaction(shinstance *psh, int signo, const struct shsigaction *newp, struct shsigaction *oldp)
966{
967 if (newp)
968 TRACE2((psh, "sh_sigaction: signo=%d:%s newp=%p:{.sh_handler=%p, .sh_flags=%#x} oldp=%p\n",
969 signo, sys_signame[signo], newp, newp->sh_handler, newp->sh_flags, oldp));
970 else
971 TRACE2((psh, "sh_sigaction: signo=%d:%s newp=NULL oldp=%p\n", signo, sys_signame[signo], oldp));
972
973 /*
974 * Input validation.
975 */
976 if (signo >= NSIG || signo <= 0)
977 {
978 errno = EINVAL;
979 return -1;
980 }
981
982 /*
983 * Make sure our data is correct.
984 */
985 sh_int_lazy_init_sigaction(psh, signo);
986
987 /*
988 * Get the old one if requested.
989 */
990 if (oldp)
991 *oldp = psh->sigactions[signo];
992
993 /*
994 * Set the new one if it has changed.
995 *
996 * This will be attempted coordinated with the other signal handlers so
997 * that we can arrive at a common denominator.
998 */
999 if ( newp
1000 && memcmp(&psh->sigactions[signo], newp, sizeof(*newp)))
1001 {
1002 shmtxtmp tmp;
1003 shmtx_enter(&g_sh_mtx, &tmp);
1004
1005 /* Undo the accounting for the current entry. */
1006 if (psh->sigactions[signo].sh_handler == SH_SIG_IGN)
1007 g_sig_state[signo].num_ignore--;
1008 else if (psh->sigactions[signo].sh_handler != SH_SIG_DFL)
1009 g_sig_state[signo].num_specific--;
1010 if (psh->sigactions[signo].sh_flags & SA_RESTART)
1011 g_sig_state[signo].num_restart--;
1012
1013 /* Set the new entry. */
1014 psh->sigactions[signo] = *newp;
1015
1016 /* Add the bits for the new action entry. */
1017 if (psh->sigactions[signo].sh_handler == SH_SIG_IGN)
1018 g_sig_state[signo].num_ignore++;
1019 else if (psh->sigactions[signo].sh_handler != SH_SIG_DFL)
1020 g_sig_state[signo].num_specific++;
1021 if (psh->sigactions[signo].sh_flags & SA_RESTART)
1022 g_sig_state[signo].num_restart++;
1023
1024 /*
1025 * Calc new common action.
1026 *
1027 * This is quit a bit ASSUMPTIVE about the limited use. We will not
1028 * bother synching the mask, and we pretend to care about SA_RESTART.
1029 * The only thing we really actually care about is the sh_handler.
1030 *
1031 * On second though, it's possible we should just tie this to the root
1032 * shell since it only really applies to external signal ...
1033 */
1034 if ( g_sig_state[signo].num_specific
1035 || g_sig_state[signo].num_ignore != g_num_shells)
1036 g_sig_state[signo].sa.sa_handler = sh_sig_common_handler;
1037 else if (g_sig_state[signo].num_ignore)
1038 g_sig_state[signo].sa.sa_handler = SIG_IGN;
1039 else
1040 g_sig_state[signo].sa.sa_handler = SIG_DFL;
1041 g_sig_state[signo].sa.sa_flags = psh->sigactions[signo].sh_flags & SA_RESTART;
1042
1043 TRACE2((psh, "sh_sigaction: setting signo=%d:%s to {.sa_handler=%p, .sa_flags=%#x}\n",
1044 signo, sys_signame[signo], g_sig_state[signo].sa.sa_handler, g_sig_state[signo].sa.sa_flags));
1045#ifdef _MSC_VER
1046 if (signal(signo, g_sig_state[signo].sa.sa_handler) == SIG_ERR)
1047 {
1048 TRACE2((psh, "sh_sigaction: SIG_ERR, errno=%d signo=%d\n", errno, signo));
1049 if ( signo != SIGHUP /* whatever */
1050 && signo != SIGQUIT
1051 && signo != SIGPIPE
1052 && signo != SIGTTIN
1053 && signo != SIGTSTP
1054 && signo != SIGTTOU
1055 && signo != SIGCONT)
1056 assert(0);
1057 }
1058#else
1059 if (sigaction(signo, &g_sig_state[signo].sa, NULL))
1060 assert(0);
1061#endif
1062
1063 shmtx_leave(&g_sh_mtx, &tmp);
1064 }
1065
1066 return 0;
1067}
1068
1069shsig_t sh_signal(shinstance *psh, int signo, shsig_t handler)
1070{
1071 shsigaction_t sa;
1072 shsig_t ret;
1073
1074 /*
1075 * Implementation using sh_sigaction.
1076 */
1077 if (sh_sigaction(psh, signo, NULL, &sa))
1078 return SH_SIG_ERR;
1079
1080 ret = sa.sh_handler;
1081 sa.sh_flags &= SA_RESTART;
1082 sa.sh_handler = handler;
1083 sh_sigemptyset(&sa.sh_mask);
1084 sh_sigaddset(&sa.sh_mask, signo); /* ?? */
1085 if (sh_sigaction(psh, signo, &sa, NULL))
1086 return SH_SIG_ERR;
1087
1088 return ret;
1089}
1090
1091int sh_siginterrupt(shinstance *psh, int signo, int interrupt)
1092{
1093 shsigaction_t sa;
1094 int oldflags = 0;
1095
1096 /*
1097 * Implementation using sh_sigaction.
1098 */
1099 if (sh_sigaction(psh, signo, NULL, &sa))
1100 return -1;
1101 oldflags = sa.sh_flags;
1102 if (interrupt)
1103 sa.sh_flags &= ~SA_RESTART;
1104 else
1105 sa.sh_flags |= ~SA_RESTART;
1106 if (!((oldflags ^ sa.sh_flags) & SA_RESTART))
1107 return 0; /* unchanged. */
1108
1109 return sh_sigaction(psh, signo, &sa, NULL);
1110}
1111
1112void sh_sigemptyset(shsigset_t *setp)
1113{
1114 memset(setp, 0, sizeof(*setp));
1115}
1116
1117void sh_sigfillset(shsigset_t *setp)
1118{
1119 memset(setp, 0xff, sizeof(*setp));
1120}
1121
1122void sh_sigaddset(shsigset_t *setp, int signo)
1123{
1124#ifdef _MSC_VER
1125 *setp |= 1U << signo;
1126#else
1127 sigaddset(setp, signo);
1128#endif
1129}
1130
1131void sh_sigdelset(shsigset_t *setp, int signo)
1132{
1133#ifdef _MSC_VER
1134 *setp &= ~(1U << signo);
1135#else
1136 sigdelset(setp, signo);
1137#endif
1138}
1139
1140int sh_sigismember(shsigset_t const *setp, int signo)
1141{
1142#ifdef _MSC_VER
1143 return !!(*setp & (1U << signo));
1144#else
1145 return !!sigismember(setp, signo);
1146#endif
1147}
1148
1149int sh_sigprocmask(shinstance *psh, int operation, shsigset_t const *newp, shsigset_t *oldp)
1150{
1151 int rc;
1152
1153 if ( operation != SIG_BLOCK
1154 && operation != SIG_UNBLOCK
1155 && operation != SIG_SETMASK)
1156 {
1157 errno = EINVAL;
1158 return -1;
1159 }
1160
1161#if defined(SH_FORKED_MODE) && !defined(_MSC_VER)
1162 rc = sigprocmask(operation, newp, oldp);
1163 if (!rc && newp)
1164 psh->sigmask = *newp;
1165
1166#else
1167 if (oldp)
1168 *oldp = psh->sigmask;
1169 if (newp)
1170 {
1171 /* calc the new mask */
1172 shsigset_t mask = psh->sigmask;
1173 switch (operation)
1174 {
1175 case SIG_BLOCK:
1176 for (rc = 0; rc < NSIG; rc++)
1177 if (sh_sigismember(newp, rc))
1178 sh_sigaddset(&mask, rc);
1179 break;
1180 case SIG_UNBLOCK:
1181 for (rc = 0; rc < NSIG; rc++)
1182 if (sh_sigismember(newp, rc))
1183 sh_sigdelset(&mask, rc);
1184 break;
1185 case SIG_SETMASK:
1186 mask = *newp;
1187 break;
1188 }
1189
1190# if defined(_MSC_VER)
1191 rc = 0;
1192# else
1193 rc = sigprocmask(operation, &mask, NULL);
1194 if (!rc)
1195# endif
1196 psh->sigmask = mask;
1197 }
1198
1199#endif
1200 return rc;
1201}
1202
1203SH_NORETURN_1 void sh_abort(shinstance *psh)
1204{
1205 shsigset_t set;
1206 TRACE2((psh, "sh_abort\n"));
1207
1208 /* block other async signals */
1209 sh_sigfillset(&set);
1210 sh_sigdelset(&set, SIGABRT);
1211 sh_sigprocmask(psh, SIG_SETMASK, &set, NULL);
1212
1213 sh_sig_do_signal(psh, psh, SIGABRT, 0 /* no lock */);
1214
1215 /** @todo die in a nicer manner. */
1216 *(char *)1 = 3;
1217
1218 TRACE2((psh, "sh_abort returns!\n"));
1219 (void)psh;
1220 abort();
1221}
1222
1223void sh_raise_sigint(shinstance *psh)
1224{
1225 TRACE2((psh, "sh_raise(SIGINT)\n"));
1226
1227 sh_sig_do_signal(psh, psh, SIGINT, 0 /* no lock */);
1228
1229 TRACE2((psh, "sh_raise(SIGINT) returns\n"));
1230}
1231
1232int sh_kill(shinstance *psh, shpid pid, int signo)
1233{
1234 shinstance *pshDst;
1235 shmtxtmp tmp;
1236 int rc;
1237
1238 /*
1239 * Self or any of the subshells?
1240 */
1241 shmtx_enter(&g_sh_mtx, &tmp);
1242
1243 pshDst = g_sh_tail;
1244 while (pshDst != NULL)
1245 {
1246 if (pshDst->pid == pid)
1247 {
1248 TRACE2((psh, "sh_kill(%" SHPID_PRI ", %d): pshDst=%p\n", pid, signo, pshDst));
1249 sh_sig_do_signal(psh, pshDst, signo, 1 /* locked */);
1250
1251 shmtx_leave(&g_sh_mtx, &tmp);
1252 return 0;
1253 }
1254 pshDst = pshDst->prev;
1255 }
1256
1257 shmtx_leave(&g_sh_mtx, &tmp);
1258
1259 /*
1260 * Some other process, call kill where possible
1261 */
1262#ifdef _MSC_VER
1263 errno = ENOSYS;
1264 rc = -1;
1265#elif defined(SH_FORKED_MODE)
1266/* fprintf(stderr, "kill(%d, %d)\n", pid, signo);*/
1267 rc = kill(pid, signo);
1268#else
1269# error "PORT ME?"
1270#endif
1271
1272 TRACE2((psh, "sh_kill(%d, %d) -> %d [%d]\n", pid, signo, rc, errno));
1273 return rc;
1274}
1275
1276int sh_killpg(shinstance *psh, shpid pgid, int signo)
1277{
1278 shinstance *pshDst;
1279 shmtxtmp tmp;
1280 int rc;
1281
1282 /*
1283 * Self or any of the subshells?
1284 */
1285 shmtx_enter(&g_sh_mtx, &tmp);
1286
1287 pshDst = g_sh_tail;
1288 while (pshDst != NULL)
1289 {
1290 if (pshDst->pgid == pgid)
1291 {
1292 TRACE2((psh, "sh_killpg(%" SHPID_PRI ", %d): pshDst=%p\n", pgid, signo, pshDst));
1293 sh_sig_do_signal(psh, pshDst, signo, 1 /* locked */);
1294
1295 shmtx_leave(&g_sh_mtx, &tmp);
1296 return 0;
1297 }
1298 pshDst = pshDst->prev;
1299 }
1300
1301 shmtx_leave(&g_sh_mtx, &tmp);
1302
1303#ifdef _MSC_VER
1304 errno = ENOSYS;
1305 rc = -1;
1306#elif defined(SH_FORKED_MODE)
1307 //fprintf(stderr, "killpg(%d, %d)\n", pgid, signo);
1308 rc = killpg(pgid, signo);
1309#else
1310# error "PORTME?"
1311#endif
1312
1313 TRACE2((psh, "sh_killpg(%" SHPID_PRI ", %d) -> %d [%d]\n", pgid, signo, rc, errno));
1314 (void)psh;
1315 return rc;
1316}
1317
1318clock_t sh_times(shinstance *psh, shtms *tmsp)
1319{
1320#ifdef _MSC_VER
1321 errno = ENOSYS;
1322 return (clock_t)-1;
1323#elif defined(SH_FORKED_MODE)
1324 (void)psh;
1325 return times(tmsp);
1326#else
1327# error "PORTME"
1328#endif
1329}
1330
1331int sh_sysconf_clk_tck(void)
1332{
1333#ifdef _MSC_VER
1334 return CLK_TCK;
1335#else
1336 return sysconf(_SC_CLK_TCK);
1337#endif
1338}
1339
1340/**
1341 * Adds a child to the shell
1342 *
1343 * @returns 0 on success, on failure -1 and errno set to ENOMEM.
1344 *
1345 * @param psh The shell instance.
1346 * @param pid The child pid.
1347 * @param hChild Windows child handle.
1348 * @param fProcess Set if process, clear if thread.
1349 */
1350int sh_add_child(shinstance *psh, shpid pid, void *hChild, KBOOL fProcess)
1351{
1352 /* get a free table entry. */
1353 unsigned i = psh->num_children++;
1354 if (!(i % 32))
1355 {
1356 void *ptr = sh_realloc(psh, psh->children, sizeof(*psh->children) * (i + 32));
1357 if (!ptr)
1358 {
1359 psh->num_children--;
1360 errno = ENOMEM;
1361 return -1;
1362 }
1363 psh->children = ptr;
1364 }
1365
1366 /* add it */
1367 psh->children[i].pid = pid;
1368#if K_OS == K_OS_WINDOWS
1369 psh->children[i].hChild = hChild;
1370#endif
1371#ifndef SH_FORKED_MODE
1372 psh->children[i].fProcess = fProcess;
1373#endif
1374 (void)hChild; (void)fProcess;
1375 return 0;
1376}
1377
1378#ifdef SH_FORKED_MODE
1379
1380pid_t sh_fork(shinstance *psh)
1381{
1382 pid_t pid;
1383 TRACE2((psh, "sh_fork\n"));
1384
1385#if K_OS == K_OS_WINDOWS //&& defined(SH_FORKED_MODE)
1386 pid = shfork_do(psh);
1387
1388#elif defined(SH_FORKED_MODE)
1389# ifdef _MSC_VER
1390 pid = -1;
1391 errno = ENOSYS;
1392# else
1393 pid = fork();
1394# endif
1395
1396#else
1397
1398#endif
1399
1400 /* child: update the pid and zap the children array */
1401 if (!pid)
1402 {
1403# ifdef _MSC_VER
1404 psh->pid = _getpid();
1405# else
1406 psh->pid = getpid();
1407# endif
1408 psh->num_children = 0;
1409 }
1410
1411 TRACE2((psh, "sh_fork -> %d [%d]\n", pid, errno));
1412 (void)psh;
1413 return pid;
1414}
1415
1416#else /* !SH_FORKED_MODE */
1417
1418# ifdef _MSC_VER
1419/** Thread wrapper procedure. */
1420static unsigned __stdcall sh_thread_wrapper(void *user)
1421{
1422 shinstance * volatile volpsh = (shinstance *)user;
1423 shinstance *psh = (shinstance *)user;
1424 struct jmploc exitjmp;
1425 int iExit;
1426
1427 /* Update the TID and PID (racing sh_thread_start) */
1428 DWORD tid = GetCurrentThreadId();
1429 shpid pid = GetCurrentProcessId();
1430
1431 pid = SHPID_MAKE(pid, tid);
1432 psh->pid = pid;
1433 psh->tid = tid;
1434
1435 /* Set the TLS entry before we try TRACE or TRACE2. */
1436 shthread_set_shell(psh);
1437
1438 TRACE2((psh, "sh_thread_wrapper: enter\n"));
1439 if ((iExit = setjmp(exitjmp.loc)) == 0)
1440 {
1441 psh->exitjmp = &exitjmp;
1442 iExit = psh->thread(psh, psh->threadarg);
1443 TRACE2((psh, "sh_thread_wrapper: thread proc returns %d (%#x)\n", iExit, iExit));
1444 }
1445 else
1446 {
1447 psh = volpsh; /* paranoia */
1448 psh->exitjmp = NULL;
1449 TRACE2((psh, "sh_thread_wrapper: longjmp: iExit=%d (%#x)\n", iExit, iExit));
1450 if (iExit == SH_EXIT_ZERO)
1451 iExit = 0;
1452 }
1453
1454 /* destroy the shell instance and exit the thread. */
1455 TRACE2((psh, "sh_thread_wrapper: quits - iExit=%d\n", iExit));
1456 sh_destroy(psh);
1457 shthread_set_shell(NULL);
1458 _endthreadex(iExit);
1459 return iExit;
1460}
1461# else
1462# error "PORTME"
1463# endif
1464
1465/**
1466 * Starts a sub-shell thread.
1467 */
1468shpid sh_thread_start(shinstance *pshparent, shinstance *pshchild, int (*thread)(shinstance *, void *), void *arg)
1469{
1470# ifdef _MSC_VER
1471 unsigned tid = 0;
1472 uintptr_t hThread;
1473 shpid pid;
1474
1475 pshchild->thread = thread;
1476 pshchild->threadarg = arg;
1477 hThread = _beginthreadex(NULL /*security*/, 0 /*stack_size*/, sh_thread_wrapper, pshchild, 0 /*initflags*/, &tid);
1478 if (hThread == -1)
1479 return -errno;
1480
1481 pid = SHPID_MAKE(SHPID_GET_PID(pshparent->pid), tid);
1482 pshchild->pid = pid;
1483 pshchild->tid = tid;
1484
1485 if (sh_add_child(pshparent, pid, (void *)hThread, K_FALSE) != 0) {
1486 return -ENOMEM;
1487 }
1488 return pid;
1489
1490# else
1491# error "PORTME"
1492# endif
1493}
1494
1495#endif /* !SH_FORKED_MODE */
1496
1497/** waitpid() */
1498shpid sh_waitpid(shinstance *psh, shpid pid, int *statusp, int flags)
1499{
1500 shpid pidret;
1501#if K_OS == K_OS_WINDOWS //&& defined(SH_FORKED_MODE)
1502 DWORD dwRet;
1503 HANDLE hChild = INVALID_HANDLE_VALUE;
1504 unsigned i;
1505
1506 *statusp = 0;
1507 pidret = -1;
1508 if (pid != -1)
1509 {
1510 /*
1511 * A specific child, try look it up in the child process table
1512 * and wait for it.
1513 */
1514 for (i = 0; i < psh->num_children; i++)
1515 if (psh->children[i].pid == pid)
1516 break;
1517 if (i < psh->num_children)
1518 {
1519 dwRet = WaitForSingleObject(psh->children[i].hChild,
1520 flags & WNOHANG ? 0 : INFINITE);
1521 if (dwRet == WAIT_OBJECT_0)
1522 hChild = psh->children[i].hChild;
1523 else if (dwRet == WAIT_TIMEOUT)
1524 {
1525 i = ~0; /* don't try close anything */
1526 pidret = 0;
1527 }
1528 else
1529 errno = ECHILD;
1530 }
1531 else
1532 errno = ECHILD;
1533 }
1534 else if (psh->num_children <= MAXIMUM_WAIT_OBJECTS)
1535 {
1536 HANDLE ahChildren[MAXIMUM_WAIT_OBJECTS];
1537 for (i = 0; i < psh->num_children; i++)
1538 ahChildren[i] = psh->children[i].hChild;
1539 dwRet = WaitForMultipleObjects(psh->num_children, &ahChildren[0],
1540 FALSE,
1541 flags & WNOHANG ? 0 : INFINITE);
1542 i = dwRet - WAIT_OBJECT_0;
1543 if (i < psh->num_children)
1544 {
1545 hChild = psh->children[i].hChild;
1546 }
1547 else if (dwRet == WAIT_TIMEOUT)
1548 {
1549 i = ~0; /* don't try close anything */
1550 pidret = 0;
1551 }
1552 else
1553 {
1554 i = ~0; /* don't try close anything */
1555 errno = EINVAL;
1556 }
1557 }
1558 else
1559 {
1560 fprintf(stderr, "panic! too many children!\n");
1561 i = ~0;
1562 *(char *)1 = '\0'; /** @todo implement this! */
1563 }
1564
1565 /*
1566 * Close the handle, and if we succeeded collect the exit code first.
1567 */
1568 if (i < psh->num_children)
1569 {
1570 if (hChild != INVALID_HANDLE_VALUE)
1571 {
1572 DWORD dwExitCode = 127;
1573#ifndef SH_FORKED_MODE
1574 if (psh->children[i].fProcess ? GetExitCodeProcess(hChild, &dwExitCode) : GetExitCodeThread(hChild, &dwExitCode))
1575#else
1576 if (GetExitCodeProcess(hChild, &dwExitCode))
1577#endif
1578 {
1579 pidret = psh->children[i].pid;
1580 if (dwExitCode && !W_EXITCODE(dwExitCode, 0))
1581 dwExitCode |= 16;
1582 *statusp = W_EXITCODE(dwExitCode, 0);
1583 }
1584 else
1585 errno = EINVAL;
1586 }
1587
1588 /* remove and close */
1589 hChild = psh->children[i].hChild;
1590 psh->num_children--;
1591 if (i < psh->num_children)
1592 psh->children[i] = psh->children[psh->num_children];
1593 i = CloseHandle(hChild); assert(i);
1594 }
1595
1596#elif defined(SH_FORKED_MODE)
1597 *statusp = 0;
1598# ifdef _MSC_VER
1599 pidret = -1;
1600 errno = ENOSYS;
1601# else
1602 pidret = waitpid(pid, statusp, flags);
1603# endif
1604
1605#else
1606#endif
1607
1608 TRACE2((psh, "waitpid(%" SHPID_PRI ", %p, %#x) -> %" SHPID_PRI " [%d] *statusp=%#x (rc=%d)\n", pid, statusp, flags,
1609 pidret, errno, *statusp, WEXITSTATUS(*statusp)));
1610 (void)psh;
1611 return pidret;
1612}
1613
1614SH_NORETURN_1 void sh__exit(shinstance *psh, int rc)
1615{
1616 TRACE2((psh, "sh__exit(%d)\n", rc));
1617
1618#if defined(SH_FORKED_MODE)
1619 _exit(rc);
1620 (void)psh;
1621
1622#else
1623 psh->exitstatus = rc;
1624
1625 /*
1626 * If we're a thread, jump to the sh_thread_wrapper and make a clean exit.
1627 */
1628 if (psh->thread)
1629 {
1630 if (psh->exitjmp)
1631 longjmp(psh->exitjmp->loc, !rc ? SH_EXIT_ZERO : rc);
1632 else
1633 {
1634 static char const s_msg[] = "fatal error in sh__exit: exitjmp is NULL!\n";
1635 shfile_write(&psh->fdtab, 2, s_msg, sizeof(s_msg) - 1);
1636 _exit(rc);
1637 }
1638 }
1639
1640 /*
1641 * The main thread will typically have to stick around till all subshell
1642 * threads have been stopped. We must tear down this shell instance as
1643 * much as possible before doing this, though, as subshells could be
1644 * waiting for pipes and such to be closed before they're willing to exit.
1645 */
1646 if (g_num_shells > 1)
1647 {
1648 TRACE2((psh, "sh__exit: %u shells around, must wait...\n", g_num_shells));
1649 shfile_uninit(&psh->fdtab);
1650 sh_int_unlink(psh);
1651 /** @todo */
1652 }
1653
1654 _exit(rc);
1655#endif
1656}
1657
1658int sh_execve(shinstance *psh, const char *exe, const char * const *argv, const char * const *envp)
1659{
1660 int rc;
1661
1662#ifdef DEBUG
1663 /* log it all */
1664 TRACE2((psh, "sh_execve(%p:{%s}, %p, %p}\n", exe, exe, argv, envp));
1665 for (rc = 0; argv[rc]; rc++)
1666 TRACE2((psh, " argv[%d]=%p:{%s}\n", rc, argv[rc], argv[rc]));
1667#endif
1668
1669 if (!envp)
1670 envp = (const char * const *)sh_environ(psh);
1671
1672#if defined(SH_FORKED_MODE) && K_OS != K_OS_WINDOWS
1673# ifdef _MSC_VER
1674 errno = 0;
1675 {
1676 intptr_t rc2 = _spawnve(_P_WAIT, exe, (char **)argv, (char **)envp);
1677 if (rc2 != -1)
1678 {
1679 TRACE2((psh, "sh_execve: child exited, rc=%d. (errno=%d)\n", rc, errno));
1680 rc = (int)rc2;
1681 if (!rc && rc2)
1682 rc = 16;
1683 exit(rc);
1684 }
1685 }
1686 rc = -1;
1687
1688# else
1689 rc = shfile_exec_unix(&psh->fdtab);
1690 if (!rc)
1691 rc = execve(exe, (char **)argv, (char **)envp);
1692# endif
1693
1694#else
1695# if K_OS == K_OS_WINDOWS
1696 {
1697 /*
1698 * This ain't quite straight forward on Windows...
1699 */
1700#ifndef SH_FORKED_MODE
1701 shmtxtmp tmp;
1702#endif
1703 PROCESS_INFORMATION ProcInfo;
1704 STARTUPINFO StrtInfo;
1705 intptr_t hndls[3];
1706 char *cwd = shfile_getcwd(&psh->fdtab, NULL, 0);
1707 char *cmdline;
1708 size_t cmdline_size;
1709 char *envblock;
1710 size_t env_size;
1711 char *p;
1712 int i;
1713
1714 /* Create the environment block. */
1715 if (!envp)
1716 envp = sh_environ(psh);
1717 env_size = 2;
1718 for (i = 0; envp[i]; i++)
1719 env_size += strlen(envp[i]) + 1;
1720 envblock = p = sh_malloc(psh, env_size);
1721 for (i = 0; envp[i]; i++)
1722 {
1723 size_t len = strlen(envp[i]) + 1;
1724 memcpy(p, envp[i], len);
1725 p += len;
1726 }
1727 *p = '\0';
1728
1729 /* Figure the size of the command line. Double quotes makes this
1730 tedious and we overestimate to simplify. */
1731 cmdline_size = 2;
1732 for (i = 0; argv[i]; i++)
1733 {
1734 const char *arg = argv[i];
1735 cmdline_size += strlen(arg) + 3;
1736 arg = strchr(arg, '"');
1737 if (arg)
1738 {
1739 do
1740 cmdline_size++;
1741 while ((arg = strchr(arg + 1, '"')) != NULL);
1742 arg = argv[i] - 1;
1743 while ((arg = strchr(arg + 1, '\\')) != NULL);
1744 cmdline_size++;
1745 }
1746 }
1747
1748 /* Create the command line. */
1749 cmdline = p = sh_malloc(psh, cmdline_size);
1750 for (i = 0; argv[i]; i++)
1751 {
1752 const char *arg = argv[i];
1753 const char *cur = arg;
1754 size_t len = strlen(arg);
1755 int quoted = 0;
1756 char ch;
1757 while ((ch = *cur++) != '\0')
1758 if (ch <= 0x20 || strchr("&><|%", ch) != NULL)
1759 {
1760 quoted = 1;
1761 break;
1762 }
1763
1764 if (i != 0)
1765 *(p++) = ' ';
1766 if (quoted)
1767 *(p++) = '"';
1768 if (memchr(arg, '"', len) == NULL)
1769 {
1770 memcpy(p, arg, len);
1771 p += len;
1772 }
1773 else
1774 { /* MS CRT style: double quotes must be escaped; backslashes
1775 must be escaped if followed by double quotes. */
1776 while ((ch = *arg++) != '\0')
1777 if (ch != '\\' && ch != '"')
1778 *p++ = ch;
1779 else if (ch == '"')
1780 {
1781 *p++ = '\\';
1782 *p++ = '"';
1783 }
1784 else
1785 {
1786 unsigned slashes = 1;
1787 *p++ = '\\';
1788 while (*arg == '\\')
1789 {
1790 *p++ = '\\';
1791 slashes++;
1792 arg++;
1793 }
1794 if (*arg == '"')
1795 {
1796 while (slashes-- > 0)
1797 *p++ = '\\';
1798 *p++ = '\\';
1799 *p++ = '"';
1800 arg++;
1801 }
1802 }
1803 }
1804 if (quoted)
1805 *(p++) = '"';
1806 }
1807 p[0] = p[1] = '\0';
1808
1809 /* Init the info structure */
1810 memset(&StrtInfo, '\0', sizeof(StrtInfo));
1811 StrtInfo.cb = sizeof(StrtInfo);
1812
1813 /* File handles. */
1814#ifndef SH_FORKED_MODE
1815 shmtx_enter(&g_sh_exec_mtx, &tmp);
1816#endif
1817 StrtInfo.dwFlags |= STARTF_USESTDHANDLES;
1818 StrtInfo.lpReserved2 = shfile_exec_win(&psh->fdtab, 1 /* prepare */, &StrtInfo.cbReserved2, hndls);
1819 StrtInfo.hStdInput = (HANDLE)hndls[0];
1820 StrtInfo.hStdOutput = (HANDLE)hndls[1];
1821 StrtInfo.hStdError = (HANDLE)hndls[2];
1822
1823 /* Get going... */
1824 rc = CreateProcess(exe,
1825 cmdline,
1826 NULL, /* pProcessAttributes */
1827 NULL, /* pThreadAttributes */
1828 TRUE, /* bInheritHandles */
1829 0, /* dwCreationFlags */
1830 envblock,
1831 cwd,
1832 &StrtInfo,
1833 &ProcInfo);
1834
1835 shfile_exec_win(&psh->fdtab, rc ? 0 /* done */ : -1 /* done but failed */, NULL, NULL);
1836#ifndef SH_FORKED_MODE
1837 shmtx_leave(&g_sh_exec_mtx, &tmp);
1838#endif
1839 if (rc)
1840 {
1841 DWORD dwErr;
1842 DWORD dwExitCode;
1843
1844 CloseHandle(ProcInfo.hThread);
1845 dwErr = WaitForSingleObject(ProcInfo.hProcess, INFINITE);
1846 assert(dwErr == WAIT_OBJECT_0);
1847
1848 if (GetExitCodeProcess(ProcInfo.hProcess, &dwExitCode))
1849 {
1850 CloseHandle(ProcInfo.hProcess);
1851 sh__exit(psh, dwExitCode);
1852 }
1853 TRACE2((psh, "sh_execve: GetExitCodeProcess failed: %u\n", GetLastError()));
1854 assert(0);
1855 CloseHandle(ProcInfo.hProcess);
1856 errno = EINVAL;
1857 }
1858 else
1859 {
1860 DWORD dwErr = GetLastError();
1861 switch (dwErr)
1862 {
1863 case ERROR_FILE_NOT_FOUND: errno = ENOENT; break;
1864 case ERROR_PATH_NOT_FOUND: errno = ENOENT; break;
1865 case ERROR_BAD_EXE_FORMAT: errno = ENOEXEC; break;
1866 case ERROR_INVALID_EXE_SIGNATURE: errno = ENOEXEC; break;
1867 default:
1868 errno = EINVAL;
1869 break;
1870 }
1871 TRACE2((psh, "sh_execve: dwErr=%d -> errno=%d\n", dwErr, errno));
1872 }
1873
1874 }
1875 rc = -1;
1876
1877# else
1878 errno = ENOSYS;
1879 rc = -1;
1880# endif
1881#endif
1882
1883 TRACE2((psh, "sh_execve -> %d [%d]\n", rc, errno));
1884 (void)psh;
1885 return (int)rc;
1886}
1887
1888uid_t sh_getuid(shinstance *psh)
1889{
1890#ifdef _MSC_VER
1891 uid_t uid = 0;
1892#else
1893 uid_t uid = getuid();
1894#endif
1895
1896 TRACE2((psh, "sh_getuid() -> %d [%d]\n", uid, errno));
1897 (void)psh;
1898 return uid;
1899}
1900
1901uid_t sh_geteuid(shinstance *psh)
1902{
1903#ifdef _MSC_VER
1904 uid_t euid = 0;
1905#else
1906 uid_t euid = geteuid();
1907#endif
1908
1909 TRACE2((psh, "sh_geteuid() -> %d [%d]\n", euid, errno));
1910 (void)psh;
1911 return euid;
1912}
1913
1914gid_t sh_getgid(shinstance *psh)
1915{
1916#ifdef _MSC_VER
1917 gid_t gid = 0;
1918#else
1919 gid_t gid = getgid();
1920#endif
1921
1922 TRACE2((psh, "sh_getgid() -> %d [%d]\n", gid, errno));
1923 (void)psh;
1924 return gid;
1925}
1926
1927gid_t sh_getegid(shinstance *psh)
1928{
1929#ifdef _MSC_VER
1930 gid_t egid = 0;
1931#else
1932 gid_t egid = getegid();
1933#endif
1934
1935 TRACE2((psh, "sh_getegid() -> %d [%d]\n", egid, errno));
1936 (void)psh;
1937 return egid;
1938}
1939
1940shpid sh_getpid(shinstance *psh)
1941{
1942 return psh->pid;
1943}
1944
1945shpid sh_getpgrp(shinstance *psh)
1946{
1947 shpid pgid = psh->pgid;
1948#ifndef _MSC_VER
1949 assert(pgid == getpgrp());
1950#endif
1951
1952 TRACE2((psh, "sh_getpgrp() -> %" SHPID_PRI " [%d]\n", pgid, errno));
1953 return pgid;
1954}
1955
1956/**
1957 * @param pid Should always be zero, i.e. referring to the current shell
1958 * process.
1959 */
1960shpid sh_getpgid(shinstance *psh, shpid pid)
1961{
1962 shpid pgid;
1963 if (pid == 0 || psh->pid == pid)
1964 {
1965 shpid pgid = psh->pgid;
1966#ifndef _MSC_VER
1967 assert(pgid == getpgrp());
1968#endif
1969 }
1970 else
1971 {
1972 assert(0);
1973 errno = ESRCH;
1974 pgid = -1;
1975 }
1976
1977 TRACE2((psh, "sh_getpgid(%" SHPID_PRI ") -> %" SHPID_PRI " [%d]\n", pid, pgid, errno));
1978 return pgid;
1979}
1980
1981/**
1982 *
1983 * @param pid The pid to modify. This is always 0, except when forkparent
1984 * calls to group a newly created child. Though, we might
1985 * almost safely ignore it in that case as the child will also
1986 * perform the operation.
1987 * @param pgid The process group to assign @a pid to.
1988 */
1989int sh_setpgid(shinstance *psh, shpid pid, shpid pgid)
1990{
1991#if defined(SH_FORKED_MODE) && !defined(_MSC_VER)
1992 int rc = setpgid(pid, pgid);
1993 TRACE2((psh, "sh_setpgid(%" SHPID_PRI ", %" SHPID_PRI ") -> %d [%d]\n", pid, pgid, rc, errno));
1994 (void)psh;
1995#else
1996 int rc = 0;
1997 if (pid == 0 || psh->pid == pid)
1998 {
1999 TRACE2((psh, "sh_setpgid(self,): %" SHPID_PRI " -> %" SHPID_PRI "\n", psh->pgid, pgid));
2000 psh->pgid = pgid;
2001 }
2002 else
2003 {
2004 /** @todo fixme */
2005 rc = -1;
2006 errno = ENOSYS;
2007 }
2008#endif
2009 return rc;
2010}
2011
2012shpid sh_tcgetpgrp(shinstance *psh, int fd)
2013{
2014 shpid pgrp;
2015
2016#ifdef _MSC_VER
2017 pgrp = -1;
2018 errno = ENOSYS;
2019#elif defined(SH_FORKED_MODE)
2020 pgrp = tcgetpgrp(fd);
2021#else
2022# error "PORT ME"
2023#endif
2024
2025 TRACE2((psh, "sh_tcgetpgrp(%d) -> %" SHPID_PRI " [%d]\n", fd, pgrp, errno));
2026 (void)psh;
2027 return pgrp;
2028}
2029
2030int sh_tcsetpgrp(shinstance *psh, int fd, shpid pgrp)
2031{
2032 int rc;
2033 TRACE2((psh, "sh_tcsetpgrp(%d, %" SHPID_PRI ")\n", fd, pgrp));
2034
2035#ifdef _MSC_VER
2036 rc = -1;
2037 errno = ENOSYS;
2038#elif defined(SH_FORKED_MODE)
2039 rc = tcsetpgrp(fd, pgrp);
2040#else
2041# error "PORT ME"
2042#endif
2043
2044 TRACE2((psh, "sh_tcsetpgrp(%d, %" SHPID_PRI ") -> %d [%d]\n", fd, pgrp, rc, errno));
2045 (void)psh;
2046 return rc;
2047}
2048
2049int sh_getrlimit(shinstance *psh, int resid, shrlimit *limp)
2050{
2051#ifdef _MSC_VER
2052 int rc = -1;
2053 errno = ENOSYS;
2054#elif defined(SH_FORKED_MODE)
2055 int rc = getrlimit(resid, limp);
2056#else
2057# error "PORT ME"
2058 /* returned the stored limit */
2059#endif
2060
2061 TRACE2((psh, "sh_getrlimit(%d, %p) -> %d [%d] {%ld,%ld}\n",
2062 resid, limp, rc, errno, (long)limp->rlim_cur, (long)limp->rlim_max));
2063 (void)psh;
2064 return rc;
2065}
2066
2067int sh_setrlimit(shinstance *psh, int resid, const shrlimit *limp)
2068{
2069#ifdef _MSC_VER
2070 int rc = -1;
2071 errno = ENOSYS;
2072#elif defined(SH_FORKED_MODE)
2073 int rc = setrlimit(resid, limp);
2074#else
2075# error "PORT ME"
2076 /* if max(shell) < limp; then setrlimit; fi
2077 if success; then store limit for later retrival and maxing. */
2078
2079#endif
2080
2081 TRACE2((psh, "sh_setrlimit(%d, %p:{%ld,%ld}) -> %d [%d]\n",
2082 resid, limp, (long)limp->rlim_cur, (long)limp->rlim_max, rc, errno));
2083 (void)psh;
2084 return rc;
2085}
2086
2087
2088/* Wrapper for strerror that makes sure it doesn't return NULL and causes the
2089 caller or fprintf routines to crash. */
2090const char *sh_strerror(shinstance *psh, int error)
2091{
2092 char *err = strerror(error);
2093 if (!err)
2094 return "strerror return NULL!";
2095 (void)psh;
2096 return err;
2097}
2098
Note: See TracBrowser for help on using the repository browser.