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

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

kash: Avoid handle inheritance on windows when possible, allowing concurrent CreateProcess calls.

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