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

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

kash: Hammering on threaded mode. Got shexit half working. Incomplete sh_destroy.

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