restart.c revision 7e748ac2
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
33
34/*
35 * Until XSMP provides a better way to know which clients are "managers",
36 * we have to hard code the list.
37 */
38
39Bool
40CheckIsManager(char *program)
41{
42    return (strcmp (program, "twm") == 0);
43}
44
45
46
47/*
48 * GetRestartInfo() will determine which method should be used to
49 * restart a client.
50 *
51 * 'restart_service_prop' is a property set by the client, or NULL.
52 * The format is "remote_start_protocol/remote_start_data".  An
53 * example is "rstart-rsh/hostname".  This is a non-standard property,
54 * which is the whole reason we need this function in order to determine
55 * the restart method.  The proxy uses this property to over-ride the
56 * 'client_host_name' from the ICE connection (the proxy is connected to
57 * the SM via a local connection, but the proxy may be acting as a proxy
58 * for a remote client).
59 *
60 * 'client_host_name' is the connection info obtained from the ICE
61 * connection.  It's format is "transport/host_info".  An example
62 * is "tcp/machine:port".
63 *
64 * If 'restart_service_prop' is NULL, we use 'client_host_name' to
65 * determine the restart method.  If the transport is "local", we
66 * do a local restart.  Otherwise, we use the default "rstart-rsh" method.
67 *
68 * If 'restart_service_prop' is non-NULL, we check the remote_start_protocol
69 * field.  "local" means a local restart.  Currently, the only remote
70 * protocol we recognize is "rstart-rsh".  If the remote protocol is
71 * "rstart-rsh" but the hostname in the 'restart_service_prop' matches
72 * 'client_host_name', we do a local restart.
73 *
74 * On return, set the run_local flag, restart_protocol and restart_machine.
75 */
76
77void
78GetRestartInfo(char *restart_service_prop, char *client_host_name,
79    Bool *run_local, char **restart_protocol, char **restart_machine)
80{
81    char hostnamebuf[80];
82    char *temp;
83
84    *run_local = False;
85    *restart_protocol = NULL;
86    *restart_machine = NULL;
87
88    if (restart_service_prop)
89    {
90	gethostname (hostnamebuf, sizeof hostnamebuf);
91
92	if ((temp = (char *) strchr (
93	    restart_service_prop, '/')) == NULL)
94	{
95	    *restart_protocol = (char *) XtNewString ("rstart-rsh");
96	    *restart_machine = (char *) XtNewString (restart_service_prop);
97	}
98	else
99	{
100	    *restart_protocol = (char *) XtNewString (restart_service_prop);
101	    (*restart_protocol)[temp - restart_service_prop] = '\0';
102	    *restart_machine = (char *) XtNewString (temp + 1);
103	}
104
105	if (strcmp (*restart_machine, hostnamebuf) == 0 ||
106	    strcmp (*restart_protocol, "local") == 0)
107	{
108	    *run_local = True;
109	}
110    }
111    else
112    {
113	if (strncmp (client_host_name, "tcp/", 4) != 0 &&
114	    strncmp (client_host_name, "decnet/", 7) != 0)
115	{
116	    *run_local = True;
117	}
118	else
119	{
120	    *restart_protocol = (char *) XtNewString ("rstart-rsh");
121
122	    if ((temp = (char *) strchr (
123		client_host_name, '/')) == NULL)
124	    {
125		*restart_machine = (char *) XtNewString (client_host_name);
126	    }
127	    else
128	    {
129		*restart_machine = (char *) XtNewString (temp + 1);
130	    }
131	}
132    }
133}
134
135
136
137/*
138 * Restart clients.  The flag indicates RESTART_MANAGERS or
139 * RESTART_REST_OF_CLIENTS.
140 */
141
142Status
143Restart(int flag)
144{
145    List 	*cl, *pl, *vl;
146    PendingClient *c;
147    Prop 	*prop;
148    const char	*cwd;
149    char	*program;
150    char	**args;
151    char	**env;
152    char	**pp;
153    int		cnt;
154    char	*p;
155    char	*restart_service_prop;
156    char	*restart_protocol;
157    char	*restart_machine;
158    Bool	run_local;
159    Bool	is_manager;
160    Bool	ran_manager = 0;
161
162    for(cl = ListFirst(PendingList); cl; cl = ListNext(cl)) {
163	c = (PendingClient *)cl->thing;
164
165	if (verbose) {
166	    printf("Restarting id '%s'...\n", c->clientId);
167	    printf("Host = %s\n", c->clientHostname);
168	}
169	cwd = ".";
170	env = NULL;
171	program=NULL;
172	args=NULL;
173	restart_service_prop=NULL;
174
175	is_manager = 0;
176
177	for(pl = ListFirst(c->props); pl; pl = ListNext(pl)) {
178	    prop = (Prop *)pl->thing;
179	    if(!strcmp(prop->name, SmProgram)) {
180		vl = ListFirst(prop->values);
181		if(vl) program = ((PropValue *)vl->thing)->value;
182		if (CheckIsManager (program))
183		    is_manager = 1;
184	    } else if(!strcmp(prop->name, SmCurrentDirectory)) {
185		vl = ListFirst(prop->values);
186		if(vl) cwd = ((PropValue *)vl->thing)->value;
187	    } else if(!strcmp(prop->name, "_XC_RestartService")) {
188		vl = ListFirst(prop->values);
189		if(vl) restart_service_prop =
190		    ((PropValue *)vl->thing)->value;
191	    } else if(!strcmp(prop->name, SmRestartCommand)) {
192		cnt = ListCount(prop->values);
193		args = (char **)XtMalloc((cnt+1) * sizeof(char *));
194		pp = args;
195		for(vl = ListFirst(prop->values); vl; vl = ListNext(vl)) {
196		    *pp++ = ((PropValue *)vl->thing)->value;
197		}
198		*pp = NULL;
199	    } else if(!strcmp(prop->name, SmEnvironment)) {
200		cnt = ListCount(prop->values);
201		env = (char **)XtMalloc((cnt+3+1) * sizeof(char *));
202		pp = env;
203		for(vl = ListFirst(prop->values); vl; vl = ListNext(vl)) {
204		    p = ((PropValue *)vl->thing)->value;
205		    if((display_env && strbw(p, "DISPLAY="))
206		    || (session_env && strbw(p, "SESSION_MANAGER="))
207		    || (audio_env && strbw(p, "AUDIOSERVER="))
208		        ) continue;
209		    *pp++ = p;
210		}
211		if(display_env) *pp++ = display_env;
212		if(session_env) *pp++ = session_env;
213		if(audio_env) *pp++ = audio_env;
214		*pp = NULL;
215	    }
216	}
217
218	if(program && args) {
219	    char logtext[256];
220
221	    if ((flag == RESTART_MANAGERS && !is_manager) ||
222	        (flag == RESTART_REST_OF_CLIENTS && is_manager))
223	    {
224		if(args) XtFree((char *)args);
225		if(env) XtFree((char *)env);
226		continue;
227	    }
228
229	    if (flag == RESTART_MANAGERS && is_manager)
230		ran_manager = 1;
231
232	    if (verbose) {
233		printf("\t%s\n", program);
234		printf("\t");
235		for(pp = args; *pp; pp++) printf("%s ", *pp);
236		printf("\n");
237	    }
238
239	    GetRestartInfo (restart_service_prop, c->clientHostname,
240    		&run_local, &restart_protocol, &restart_machine);
241
242	    if (run_local)
243	    {
244		/*
245		 * The client is being restarted on the local machine.
246		 */
247
248		snprintf (logtext, sizeof(logtext), "Restarting locally : ");
249		for (pp = args; *pp; pp++)
250		{
251		    strcat (logtext, *pp);
252		    strcat (logtext, " ");
253		}
254
255		strcat (logtext, "\n");
256		add_log_text (logtext);
257
258		switch(fork()) {
259		case -1:
260		    snprintf (logtext, sizeof(logtext),
261			      "%s: Can't fork() %s", Argv[0], program);
262		    add_log_text (logtext);
263		    perror (logtext);
264		    break;
265		case 0:		/* kid */
266		    chdir(cwd);
267		    if(env) environ = env;
268		    execvp(program, args);
269		    snprintf (logtext, sizeof(logtext),
270			      "%s: Can't execvp() %s", Argv[0], program);
271		    perror (logtext);
272		    /*
273		     * TODO : We would like to send this log information to the
274		     * log window in the parent.  This would require opening
275		     * a pipe between the parent and child.  The child would
276		     * set close-on-exec.  If the exec succeeds, the pipe will
277		     * be closed.  If it fails, the child can write a message
278		     * to the parent.
279		     */
280		    _exit(255);
281		default:	/* parent */
282		    break;
283		}
284	    }
285	    else if (!remote_allowed)
286	    {
287		fprintf(stderr,
288		   "Can't remote start client ID '%s': only local supported\n",
289			c->clientId);
290	    }
291	    else
292	    {
293		/*
294		 * The client is being restarted on a remote machine.
295		 */
296
297		snprintf (logtext, sizeof(logtext),
298			  "Restarting remotely on %s : ", restart_machine);
299		for (pp = args; *pp; pp++)
300		{
301		    strcat (logtext, *pp);
302		    strcat (logtext, " ");
303		}
304		strcat (logtext, "\n");
305		add_log_text (logtext);
306
307		remote_start (restart_protocol, restart_machine,
308		    program, args, cwd, env,
309		    non_local_display_env, non_local_session_env);
310	    }
311
312	    if (restart_protocol)
313		XtFree (restart_protocol);
314
315	    if (restart_machine)
316		XtFree (restart_machine);
317
318	} else {
319	    fprintf(stderr, "Can't restart ID '%s':  no program or no args\n",
320		c->clientId);
321	}
322	if(args) XtFree((char *)args);
323	if(env) XtFree((char *)env);
324    }
325
326    if (flag == RESTART_MANAGERS && !ran_manager)
327	return (0);
328    else
329	return (1);
330}
331
332
333
334/*
335 * Clone a client
336 */
337
338void
339Clone(ClientRec *client, Bool useSavedState)
340{
341    const char	*cwd;
342    char	*program;
343    char	**args;
344    char	**env;
345    char	**pp;
346    char	*p;
347    char	*restart_service_prop;
348    char	*restart_protocol;
349    char	*restart_machine;
350    Bool	run_local;
351    List	*pl, *pj;
352
353    if (verbose)
354    {
355	printf ("Cloning id '%s', useSavedState = %d...\n",
356		client->clientId, useSavedState);
357	printf ("Host = %s\n", client->clientHostname);
358    }
359
360    cwd = ".";
361    env = NULL;
362    program = NULL;
363    args = NULL;
364    restart_service_prop = NULL;
365
366    for (pl = ListFirst (client->props); pl; pl = ListNext (pl))
367    {
368	Prop *pprop = (Prop *) pl->thing;
369	List *vl = ListFirst (pprop->values);
370	PropValue *pval = (PropValue *) vl->thing;
371
372	if (strcmp (pprop->name, SmProgram) == 0)
373	    program = (char *) pval->value;
374	else if (strcmp (pprop->name, SmCurrentDirectory) == 0)
375	    cwd = (char *) pval->value;
376	else if (strcmp (pprop->name, "_XC_RestartService") == 0)
377	    restart_service_prop = (char *) pval->value;
378	else if (
379	    (!useSavedState && strcmp (pprop->name, SmCloneCommand) == 0) ||
380	    (useSavedState && strcmp (pprop->name, SmRestartCommand) == 0))
381	{
382	    args = (char **) XtMalloc (
383		(ListCount (pprop->values) + 1) * sizeof (char *));
384
385	    pp = args;
386
387	    for (pj = ListFirst (pprop->values); pj; pj = ListNext (pj))
388	    {
389		pval = (PropValue *) pj->thing;
390		*pp++ = (char *) pval->value;
391	    }
392	    *pp = NULL;
393	}
394	else if (strcmp (pprop->name, SmEnvironment) == 0)
395	{
396	    env = (char **) XtMalloc (
397		(ListCount (pprop->values) + 3 + 1) * sizeof (char *));
398	    pp = env;
399
400	    for (pj = ListFirst (pprop->values); pj; pj = ListNext (pj))
401	    {
402		pval = (PropValue *) pj->thing;
403		p = (char *) pval->value;
404
405		if ((display_env && strbw (p, "DISPLAY="))
406	         || (session_env && strbw (p, "SESSION_MANAGER="))
407		 || (audio_env && strbw (p, "AUDIOSERVER=")))
408		    continue;
409
410		*pp++ = p;
411	    }
412
413	    if (display_env)
414		*pp++ = display_env;
415	    if (session_env)
416		*pp++ = session_env;
417	    if (audio_env)
418		*pp++ = audio_env;
419
420	    *pp = NULL;
421	}
422    }
423
424    if (program && args)
425    {
426	if (verbose)
427	{
428	    printf("\t%s\n", program);
429	    printf("\t");
430	    for (pp = args; *pp; pp++)
431		printf ("%s ", *pp);
432	    printf("\n");
433	}
434
435	GetRestartInfo (restart_service_prop, client->clientHostname,
436    	    &run_local, &restart_protocol, &restart_machine);
437
438	if (run_local)
439	{
440	    /*
441	     * The client is being cloned on the local machine.
442	     */
443
444	    char msg[256];
445
446	    switch(fork()) {
447	    case -1:
448		snprintf (msg, sizeof(msg),
449			  "%s: Can't fork() %s", Argv[0], program);
450		add_log_text (msg);
451		perror (msg);
452		break;
453	    case 0:		/* kid */
454		chdir(cwd);
455		if(env) environ = env;
456		execvp(program, args);
457		snprintf (msg, sizeof(msg),
458			  "%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, filename[128];
522    const char *home;
523    int buflen, len;
524
525    /*
526     * First try ~/.xsmstartup, then system.xsm
527     */
528
529    home = getenv ("HOME");
530    if (!home)
531	home = ".";
532    snprintf (filename, sizeof(filename), "%s/.xsmstartup", home);
533
534    f = fopen (filename, "re");
535
536    if (!f)
537    {
538	f = fopen (SYSTEM_INIT_FILE, "re");
539	if (!f)
540	{
541	    printf ("Could not find default apps file.  Make sure you did\n");
542	    printf ("a 'make install' in the xsm build directory.\n");
543	    exit (1);
544	}
545    }
546
547    buf = NULL;
548    buflen = 0;
549
550    while (getnextline(&buf, &buflen, f))
551    {
552	char logtext[256];
553
554	if (buf[0] == '!')
555	    continue;		/* a comment */
556
557	if ((p = strchr (buf, '\n')))
558	    *p = '\0';
559
560	snprintf (logtext, sizeof(logtext), "Starting locally : %s\n", buf);
561	add_log_text (logtext);
562
563	len = strlen (buf);
564
565	buf[len] = '&';
566	buf[len+1] = '\0';
567
568	/* let the shell parse the stupid args */
569
570	execute_system_command (buf);
571    }
572
573    if (buf)
574	free (buf);
575}
576
577
578
579void
580StartNonSessionAwareApps(void)
581{
582    char logtext[256];
583    int i;
584
585    for (i = 0; i < non_session_aware_count; i++)
586    {
587	/*
588	 * Let the shell parse the stupid args.  We need to add an "&"
589	 * at the end of the command.  We previously allocated an extra
590	 * byte for this.
591	 */
592
593	snprintf (logtext, sizeof(logtext), "Restarting locally : %s\n",
594	    non_session_aware_clients[i]);
595	add_log_text (logtext);
596
597	strcat (non_session_aware_clients[i], "&");
598	execute_system_command (non_session_aware_clients[i]);
599	free ((char *) non_session_aware_clients[i]);
600    }
601
602    if (non_session_aware_clients)
603    {
604	free ((char *) non_session_aware_clients);
605	non_session_aware_clients = NULL;
606    }
607}
608