restart.c revision 8108eb18
1/* $Xorg: restart.c,v 1.5 2001/02/09 02:06:01 xorgcvs Exp $ */
2/******************************************************************************
3
4Copyright 1993, 1998  The Open Group
5
6Permission to use, copy, modify, distribute, and sell this software and its
7documentation for any purpose is hereby granted without fee, provided that
8the above copyright notice appear in all copies and that both that
9copyright notice and this permission notice appear in supporting
10documentation.
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall not be
23used in advertising or otherwise to promote the sale, use or other dealings
24in this Software without prior written authorization from The Open Group.
25******************************************************************************/
26/* $XFree86: xc/programs/xsm/restart.c,v 1.5 2001/01/17 23:46:30 dawes Exp $ */
27
28#include "xsm.h"
29#include "log.h"
30#include "restart.h"
31#include "saveutil.h"
32
33extern char **environ;
34
35
36/*
37 * Until XSMP provides a better way to know which clients are "managers",
38 * we have to hard code the list.
39 */
40
41Bool
42CheckIsManager(char *program)
43{
44    return (strcmp (program, "twm") == 0);
45}
46
47
48
49/*
50 * GetRestartInfo() will determine which method should be used to
51 * restart a client.
52 *
53 * 'restart_service_prop' is a property set by the client, or NULL.
54 * The format is "remote_start_protocol/remote_start_data".  An
55 * example is "rstart-rsh/hostname".  This is a non-standard property,
56 * which is the whole reason we need this function in order to determine
57 * the restart method.  The proxy uses this property to over-ride the
58 * 'client_host_name' from the ICE connection (the proxy is connected to
59 * the SM via a local connection, but the proxy may be acting as a proxy
60 * for a remote client).
61 *
62 * 'client_host_name' is the connection info obtained from the ICE
63 * connection.  It's format is "transport/host_info".  An example
64 * is "tcp/machine:port".
65 *
66 * If 'restart_service_prop' is NULL, we use 'client_host_name' to
67 * determine the restart method.  If the transport is "local", we
68 * do a local restart.  Otherwise, we use the default "rstart-rsh" method.
69 *
70 * If 'restart_service_prop' is non-NULL, we check the remote_start_protocol
71 * field.  "local" means a local restart.  Currently, the only remote
72 * protocol we recognize is "rstart-rsh".  If the remote protocol is
73 * "rstart-rsh" but the hostname in the 'restart_service_prop' matches
74 * 'client_host_name', we do a local restart.
75 *
76 * On return, set the run_local flag, restart_protocol and restart_machine.
77 */
78
79void
80GetRestartInfo(char *restart_service_prop, char *client_host_name,
81    Bool *run_local, char **restart_protocol, char **restart_machine)
82{
83    char hostnamebuf[80];
84    char *temp;
85
86    *run_local = False;
87    *restart_protocol = NULL;
88    *restart_machine = NULL;
89
90    if (restart_service_prop)
91    {
92	gethostname (hostnamebuf, sizeof hostnamebuf);
93
94	if ((temp = (char *) strchr (
95	    restart_service_prop, '/')) == NULL)
96	{
97	    *restart_protocol = (char *) XtNewString ("rstart-rsh");
98	    *restart_machine = (char *) XtNewString (restart_service_prop);
99	}
100	else
101	{
102	    *restart_protocol = (char *) XtNewString (restart_service_prop);
103	    (*restart_protocol)[temp - restart_service_prop] = '\0';
104	    *restart_machine = (char *) XtNewString (temp + 1);
105	}
106
107	if (strcmp (*restart_machine, hostnamebuf) == 0 ||
108	    strcmp (*restart_protocol, "local") == 0)
109	{
110	    *run_local = True;
111	}
112    }
113    else
114    {
115	if (strncmp (client_host_name, "tcp/", 4) != 0 &&
116	    strncmp (client_host_name, "decnet/", 7) != 0)
117	{
118	    *run_local = True;
119	}
120	else
121	{
122	    *restart_protocol = (char *) XtNewString ("rstart-rsh");
123
124	    if ((temp = (char *) strchr (
125		client_host_name, '/')) == NULL)
126	    {
127		*restart_machine = (char *) XtNewString (client_host_name);
128	    }
129	    else
130	    {
131		*restart_machine = (char *) XtNewString (temp + 1);
132	    }
133	}
134    }
135}
136
137
138
139/*
140 * Restart clients.  The flag indicates RESTART_MANAGERS or
141 * RESTART_REST_OF_CLIENTS.
142 */
143
144Status
145Restart(int flag)
146{
147    List 	*cl, *pl, *vl;
148    PendingClient *c;
149    Prop 	*prop;
150    char	*cwd;
151    char	*program;
152    char	**args;
153    char	**env;
154    char	**pp;
155    int		cnt;
156    char	*p;
157    char	*restart_service_prop;
158    char	*restart_protocol;
159    char	*restart_machine;
160    Bool	run_local;
161    Bool	is_manager;
162    Bool	ran_manager = 0;
163
164    for(cl = ListFirst(PendingList); cl; cl = ListNext(cl)) {
165	c = (PendingClient *)cl->thing;
166
167	if (verbose) {
168	    printf("Restarting id '%s'...\n", c->clientId);
169	    printf("Host = %s\n", c->clientHostname);
170	}
171	cwd = ".";
172	env = NULL;
173	program=NULL;
174	args=NULL;
175	restart_service_prop=NULL;
176
177	is_manager = 0;
178
179	for(pl = ListFirst(c->props); pl; pl = ListNext(pl)) {
180	    prop = (Prop *)pl->thing;
181	    if(!strcmp(prop->name, SmProgram)) {
182		vl = ListFirst(prop->values);
183		if(vl) program = ((PropValue *)vl->thing)->value;
184		if (CheckIsManager (program))
185		    is_manager = 1;
186	    } else if(!strcmp(prop->name, SmCurrentDirectory)) {
187		vl = ListFirst(prop->values);
188		if(vl) cwd = ((PropValue *)vl->thing)->value;
189	    } else if(!strcmp(prop->name, "_XC_RestartService")) {
190		vl = ListFirst(prop->values);
191		if(vl) restart_service_prop =
192		    ((PropValue *)vl->thing)->value;
193	    } else if(!strcmp(prop->name, SmRestartCommand)) {
194		cnt = ListCount(prop->values);
195		args = (char **)XtMalloc((cnt+1) * sizeof(char *));
196		pp = args;
197		for(vl = ListFirst(prop->values); vl; vl = ListNext(vl)) {
198		    *pp++ = ((PropValue *)vl->thing)->value;
199		}
200		*pp = NULL;
201	    } else if(!strcmp(prop->name, SmEnvironment)) {
202		cnt = ListCount(prop->values);
203		env = (char **)XtMalloc((cnt+3+1) * sizeof(char *));
204		pp = env;
205		for(vl = ListFirst(prop->values); vl; vl = ListNext(vl)) {
206		    p = ((PropValue *)vl->thing)->value;
207		    if((display_env && strbw(p, "DISPLAY="))
208		    || (session_env && strbw(p, "SESSION_MANAGER="))
209		    || (audio_env && strbw(p, "AUDIOSERVER="))
210		        ) continue;
211		    *pp++ = p;
212		}
213		if(display_env) *pp++ = display_env;
214		if(session_env) *pp++ = session_env;
215		if(audio_env) *pp++ = audio_env;
216		*pp = NULL;
217	    }
218	}
219
220	if(program && args) {
221	    char logtext[256];
222
223	    if ((flag == RESTART_MANAGERS && !is_manager) ||
224	        (flag == RESTART_REST_OF_CLIENTS && is_manager))
225	    {
226		if(args) XtFree((char *)args);
227		if(env) XtFree((char *)env);
228		continue;
229	    }
230
231	    if (flag == RESTART_MANAGERS && is_manager)
232		ran_manager = 1;
233
234	    if (verbose) {
235		printf("\t%s\n", program);
236		printf("\t");
237		for(pp = args; *pp; pp++) printf("%s ", *pp);
238		printf("\n");
239	    }
240
241	    GetRestartInfo (restart_service_prop, c->clientHostname,
242    		&run_local, &restart_protocol, &restart_machine);
243
244	    if (run_local)
245	    {
246		/*
247		 * The client is being restarted on the local machine.
248		 */
249
250		sprintf (logtext, "Restarting locally : ");
251		for (pp = args; *pp; pp++)
252		{
253		    strcat (logtext, *pp);
254		    strcat (logtext, " ");
255		}
256
257		strcat (logtext, "\n");
258		add_log_text (logtext);
259
260		switch(fork()) {
261		case -1:
262		    sprintf (logtext,
263			"%s: Can't fork() %s", Argv[0], program);
264		    add_log_text (logtext);
265		    perror (logtext);
266		    break;
267		case 0:		/* kid */
268		    chdir(cwd);
269		    if(env) environ = env;
270		    execvp(program, args);
271		    sprintf (logtext, "%s: Can't execvp() %s",
272			Argv[0], program);
273		    perror (logtext);
274		    /*
275		     * TODO : We would like to send this log information to the
276		     * log window in the parent.  This would require opening
277		     * a pipe between the parent and child.  The child would
278		     * set close-on-exec.  If the exec succeeds, the pipe will
279		     * be closed.  If it fails, the child can write a message
280		     * to the parent.
281		     */
282		    _exit(255);
283		default:	/* parent */
284		    break;
285		}
286	    }
287	    else if (!remote_allowed)
288	    {
289		fprintf(stderr,
290		   "Can't remote start client ID '%s': only local supported\n",
291			c->clientId);
292	    }
293	    else
294	    {
295		/*
296		 * The client is being restarted on a remote machine.
297		 */
298
299		sprintf (logtext, "Restarting remotely on %s : ",
300		    restart_machine);
301		for (pp = args; *pp; pp++)
302		{
303		    strcat (logtext, *pp);
304		    strcat (logtext, " ");
305		}
306		strcat (logtext, "\n");
307		add_log_text (logtext);
308
309		remote_start (restart_protocol, restart_machine,
310		    program, args, cwd, env,
311		    non_local_display_env, non_local_session_env);
312	    }
313
314	    if (restart_protocol)
315		XtFree (restart_protocol);
316
317	    if (restart_machine)
318		XtFree (restart_machine);
319
320	} else {
321	    fprintf(stderr, "Can't restart ID '%s':  no program or no args\n",
322		c->clientId);
323	}
324	if(args) XtFree((char *)args);
325	if(env) XtFree((char *)env);
326    }
327
328    if (flag == RESTART_MANAGERS && !ran_manager)
329	return (0);
330    else
331	return (1);
332}
333
334
335
336/*
337 * Clone a client
338 */
339
340void
341Clone(ClientRec *client, Bool useSavedState)
342{
343    char	*cwd;
344    char	*program;
345    char	**args;
346    char	**env;
347    char	**pp;
348    char	*p;
349    char	*restart_service_prop;
350    char	*restart_protocol;
351    char	*restart_machine;
352    Bool	run_local;
353    List	*pl, *pj;
354
355    if (verbose)
356    {
357	printf ("Cloning id '%s', useSavedState = %d...\n",
358		client->clientId, useSavedState);
359	printf ("Host = %s\n", client->clientHostname);
360    }
361
362    cwd = ".";
363    env = NULL;
364    program = NULL;
365    args = NULL;
366    restart_service_prop = NULL;
367
368    for (pl = ListFirst (client->props); pl; pl = ListNext (pl))
369    {
370	Prop *pprop = (Prop *) pl->thing;
371	List *vl = ListFirst (pprop->values);
372	PropValue *pval = (PropValue *) vl->thing;
373
374	if (strcmp (pprop->name, SmProgram) == 0)
375	    program = (char *) pval->value;
376	else if (strcmp (pprop->name, SmCurrentDirectory) == 0)
377	    cwd = (char *) pval->value;
378	else if (strcmp (pprop->name, "_XC_RestartService") == 0)
379	    restart_service_prop = (char *) pval->value;
380	else if (
381	    (!useSavedState && strcmp (pprop->name, SmCloneCommand) == 0) ||
382	    (useSavedState && strcmp (pprop->name, SmRestartCommand) == 0))
383	{
384	    args = (char **) XtMalloc (
385		(ListCount (pprop->values) + 1) * sizeof (char *));
386
387	    pp = args;
388
389	    for (pj = ListFirst (pprop->values); pj; pj = ListNext (pj))
390	    {
391		pval = (PropValue *) pj->thing;
392		*pp++ = (char *) pval->value;
393	    }
394	    *pp = NULL;
395	}
396	else if (strcmp (pprop->name, SmEnvironment) == 0)
397	{
398	    env = (char **) XtMalloc (
399		(ListCount (pprop->values) + 3 + 1) * sizeof (char *));
400	    pp = env;
401
402	    for (pj = ListFirst (pprop->values); pj; pj = ListNext (pj))
403	    {
404		pval = (PropValue *) pj->thing;
405		p = (char *) pval->value;
406
407		if ((display_env && strbw (p, "DISPLAY="))
408	         || (session_env && strbw (p, "SESSION_MANAGER="))
409		 || (audio_env && strbw (p, "AUDIOSERVER=")))
410		    continue;
411
412		*pp++ = p;
413	    }
414
415	    if (display_env)
416		*pp++ = display_env;
417	    if (session_env)
418		*pp++ = session_env;
419	    if (audio_env)
420		*pp++ = audio_env;
421
422	    *pp = NULL;
423	}
424    }
425
426    if (program && args)
427    {
428	if (verbose)
429	{
430	    printf("\t%s\n", program);
431	    printf("\t");
432	    for (pp = args; *pp; pp++)
433		printf ("%s ", *pp);
434	    printf("\n");
435	}
436
437	GetRestartInfo (restart_service_prop, client->clientHostname,
438    	    &run_local, &restart_protocol, &restart_machine);
439
440	if (run_local)
441	{
442	    /*
443	     * The client is being cloned on the local machine.
444	     */
445
446	    char msg[256];
447
448	    switch(fork()) {
449	    case -1:
450		sprintf (msg, "%s: Can't fork() %s", Argv[0], program);
451		add_log_text (msg);
452		perror (msg);
453		break;
454	    case 0:		/* kid */
455		chdir(cwd);
456		if(env) environ = env;
457		execvp(program, args);
458		sprintf (msg, "%s: Can't execvp() %s", Argv[0], program);
459		perror (msg);
460		/*
461		 * TODO : We would like to send this log information to the
462		 * log window in the parent.  This would require opening
463		 * a pipe between the parent and child.  The child would
464		 * set close-on-exec.  If the exec succeeds, the pipe will
465		 * be closed.  If it fails, the child can write a message
466		 * to the parent.
467		 */
468		_exit(255);
469	    default:	/* parent */
470		break;
471	    }
472	}
473	else if (!remote_allowed)
474	{
475	    fprintf(stderr,
476		   "Can't remote clone client ID '%s': only local supported\n",
477			client->clientId);
478	}
479	else
480	{
481	    /*
482	     * The client is being cloned on a remote machine.
483	     */
484
485	    remote_start (restart_protocol, restart_machine,
486		program, args, cwd, env,
487		non_local_display_env, non_local_session_env);
488	}
489
490	if (restart_protocol)
491	    XtFree (restart_protocol);
492
493	if (restart_machine)
494	    XtFree (restart_machine);
495
496    }
497    else
498    {
499#ifdef XKB
500	XkbStdBell(XtDisplay(topLevel),XtWindow(topLevel),0,XkbBI_Failure);
501#else
502	XBell (XtDisplay (topLevel), 0);
503#endif
504
505	fprintf(stderr, "Can't restart ID '%s':  no program or no args\n",
506		client->clientId);
507    }
508
509    if (args)
510	XtFree ((char *)args);
511    if (env)
512	XtFree ((char *)env);
513}
514
515
516
517void
518StartDefaultApps (void)
519{
520    FILE *f;
521    char *buf, *p, *home, filename[128];
522    int buflen, len;
523
524    /*
525     * First try ~/.xsmstartup, then system.xsm
526     */
527
528    home = (char *) getenv ("HOME");
529    if (!home)
530	home = ".";
531    sprintf (filename, "%s/.xsmstartup", home);
532
533    f = fopen (filename, "r");
534
535    if (!f)
536    {
537	f = fopen (SYSTEM_INIT_FILE, "r");
538	if (!f)
539	{
540	    printf ("Could not find default apps file.  Make sure you did\n");
541	    printf ("a 'make install' in the xsm build directory.\n");
542	    exit (1);
543	}
544    }
545
546    buf = NULL;
547    buflen = 0;
548
549    while (getnextline(&buf, &buflen, f))
550    {
551	char logtext[256];
552
553	if (buf[0] == '!')
554	    continue;		/* a comment */
555
556	if ((p = strchr (buf, '\n')))
557	    *p = '\0';
558
559	sprintf (logtext, "Starting locally : %s\n", buf);
560	add_log_text (logtext);
561
562	len = strlen (buf);
563
564	buf[len] = '&';
565	buf[len+1] = '\0';
566
567	/* let the shell parse the stupid args */
568
569	execute_system_command (buf);
570    }
571
572    if (buf)
573	free (buf);
574}
575
576
577
578void
579StartNonSessionAwareApps(void)
580{
581    char logtext[256];
582    int i;
583
584    for (i = 0; i < non_session_aware_count; i++)
585    {
586	/*
587	 * Let the shell parse the stupid args.  We need to add an "&"
588	 * at the end of the command.  We previously allocated an extra
589	 * byte for this.
590	 */
591
592	sprintf (logtext, "Restarting locally : %s\n",
593	    non_session_aware_clients[i]);
594	add_log_text (logtext);
595
596	strcat (non_session_aware_clients[i], "&");
597	execute_system_command (non_session_aware_clients[i]);
598	free ((char *) non_session_aware_clients[i]);
599    }
600
601    if (non_session_aware_clients)
602    {
603	free ((char *) non_session_aware_clients);
604	non_session_aware_clients = NULL;
605    }
606}
607