A simple replacement for the "big" vsh that was used by PlanetLab 2.0.
[util-vserver.git] / src / vsh.c
1 /*
2  * Marc E. Fiuczynski <mef@cs.princeton.edu>
3  *
4  * Copyright (c) 2004 The Trustees of Princeton University (Trustees).
5  *
6  * vsh is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * vsh is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
14  * License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Poptop; see the file COPYING.  If not, write to the Free
18  * Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19  * 02111-1307, USA.
20  */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <sys/types.h>
26 #include <pwd.h>
27 #include <string.h>
28 #include <errno.h>
29
30 #define DEFAULT_SHELL "/bin/sh"
31
32 /* Exit statuses for programs like 'env' that exec other programs.
33    EXIT_FAILURE might not be 1, so use EXIT_FAIL in such programs.  */
34 enum
35 {
36   EXIT_CANNOT_INVOKE = 126,
37   EXIT_ENOENT = 127
38 };
39
40 extern void slice_enter(char *);
41 extern void runas_slice_user(char *);
42
43 int main(int argc, char **argv)
44 {
45     char *context, *username, *shell;
46     struct passwd   *pwd;
47     uid_t           uid;
48     int index, i;
49
50     if (argv[0][0]=='-') 
51       index = 1;
52     else
53       index = 0;
54
55     if ((uid = getuid()) < 0) {
56       perror("vsh: getuid error ");
57       exit(1);
58     }
59     if ((pwd = getpwuid(uid)) == NULL) {
60       fprintf(stderr,"vsh: getpwnam error failed for %d\n",uid); 
61       exit(1);
62     }
63
64     context = (char*)strdup(pwd->pw_name);
65     if (!context) {
66       perror("vsh: strdup failed");
67       exit(2);
68     }
69
70     /* enter vserver "context" */
71     slice_enter(context);
72
73     /* Now run as username in this context. Note that for PlanetLab's
74        vserver configuration the context name also happens to be the
75        "default" username within the vserver context.
76     */
77     username = context;
78     runas_slice_user(username);
79
80     /* With the uid/gid appropriately set. Let's figure out what the
81      * shell in the vserver's /etc/passwd is for the given username.
82      */
83     if ((pwd = getpwnam(username)) == NULL) {
84         fprintf(stderr,"vsh: getpwnam error failed for %s\n",username); 
85         exit(1);
86     }
87
88     /* Make sure pw->pw_shell is non-NULL.*/
89     if (pwd->pw_shell == NULL || pwd->pw_shell[0] == '\0') {
90       pwd->pw_shell = (char *) DEFAULT_SHELL;
91     }
92
93     shell = (char *)strdup(pwd->pw_shell);
94     if (!shell) {
95       perror("vsh: strdup failed");
96       exit(2);
97     }
98
99     /* Check whether 'su' or 'sshd' invoked us as a login shell or
100        not; did this above when testing argv[0]=='-'.
101     */
102     argv[0] = shell;
103     if (index == 1) {
104       char **args;
105       args = (char**)malloc(sizeof(char*)*(argc+2));
106       if (!args) {
107         perror("vsh: malloc failed");
108       }
109       args[0] = argv[0];
110       args[1] = "-l";
111       for(i=1;i<argc+1;i++) {
112         args[i+1] = argv[i];
113       }
114       argv = args;
115     }
116     (void) execvp(shell,argv);
117     {
118       int exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
119       exit (exit_status);
120     }
121
122     return 0; /* shutup compiler */
123 }