VirtualBox

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

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

kash: Introduced asynchronous closing of files we're written to on windows because it may take a while on NTFS. We only need to make sure async CloseHandle are done when executing or forking.

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

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