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

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

kash: Hammering on threaded mode.

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