VirtualBox

source: kBuild/trunk/src/kash/shinstance.c@ 3451

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

kash: forked-mode build fixes.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette