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

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

kash: Need to initialize mutexes on windows after forking now that we've finally got real mutexes.

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