xref: /plan9/sys/src/cmd/cpu.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1 /*
2  * cpu.c - Make a connection to a cpu server
3  *
4  *	   Invoked by listen as 'cpu -R | -N service net netdir'
5  *	    	   by users  as 'cpu [-h system] [-c cmd args ...]'
6  */
7 
8 #include <u.h>
9 #include <libc.h>
10 #include <bio.h>
11 #include <auth.h>
12 
13 void	remoteside(void);
14 void	fatal(int, char*, ...);
15 void	noteproc(char*);
16 void	catcher(void*, char*);
17 void	remotenote(void);
18 void	usage(void);
19 void	writestr(int, char*, char*, int);
20 int	readstr(int, char*, int);
21 char	*rexcall(int*, char*, char*);
22 
23 int 	notechan;
24 char	system[32];
25 int	cflag;
26 int	hflag;
27 int	dbg;
28 char 	notebuf[ERRLEN];
29 
30 char	*srvname = "cpu";
31 char	*notesrv = "cpunote";
32 char	*exportfs = "/bin/exportfs";
33 
34 void
35 usage(void)
36 {
37 	fprint(2, "usage: cpu [-h system] [-c cmd args ...]\n");
38 	exits("usage");
39 }
40 
41 void
42 main(int argc, char **argv)
43 {
44 	char dat[128], buf[128], cmd[128], *p, *err;
45 	int data;
46 
47 	ARGBEGIN{
48 	case 'd':
49 		dbg++;
50 		break;
51 	case 'R':				/* From listen */
52 		remoteside();
53 		break;
54 	case 'N':
55 		remotenote();
56 		break;
57 	case 'h':
58 		hflag++;
59 		p = ARGF();
60 		if(p==0)
61 			usage();
62 		strcpy(system, p);
63 		break;
64 	case 'c':
65 		cflag++;
66 		cmd[0] = '!';
67 		cmd[1] = '\0';
68 		while(p = ARGF()) {
69 			strcat(cmd, " ");
70 			strcat(cmd, p);
71 		}
72 		break;
73 	}ARGEND;
74 
75 	if(argv);
76 	if(argc != 0)
77 		usage();
78 
79 	if(hflag == 0) {
80 		p = getenv("cpu");
81 		if(p == 0)
82 			fatal(0, "set $cpu");
83 		strcpy(system, p);
84 	}
85 
86 	if(err = rexcall(&data, system, srvname))
87 		fatal(1, "%s: %s", err, system);
88 
89 	/* Tell the remote side the command to execute and where our working directory is */
90 	if(cflag)
91 		writestr(data, cmd, "command", 0);
92 	if(getwd(dat, sizeof(dat)) == 0)
93 		writestr(data, "NO", "dir", 0);
94 	else
95 		writestr(data, dat, "dir", 0);
96 
97 	if(readstr(data, buf, sizeof(buf)) < 0)
98 		fatal(1, "bad pid");
99 	noteproc(buf);
100 
101 	/* Wait for the other end to execute and start our file service
102 	 * of /mnt/term */
103 	if(readstr(data, buf, sizeof(buf)) < 0)
104 		fatal(1, "waiting for FS");
105 	if(strncmp("FS", buf, 2) != 0) {
106 		print("remote cpu: %s", buf);
107 		exits(buf);
108 	}
109 
110 	/* Begin serving the gnot namespace */
111 	close(0);
112 	dup(data, 0);
113 	close(data);
114 	if(dbg)
115 		execl(exportfs, exportfs, "-d", 0);
116 	else
117 		execl(exportfs, exportfs, 0);
118 	fatal(1, "starting exportfs");
119 }
120 
121 void
122 fatal(int syserr, char *fmt, ...)
123 {
124 	char buf[ERRLEN];
125 
126 	doprint(buf, buf+sizeof(buf), fmt, (&fmt+1));
127 	if(syserr)
128 		fprint(2, "cpu: %s: %r\n", buf);
129 	else
130 		fprint(2, "cpu: %s\n", buf);
131 	exits(buf);
132 }
133 
134 
135 /* Invoked with stdin, stdout and stderr connected to the network connection */
136 void
137 remoteside(void)
138 {
139 	char user[NAMELEN], home[128], buf[128], xdir[128], cmd[128];
140 	int i, n, fd, badchdir, gotcmd;
141 
142 	if(srvauth(0, user) < 0)
143 		fatal(1, "srvauth");
144 	if(newns(user, 0) < 0)
145 		fatal(1, "newns");
146 
147 	/* Set environment values for the user */
148 	putenv("user", user);
149 	sprint(home, "/usr/%s", user);
150 	putenv("home", home);
151 
152 	/* Now collect invoking cpus current directory or possibly a command */
153 	gotcmd = 0;
154 	if(readstr(0, xdir, sizeof(xdir)) < 0)
155 		fatal(1, "dir/cmd");
156 	if(xdir[0] == '!') {
157 		strcpy(cmd, &xdir[1]);
158 		gotcmd = 1;
159 		if(readstr(0, xdir, sizeof(xdir)) < 0)
160 			fatal(1, "dir");
161 	}
162 
163 	/* Establish the new process at the current working directory of the
164 	 * gnot */
165 	badchdir = 0;
166 	if(strcmp(xdir, "NO") == 0)
167 		chdir(home);
168 	else if(chdir(xdir) < 0) {
169 		badchdir = 1;
170 		chdir(home);
171 	}
172 
173 	sprint(buf, "%d", getpid());
174 	writestr(1, buf, "pid", 0);
175 
176 	/* Start the gnot serving its namespace */
177 	writestr(1, "FS", "FS", 0);
178 	writestr(1, "/", "exportfs dir", 0);
179 
180 	n = read(1, buf, sizeof(buf));
181 	if(n != 2 || buf[0] != 'O' || buf[1] != 'K')
182 		exits("remote tree");
183 
184 	fd = dup(1, -1);
185 	if(amount(fd, "/mnt/term", MREPL, "") < 0)
186 		exits("mount failed");
187 	close(fd);
188 
189 	for(i = 0; i < 3; i++)
190 		close(i);
191 
192 	if(open("/mnt/term/dev/cons", OREAD) != 0){
193 		exits("open stdin");
194 	}
195 	if(open("/mnt/term/dev/cons", OWRITE) != 1){
196 		exits("open stdout");
197 	}
198 	dup(1, 2);
199 
200 	if(badchdir)
201 		print("cpu: failed to chdir to '%s'\n", xdir);
202 
203 	if(gotcmd)
204 		execl("/bin/rc", "rc", "-lc", cmd, 0);
205 	else
206 		execl("/bin/rc", "rc", "-li", 0);
207 	fatal(1, "exec shell");
208 }
209 
210 void
211 noteproc(char *pid)
212 {
213 	char cmd[NAMELEN], syserr[ERRLEN], *err;
214 	int notepid, n;
215 	Waitmsg w;
216 
217 	notepid = fork();
218 	if(notepid < 0)
219 		fatal(1, "forking noteproc");
220 	if(notepid == 0)
221 		return;
222 
223 	if(err = rexcall(&notechan, system, notesrv))
224 		fprint(2, "cpu: can't dial for notify: %s: %r\n", err);
225 	else{
226 		sprint(cmd, "%s", pid);
227 		writestr(notechan, cmd, "notepid", 0);
228 	}
229 
230 	notify(catcher);
231 
232 	for(;;) {
233 		n = wait(&w);
234 		if(n < 0) {
235 			writestr(notechan, notebuf, "catcher", 1);
236 			errstr(syserr);
237 			if(strcmp(syserr, "interrupted") != 0){
238 				fprint(2, "cpu: wait: %s\n", syserr);
239 				exits("waiterr");
240 			}
241 		}
242 		if(n == notepid)
243 			break;
244 	}
245 	exits(w.msg);
246 }
247 
248 void
249 catcher(void *a, char *text)
250 {
251 	if(a);
252 	strcpy(notebuf, text);
253 	noted(NCONT);
254 }
255 
256 void
257 remotenote(void)
258 {
259 	int pid, e;
260 	char buf[128];
261 
262 	if(srvauth(0, buf) < 0)
263 		exits("srvauth");
264 	if(readstr(0, buf, sizeof(buf)) < 0)
265 		fatal(1, "read pid");
266 	pid = atoi(buf);
267 	e = 0;
268 	while(e == 0) {
269 		if(readstr(0, buf, sizeof(buf)) < 0) {
270 			strcpy(buf, "hangup");
271 			e = 1;
272 		}
273 		if(postnote(PNGROUP, pid, buf) < 0)
274 			e = 1;
275 	}
276 	exits("remotenote");
277 }
278 
279 char*
280 rexcall(int *fd, char *host, char *service)
281 {
282 	char *na;
283 
284 	na = netmkaddr(host, 0, service);
285 	if((*fd = dial(na, 0, 0, 0)) < 0)
286 		return "can't dial";
287 	if(auth(*fd) < 0)
288 		return "can't authenticate";
289 	return 0;
290 }
291 
292 void
293 writestr(int fd, char *str, char *thing, int ignore)
294 {
295 	int l, n;
296 
297 	l = strlen(str);
298 	n = write(fd, str, l+1);
299 	if(!ignore && n < 0)
300 		fatal(1, "writing network: %s", thing);
301 }
302 
303 int
304 readstr(int fd, char *str, int len)
305 {
306 	int n;
307 
308 	while(len) {
309 		n = read(fd, str, 1);
310 		if(n < 0)
311 			return -1;
312 		if(*str == '\0')
313 			return 0;
314 		str++;
315 		len--;
316 	}
317 	return -1;
318 }
319