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

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

kash: Eliminate the 'temp' node field in nfile so we can share node trees later.

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