dtwm: Remove define MOTIF_ONE_DOT_ONE, backwards compatibility for motif 1.1, CDE...
[oweals/cde.git] / cde / programs / dtspcd / main.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*
24  * File:         main.c $TOG: main.c /main/8 1999/09/30 15:31:41 mgreess $
25  * Language:     C
26  *
27  * (c) Copyright 1988, Hewlett-Packard Company, all rights reserved.
28  *
29  * (c) Copyright 1993, 1994 Hewlett-Packard Company                     *
30  * (c) Copyright 1993, 1994 International Business Machines Corp.       *
31  * (c) Copyright 1993, 1994 Sun Microsystems, Inc.                      *
32  * (c) Copyright 1993, 1994 Novell, Inc.                                *
33  */
34
35 #include <bms/sbport.h>
36
37 #include <stdint.h>
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <time.h>                       /* ctime() */
41 #include <pwd.h>
42 #include <errno.h>
43 #include <signal.h>
44 #include <unistd.h>                     /* initgroups() */
45 #include <string.h>
46 #include <libgen.h>                     /* basename() */
47 #include <sys/param.h>                  /* MAXPATHLEN */
48
49 #include <bms/bms.h>
50 #include <bms/MemoryMgr.h>
51 #include <bms/XeUserMsg.h>
52 #include <bms/usersig.h>
53 #include <bms/spc.h>
54
55 #include <SPC/spcP.h>           
56 #include <SPC/spc-proto.h>
57 #include <XlationSvc.h>
58 #include <LocaleXlate.h>
59
60 #include <Tt/tt_c.h>
61
62 #include "spcd_event.h"
63
64 static char *MOUNT_POINT = "DTMOUNTPOINT";
65
66 /* Forward declarations */
67 /** ------------------ **/
68 static void Merge_Lang_Var(SPC_Channel_Ptr channel);
69
70 void SPCD_Handle_Client_Data(void *channel, int *source, SbInputId *id );
71 int SPCD_Initialize(void);
72 void SPCD_Exit(int exitval);
73 void SPCD_Handle_Application_Data(void *chn, XeString text, int size, int conn_type );
74 void SPCD_Termination_Handler(SPC_Channel_Ptr channel, int pid, int type, int cause, void *data );
75 int Client_Abort(protocol_request_ptr prot);
76 int Client_Register(protocol_request_ptr prot);
77 int Client_Unregister(protocol_request_ptr prot);
78 int Client_Channel_Open(protocol_request_ptr prot);
79 int Client_Channel_Close(protocol_request_ptr prot);
80 int Client_Channel_Reset(protocol_request_ptr prot);
81 int Client_Channel_Attach(protocol_request_ptr prot);
82 int Client_Application_Spawn(protocol_request_ptr prot);
83 int Client_Application_Signal(protocol_request_ptr prot);
84 int Client_Application_Data(protocol_request_ptr prot);
85 int Client_Server_Debug(protocol_request_ptr prot);
86 int Client_Environ_Reset(protocol_request_ptr prot);
87 int Client_Reply_Devices(protocol_request_ptr prot);
88 int Client_Reply_Logfile(protocol_request_ptr prot);
89 int Client_Delete_Logfile(protocol_request_ptr prot);
90 int Client_Reset_Termio(protocol_request_ptr prot);
91 int Client_Reset_Termios(protocol_request_ptr prot);
92 int Client_Protocol_Version(protocol_request_ptr prot);
93 int Client_Reply_Protocol(protocol_request_ptr prot);
94 int Client_Reply_Hostinfo(protocol_request_ptr prot);
95
96 /* New B.00 methods */
97
98 int Client_Send_EOF(protocol_request_ptr prot);
99 int Client_Channel_Termios(protocol_request_ptr prot);
100 int Client_Enhanced_Spawn(protocol_request_ptr prot);
101
102 #if defined(_AIX) || defined(__linux__)
103 # define SA_HANDLER_INT_ARG
104 #endif /* _AIX */
105
106 void conditional_putenv(XeString env_str);
107 void SPCD_Reply(SPC_Connection_Ptr connection, protocol_request_ptr prot, int retval, int errval );
108 #ifdef SA_HANDLER_INT_ARG
109 void SPCD_Alarm_Handler (int);
110 #else
111 void SPCD_Alarm_Handler (void);
112 #endif /* SA_HANDLER_INT_ARG */
113
114 /* Global data */
115
116 protocol_request_handler spcd_protocol_dispatch_table[NREQS]={
117   Client_Application_Data,   /* APPLICATION_DATA */
118   NULL,                      /* APPLICATION_STDOUT */
119   NULL,                      /* APPLICATION_STDERR */
120   Client_Abort,              /* ABORT */
121   Client_Register,           /* REGISTER */
122   Client_Unregister,         /* UNREGISTER */
123   Client_Channel_Open,       /* CHANNEL_OPEN */
124   Client_Channel_Close,      /* CHANNEL_CLOSE */
125   Client_Channel_Reset,      /* CHANNEL_RESET */
126   Client_Channel_Attach,     /* CHANNEL_ATTACH */
127   Client_Application_Spawn,  /* APPLICATION_SPAWN */
128   Client_Application_Signal, /* APPLICATION_SIGNAL */
129   NULL,                      /* APPLICATION_DIED */
130   NULL,                      /* SERVER_ERROR */
131   NULL,                      /* REPLY */
132   Client_Server_Debug,       /* SERVER_DEBUG */
133   Client_Environ_Reset,      /* ENVIRON_RESET */
134   Client_Reply_Devices,      /* QUERY_DEVICES */
135   NULL,                      /* DEVICE_REPLY */
136   Client_Reply_Logfile,      /* QUERY_LOGFILE */
137   NULL,                      /* LOGFILE_REPLY */
138   Client_Delete_Logfile,     /* DELETE_LOGFILE */
139   Client_Reset_Termio,       /* RESET_TERMIO (obsolete) */
140   Client_Reset_Termios,      /* RESET_TERMIOS */
141   Client_Send_EOF,           /* CHANNEL_SEND_EOF */
142   Client_Channel_Termios,    /* CHANNEL_TERMIOS */
143   Client_Enhanced_Spawn,     /* APP_B00_SPAWN */
144 };
145
146 /*
147  * HomeDir will be set to "HOME=pwent->pw_dir".
148  *
149  * ShellDir will be set to "SHELL=pwent->pw_shell".
150  */
151 XeChar HomeDir[MAXPATHLEN + 6];
152 XeChar ShellDir[MAXPATHLEN + 7];
153 XeString *default_environment;
154
155 int client_validated=0;
156 int SPCD_Abort_Okay = FALSE;
157
158 SPC_Connection_Ptr client_connection;
159
160 SPC_Channel client_channel;
161
162 /*
163  * This variable contains the number of minutes in the daemon's
164  * exit timer.  If the daemon has no activity within this period 
165  * of time and there are no sub-processes running, the daemon
166  * will exit.
167  */
168 static int exit_timeout = SPCD_DEFAULT_TIMEOUT;
169
170 /*
171  * The following variable is used by the timer code to indicate
172  * if a request is currently being serviced.  This is necessary
173  * because of the following scenario:
174  *
175  *   t0 - a request is made
176  *   t1 - the request is serviced
177  *   t2 - the timer expires
178  *   t3 - the timer handler sees no sub-processes running so it exits
179  *   t4 - request is done, reply to client
180  *
181  * This variable is set to SPCD_REQUEST_PENDING at t0 and to
182  * SPCD_NO_REQUST_PENDING at t4.  If at t3 the variable is set
183  * to SPCD_REQUEST_PENDING, the alarm will be reset and the
184  * daemon will continue.
185  */
186 static int request_pending = SPCD_NO_REQUEST_PENDING;
187
188 /*----------------------------------------------------------------------+*/
189 int main(int argc, XeString *argv)
190 /*----------------------------------------------------------------------+*/
191 {
192   /* Parse the command line and set globals accordingly. */
193   XeString log_path = NULL;
194   Boolean terminate_flag = FALSE;
195   int i;
196   struct sigaction alarm_vector;
197   char tmp[200];
198   char *pch;
199   
200   /*
201    * The SPC library needs to know this is a SPC 'daemon' process
202    * to ensure 'SPC_Initialize()' installs a SIGCLD handler.
203    */
204   SPC_who_am_i = SPC_I_AM_A_DAEMON;
205   SPCD_Authentication_Dir = NULL;
206
207   /* set up log file path */
208   log_path = XeSBTempPath((XeString)"DTSPCD.log");
209
210   if(NULL == freopen("/dev/null", "w", stderr)) {
211     printf("Unable to open /dev/null\n");
212     exit(EXIT_FAILURE);
213   }
214   
215   /* Process arguments and set flags.  */
216   for (i=1; i < argc; i++) {
217
218     if (!strcmp ("-log", argv[i])) {
219       /* Log mode.  Print information to a log file */
220       /* Open an error log with whatever name the library wants to use */
221       SPC_Open_Log(log_path, FALSE);
222     }
223
224     else if (!strcmp ("-debug", argv[i])) {
225       /* Debug mode.  Print protocol information to a log file */
226       /* Open an error log with whatever name the library wants to use */
227       SPC_Open_Log(log_path, FALSE);
228       SPC_Print_Protocol = spc_logF;
229       if(NULL == freopen(log_path, "a", stderr)) {
230         printf("Unable to reopen '%s' as stderr\n", log_path);
231         exit(EXIT_FAILURE);
232       }
233       setbuf(stderr, NULL);
234     }
235
236     else if (!strcmp ("-auth_dir", argv[i])) {
237       /* 
238        * Used to override the default directory for authentication file 
239        */
240       i++;
241       if (i != argc) {
242         SPC_Format_Log((XeString)"Authentication directory set to '%s'.", 
243                        argv[i]);
244         SPCD_Authentication_Dir = strdup (argv[i]);
245       }
246     }
247
248     else if (!strcmp ("-mount_point", argv[i])) {
249       /* 
250        * Mount point for the filename mapping system.
251        */
252       i++;
253       if (i != argc) {
254         (void) snprintf (tmp, sizeof(tmp), "%s=%s", MOUNT_POINT, argv[i]);
255         if (putenv (tmp) == 0) {
256           SPC_Format_Log((XeString)"Mount point set to '%s'.", argv[i]);
257           SPC_mount_point_env_var = (char *) malloc (strlen (tmp) + 1);
258           (void) strcpy (SPC_mount_point_env_var, tmp);
259         }
260         else
261           SPC_Format_Log((XeString)"Failed to add the mount point '%s' to the environment.", tmp);
262       }
263     }
264
265     else if (!strcmp ("-timeout", argv[i])) {
266       /* 
267        * The timeout is specified, so use it instead of the default.
268        */
269       i++;
270       if (i != argc) {
271         exit_timeout = atoi(argv[i]);
272         SPC_Format_Log((XeString)"Setting the exit timer to '%s' minutes.", 
273                        argv[i]);
274       }
275     }
276
277     else {
278       /* 
279        * Unknown command option 
280        */
281       SPC_Format_Log((XeString)"Command line option '%s' unrecognized.", 
282                      argv[i]);
283     }
284   }
285
286   /* free strings allocated for path */
287   XeFree(log_path);
288
289   /*
290    * Initialize the i/o function pointers.
291    */
292   SbAddInput_hookfn        = SPCD_AddInput;
293   SbAddException_hookfn    = SPCD_AddException;
294   SbRemoveInput_hookfn     = SPCD_RemoveInput;
295   SbRemoveException_hookfn = SPCD_RemoveException;
296   SbMainLoopUntil_hookfn   = SPCD_MainLoopUntil;
297   SbBreakMainLoop_hookfn   = SPCD_BreakMainLoop;
298
299   /* Initialization Commands */
300   if (SPCD_Initialize()==SPC_ERROR)
301     SPCD_Exit(1);
302
303   client_connection=SPC_Start_Daemon(FALSE);
304   if(client_connection==SPC_ERROR)
305     SPCD_Exit(1);
306
307   client_channel.connection = client_connection;
308   
309   SPC_XtAddInput(&client_channel,
310                  &client_connection->termination_id,
311                  client_connection->sid,
312                  SPCD_Handle_Client_Data,
313                  SPC_Client);
314   
315   if (exit_timeout != SPCD_NO_TIMER) {
316     memset(&alarm_vector, 0, sizeof(struct sigaction));
317     alarm_vector.sa_handler = SPCD_Alarm_Handler;
318     alarm_vector.sa_flags = 0;
319     (void) sigaction (SIGALRM, &alarm_vector, (struct sigaction *)NULL);
320     (void) alarm (exit_timeout * 60);
321   }
322
323   /*
324    * The daemon's mount point environment variable needs to be 
325    * saved.  It will be used to override the client's mount point
326    * setting or the mount point specified in any of the environment 
327    * files.
328    */
329   if (SPC_mount_point_env_var == NULL)
330     if ((pch = getenv (MOUNT_POINT)) != NULL) {
331       SPC_mount_point_env_var = (char *) malloc (strlen (pch) + 
332                                                  strlen (MOUNT_POINT) + 2);
333       (void) sprintf (SPC_mount_point_env_var, "%s=%s", MOUNT_POINT, pch);
334     }
335
336   XeCall_SbMainLoopUntil(&terminate_flag);
337   SPCD_Exit(0);
338 }
339
340 /*----------------------------------------------------------------------+*/
341 void SPCD_Handle_Client_Data(void      *channel,
342                              int       *UNUSED_PARM(source),
343                              SbInputId *UNUSED_PARM(id))
344 /*----------------------------------------------------------------------+*/
345 {
346   protocol_request_ptr prot;
347   int retval;
348   SPC_Connection_Ptr connection=((SPC_Channel_Ptr)channel)->connection;
349
350   request_pending = SPCD_REQUEST_PENDING;
351
352   prot=SPC_Read_Protocol(connection);
353   if(!prot)
354     SPCD_Exit(0);
355
356   /* Check for valid client (in other words, that we have registered
357      this client). */
358
359   if((!client_validated) && (prot->request_type != REGISTER)) {
360     SPC_Error(SPC_Client_Not_Valid);
361     if (exit_timeout != SPCD_NO_TIMER)
362       (void) alarm (exit_timeout * 60);
363     request_pending = SPCD_REQUEST_PENDING;
364     SPC_Free_Protocol_Ptr(prot);
365     return;
366   }
367
368   SPCD_Abort_Okay = TRUE;
369   retval=SPC_Dispatch_Protocol(prot, spcd_protocol_dispatch_table);
370   SPCD_Abort_Okay = FALSE;
371   if(REPLY_EXPECTED(prot->request_type, TRUE) != NO_REPLY_VAL)
372     SPCD_Reply(connection, prot, retval, errno);
373   SPC_Free_Protocol_Ptr(prot);
374
375   /* 
376    * Reset the alarm and go back to select.
377    */
378   if (exit_timeout != SPCD_NO_TIMER)
379     (void) alarm (exit_timeout * 60);
380
381   request_pending = SPCD_NO_REQUEST_PENDING;
382 }
383
384 /*----------------------------------------------------------------------+*/
385 int SPCD_Initialize(void)
386 /*----------------------------------------------------------------------+*/
387 {
388   XeString sys_env_file = NULL;
389   
390   /* Do initialization for SPC */
391   if(SPC_Initialize()==SPC_ERROR)
392     return(SPC_ERROR);
393
394   /* Do Daemon specific initialization */
395
396   /* Error handling */
397
398   XeProgName=SPCD_PROG_NAME;
399
400   default_environment=
401     (XeString *) malloc(DEFAULT_ENVP_SIZE * sizeof(XeString));
402   default_environment[0]=NULL;
403
404   /*
405    * First add the installed environment file.
406    */
407   sys_env_file = (XeString) malloc (strlen(SPCD_ENV_INSTALL_DIRECTORY) +
408                                     strlen(SPCD_ENV_FILE) + 3);
409   (void) sprintf (sys_env_file, "%s/%s", 
410                   SPCD_ENV_INSTALL_DIRECTORY, 
411                   SPCD_ENV_FILE);
412   default_environment=SPC_Add_Env_File(sys_env_file,default_environment);
413
414   /*
415    * Now add the configured environment file.
416    */
417   sys_env_file = (XeString) realloc (sys_env_file,
418                                      strlen(SPCD_ENV_CONFIG_DIRECTORY) +
419                                      strlen(SPCD_ENV_FILE) + 3);
420   (void) sprintf (sys_env_file, "%s/%s", 
421                   SPCD_ENV_CONFIG_DIRECTORY, 
422                   SPCD_ENV_FILE);
423
424   default_environment=SPC_Add_Env_File(sys_env_file,default_environment);
425
426   free (sys_env_file);
427
428   return(TRUE);
429 }
430
431 /*----------------------------------------------------------------------+*/
432 void SPCD_Exit(int exitval)
433 /*----------------------------------------------------------------------+*/
434 {
435   int i;
436
437   if (SPC_logfile_list != NULL)
438     for (i = 0; SPC_logfile_list[i] != NULL; i++)
439       (void) unlink (SPC_logfile_list[i]);
440
441   SPC_Format_Log((XeString)"Exiting server.  Retval: %d", exitval);
442   SPC_Close_Log();
443
444   exit(exitval);
445 }
446
447 /*----------------------------------------------------------------------+*/
448 void SPCD_Handle_Application_Data(void * chn,
449                                   XeString text,
450                                   int size,
451                                   int conn_type )
452 /*----------------------------------------------------------------------+*/
453 {
454   SPC_Channel_Ptr channel= (SPC_Channel_Ptr) chn;
455   SPC_Write_Protocol_Request(client_connection, channel,
456                              CONNECTOR_TO_PROT(conn_type), text, size);
457 }
458
459
460 /*----------------------------------------------------------------------+*/
461 void SPCD_Termination_Handler(SPC_Channel_Ptr channel,
462                               int UNUSED_PARM(pid),
463                               int UNUSED_PARM(type),
464                               int UNUSED_PARM(cause),
465                               void * UNUSED_PARM(data))
466 /*----------------------------------------------------------------------+*/
467 {
468   /* Write a termination protocol request */
469
470   SPC_Write_Protocol_Request(client_connection, channel,
471                              APPLICATION_DIED, channel->status);
472
473   /* That's all, folks! */
474 }
475
476 /*
477  ***
478  *** Protocol request handlers
479  ***
480 */
481
482 /*----------------------------------------------------------------------+*/
483 int Client_Abort(protocol_request_ptr prot)
484 /*----------------------------------------------------------------------+*/
485 {
486   return(print_protocol_request((XeString)"--> ABORT", prot));
487 }
488
489 #define FREE_USER_PASS(a, b) free(a); free(b);
490
491 /*----------------------------------------------------------------------+*/
492 int Client_Register(protocol_request_ptr prot)
493 /*----------------------------------------------------------------------+*/
494 {
495   XeString username;
496   XeString passwd;
497   XeString proto_ver;
498   XeString hostinfo;
499   struct passwd *pwent;
500   XeString tmpfile, tmppath;
501   XeChar  buffer[MAXPATHLEN * 2];
502   struct stat buf;
503   XeString netfile;
504   int free_netfile = 0;
505   char *spc_prefix = "/.SPC_";
506   char *spc_suffix;
507   char tmpnam_buf[L_tmpnam + 1];
508   size_t buffsize;
509
510   print_protocol_request((XeString)"--> REGISTER", prot);
511   prot->channel=0;
512
513   READ_REGISTER(prot->dataptr, username, passwd, proto_ver, hostinfo);
514   
515   if(strcmp(username, "") != 0) {
516     SPC_Format_Log ( "+++> Starting authentication for user '%s'\n     from host '%s'.", 
517             username, hostinfo);
518     /*  
519      * We have a username so generate a temp filename and send it 
520      * back to the client after creating the proper path for the file.
521      */
522     if(SPCD_Authentication_Dir)
523       /*
524        * Use the directory specified on the command line.
525        */
526       tmppath = strdup(SPCD_Authentication_Dir);
527     else {
528       /*
529        * Use the $HOME directory if it can be retrieved from the
530        * password file.
531        */
532       if(!(pwent=getpwnam(username))) {
533         SPC_Format_Log ("+++> FAILURE: username '%s' is unknown.", username);
534         SPC_Write_Protocol_Request(client_connection, NULL, LOGFILE_REPLY,
535                                    FAILED_FILE_NAME, NULL, NULL);
536         FREE_USER_PASS(username, passwd);
537         SPC_Error(SPC_Bad_Username);
538         free(hostinfo);
539         return(SPC_ERROR);
540       }
541       else {
542         tmppath = (XeString)(pwent->pw_dir);
543            
544         if ((lstat (tmppath, &buf) != 0) || 
545             (!S_ISDIR(buf.st_mode))     || 
546             (!(buf.st_mode & S_IRUSR)))
547           /*
548            * Use the default directory.
549            */
550           tmppath = XeSBTempPath(XeString_NULL);
551       }
552     }
553
554     /*
555      * tempnam(3) has side effects caused by permissions of the directory
556      * given or the TMPDIR envirnoment variable.  Because of these side
557      * effects, the function may return "/tmp/.SPC_xxxxxx" and ignore
558      * tmppath.  The protocol will fail when this occurs.  The fix is
559      * to construct the tmpfile name.
560      */
561     tmpnam(tmpnam_buf);
562     spc_suffix = basename(tmpnam_buf); /* Don't free result - not alloc'd! */
563
564     /* Allocate space for tmppath, spc_prefix, and spc_suffix. */
565     buffsize = strlen(tmppath) + strlen(spc_prefix) + strlen(spc_suffix) + 1;
566     tmpfile = (char *)malloc(buffsize);
567     if(tmpfile) {
568         snprintf(tmpfile, buffsize, "%s%s%s", tmppath, spc_prefix, spc_suffix);
569     }
570   }
571   else {
572 #if 0
573     /*
574      * No username was supplied (this could happen with pre-CDE
575      * clients) so generate a temp filename.
576      */
577     if(SPCD_Authentication_Dir)
578       tmppath = strdup(SPCD_Authentication_Dir);
579     else
580       tmppath = XeSBTempPath(XeString_NULL);
581     tmpfile = tempnam(tmppath, ".SPC_" );
582     XeFree(tmppath);
583 #endif
584
585     SPC_Format_Log ("+++> FAILURE: NULL username.");
586     SPC_Write_Protocol_Request(client_connection, NULL, LOGFILE_REPLY,
587                                FAILED_FILE_NAME, NULL, NULL);
588     FREE_USER_PASS(username, passwd);
589     SPC_Error(SPC_Bad_Username);
590     free(hostinfo);
591     return(SPC_ERROR);
592
593   }
594
595   if(!tmpfile) {
596     SPC_Write_Protocol_Request(client_connection, NULL, LOGFILE_REPLY,
597                                FAILED_FILE_NAME, NULL, NULL);
598     SPC_Format_Log("+++> FAILURE: cannot malloc.");
599     SPC_Error(SPC_Out_Of_Memory);
600     free(hostinfo);
601     return(SPC_ERROR);
602   }
603
604   /* 
605    * Store info about the client (protocol version & host type) 
606    * For Pre A.01, this will be defaulted to (hpux 7.0 s300) 
607    */
608   client_connection->hostinfo = hostinfo;
609
610   if (proto_ver) {
611       (void) sscanf(proto_ver, "%d", &client_connection->protocol_version);
612       SPC_client_version_number = client_connection->protocol_version;
613       XeFree(proto_ver);
614   }
615   else
616       /* The client didn't send a protocol_version so set it to "1". */
617       SPC_client_version_number = 1;
618
619   SPC_Format_Log("     Client protocol version is '%d'.", 
620                  SPC_client_version_number);
621   SPC_Format_Log("+++> Authentication file is '%s'.", tmpfile);
622
623   /*
624    * For non-CDE clients, the clients expect the daemon to send
625    * a "real" file name.  Beginning with 'SPC_PROTOCOL_VERSION_CDE_BASE'
626    * the clients are expecting a "netfile" name.
627    */
628   netfile = tmpfile;
629
630   if (SPC_client_version_number >= SPC_PROTOCOL_VERSION_CDE_BASE) {
631      netfile = tt_file_netfile (tmpfile);
632
633      if (tt_ptr_error (netfile) != TT_OK) {
634        SPC_Write_Protocol_Request(client_connection, NULL, LOGFILE_REPLY,
635                                   FAILED_FILE_NAME, NULL, NULL);
636        SPC_Format_Log("+++> FAILURE: cannot create a cannonical file name for the authentication file.\n     (%s)", 
637                       tt_status_message(tt_pointer_error(netfile)));
638        XeFree(tmpfile);
639        SPC_Error(SPC_Bad_Authentication);
640        return(SPC_ERROR);
641      }
642      free_netfile = 1;
643      SPC_Format_Log("     Authentication 'netfile' is '%s'.", netfile);
644   }
645
646   /* As we send the next packet, include protocol and host info about */
647   /* us (the server).  If going to a pre A.02 client, it will be      */
648   /* ignored by it.                                                   */
649
650   SPC_Write_Protocol_Request(client_connection, NULL, LOGFILE_REPLY, 
651                              netfile, SPC_PROTOCOL_VERSION_STR, 
652                              SPC_LocalHostinfo());
653
654   prot=SPC_Filter_Connection(client_connection, NULL, REGISTER, TRUE);
655   if(prot==SPC_ERROR) {
656     XeFree(tmpfile);
657     return(SPC_ERROR);
658   }
659   sprintf(buffer, (XeString)"--> REGISTER (%s)", netfile);
660   print_protocol_request(buffer, prot);
661   SPC_Free_Protocol_Ptr(prot);
662
663   /* Was the client able to create the authentication */
664   /* file in the temp directory?                        */
665
666   if(lstat(tmpfile, &buf)==ERROR) {
667     int terrno = errno;
668     SPC_Write_Protocol_Request(client_connection, NULL, LOGFILE_REPLY,
669                                FAILED_FILE_NAME, NULL, NULL);
670     SPC_Format_Log("+++> FAILURE: lstat authentication file '%s'.", tmpfile);
671     SPC_Format_Log("+++> FAILURE: lstat() returned error '%s'\n", 
672                    strerror(terrno));
673     if (free_netfile)
674       tt_free(netfile);
675     XeFree(tmpfile);
676     SPC_Error(SPC_Bad_Authentication);
677     return(SPC_ERROR);
678   }
679
680   if (S_ISLNK(buf.st_mode))
681     {                           /* somebody is jerkin us around */
682       SPC_Write_Protocol_Request(client_connection, NULL, LOGFILE_REPLY,
683                                  FAILED_FILE_NAME, NULL, NULL);
684       SPC_Format_Log("+++> FAILURE: lstat authentication file '%s' is a symlink! Possible compromise attempt.", tmpfile);
685       if (free_netfile)
686         tt_free(netfile);
687       XeFree(tmpfile);
688       SPC_Error(SPC_Bad_Authentication);
689       return(SPC_ERROR);
690     }
691
692   /*
693    * If the file does not have the setuid bit set then return failure.
694    *
695    * Note that if the protocol_version is < 2, this bit will
696    * not be set and the client will not be able to connect.
697    */
698   if(!(buf.st_mode & S_ISUID)) {
699     SPC_Write_Protocol_Request(client_connection, NULL, LOGFILE_REPLY,
700                                FAILED_FILE_NAME, NULL, NULL);
701     SPC_Format_Log("+++> FAILURE: authentication file '%s' does not have the setuid bit set.",
702                    tmpfile);
703     if (free_netfile)
704       tt_free(netfile);
705     XeFree(tmpfile);
706     SPC_Error(SPC_Bad_Permission);
707     return(SPC_ERROR);
708   }
709     
710   unlink(tmpfile);
711   XeFree(tmpfile);
712     
713   /* 
714    * Is uid associated with the authentication file created by the 
715    * client present in our password file?    
716    */
717   if(!(pwent=getpwuid(buf.st_uid))) {
718     SPC_Write_Protocol_Request(client_connection, NULL, LOGFILE_REPLY,
719                                FAILED_FILE_NAME, NULL, NULL);
720     SPC_Format_Log("+++> FAILURE: the authentication file created by the client has a uid '%d'\n     and this uid is not in the password file.",
721                      buf.st_uid);
722     SPC_Error(SPC_Bad_Username);
723     if (free_netfile)
724       tt_free(netfile);
725     return(SPC_ERROR);
726   }
727
728   /*
729    * Comapre the user name in the request with the user name in 
730    * the passwd file.  They must be the same to continue.
731    */
732   if (strcmp (pwent->pw_name, username) != 0) {
733       SPC_Write_Protocol_Request(client_connection, NULL, LOGFILE_REPLY,
734                                  FAILED_FILE_NAME, NULL, NULL);
735       SPC_Format_Log("+++> FAILURE: the request is for username '%s' with uid '%d'\n     but this uid has name '%s' in the password file.",
736                      username,
737                      buf.st_uid,
738                      pwent->pw_name);
739       SPC_Error(SPC_Bad_Password);
740       if (free_netfile)
741         tt_free(netfile);
742       return(SPC_ERROR);
743   }
744
745   client_validated=TRUE;
746
747   /*
748    * Need to initialize the group access list if a username
749    * was supplied in the request.
750    */
751   if ((initgroups(username, pwent->pw_gid)) == -1) {
752         SPC_Format_Log("+++> FAILURE: initgroups ('%s', '%s')",
753                        username, 
754                        pwent->pw_gid);
755   }
756
757   /* We have authenticated ourselves.  Set the process identifiers of
758      this process to the looked up ones. */
759   setgid(pwent->pw_gid);
760   setuid(pwent->pw_uid);
761
762   Xechdir(pwent->pw_dir);
763   sprintf(HomeDir,  (XeString)"HOME=%s",  pwent->pw_dir);
764   sprintf(ShellDir, (XeString)"SHELL=%s", pwent->pw_shell);
765   putenv(HomeDir);
766   putenv(ShellDir);
767
768   spc_user_environment_file=(XeString)XeMalloc(MAXPATHLEN);
769   sprintf(spc_user_environment_file, (XeString)"%s/%s/%s", pwent->pw_dir, 
770           SPCD_ENV_HOME_DIRECTORY, SPCD_ENV_FILE);
771   default_environment=
772     SPC_Add_Env_File(spc_user_environment_file, default_environment);
773
774   SPC_Write_Protocol_Request(client_connection, NULL, LOGFILE_REPLY,
775                              PASSED_FILE_NAME, NULL, NULL);
776
777   return(prot->seqno);
778 }
779
780 /*----------------------------------------------------------------------+*/
781 int Client_Unregister(protocol_request_ptr prot)
782 /*----------------------------------------------------------------------+*/
783 {
784   return(print_protocol_request((XeString)"--> UNREGISTER", prot));
785 }
786
787 /*----------------------------------------------------------------------+*/
788 int Client_Channel_Open(protocol_request_ptr prot)
789 /*----------------------------------------------------------------------+*/
790 {
791   int iomode;
792   SPC_Channel_Ptr channel;
793
794   print_protocol_request((XeString)"--> CHANNEL_OPEN", prot);
795   READ_OPEN(prot->dataptr, iomode);
796
797   /* Don't ever wanna wait, do line-oriented reads,
798      use the toolkit, or execute as a system command */
799
800   /* We don't do system commands because the client side has
801      already converted the channel into the proper form for us */
802
803   iomode &= ~(SPCIO_WAIT | SPCIO_LINEORIENTED |
804               SPCIO_USE_XTOOLKIT | SPCIO_SYSTEM);
805
806   /* However, we do always want our termination to be synchronous */
807
808   iomode |= SPCIO_SYNC_TERMINATOR;
809
810   channel=XeSPCOpen(NULL, iomode);
811   if(channel==SPC_ERROR)
812     return(SPC_ERROR);
813
814   XeSPCAddInput(channel, SPCD_Handle_Application_Data, channel);
815   XeSPCRegisterTerminator(channel, SPCD_Termination_Handler, channel);
816
817   return((int) (intptr_t) channel);
818 }
819
820 /*----------------------------------------------------------------------+*/
821 int Client_Channel_Close(protocol_request_ptr prot)
822 /*----------------------------------------------------------------------+*/
823 {
824   SPC_Channel_Ptr channel=prot->channel;
825
826   print_protocol_request((XeString)"--> CHANNEL_CLOSE", prot);
827
828   if(IS_ACTIVE(channel)) {
829
830     /*
831       Uh-oh.  We have received a close request, but the channel is
832       active.  We kill the process, and explicitly wait for the
833       process to terminate.
834       */
835
836     XeSPCSignalProcess(channel, SIGKILL);
837     SPC_Wait_For_Termination(channel);
838   }
839   
840   return(XeSPCClose(channel));
841 }
842
843 /*----------------------------------------------------------------------+*/
844 int Client_Channel_Reset(protocol_request_ptr prot)
845 /*----------------------------------------------------------------------+*/
846 {
847   SPC_Channel_Ptr channel=prot->channel;
848
849   print_protocol_request((XeString)"--> CHANNEL_RESET", prot);
850
851   return(XeSPCReset(channel));
852 }
853
854 /*----------------------------------------------------------------------+*/
855 int Client_Channel_Attach(protocol_request_ptr prot)
856 /*----------------------------------------------------------------------+*/
857 {
858   SPC_Channel_Ptr channel=prot->channel;
859   int pid;
860
861   print_protocol_request((XeString)"--> ATTACH", prot);
862
863   READ_ATTACH(prot->dataptr, pid);
864   return(XeSPCAttach(channel, pid));
865 }
866
867 static void
868 Merge_Lang_Var(SPC_Channel_Ptr channel)
869 {
870   XeString stdLang;
871
872   if ((stdLang = SPC_Getenv((XeString)"LANG", channel->envp))
873       != (XeString)NULL)
874   {
875     _DtXlateDb db = NULL;
876     char platform[_DtPLATFORM_MAX_LEN];
877     XeChar *langBuf;
878     int execVer;
879     int compVer;
880     XeString myLang;
881
882     if (_DtLcxOpenAllDbs(&db) == 0)
883     {
884       if ((_DtXlateGetXlateEnv(db, platform, &execVer, &compVer) == 0) &&
885           (_DtLcxXlateStdToOp(db, platform, compVer, DtLCX_OPER_SETLOCALE,
886                               stdLang, NULL, NULL, NULL, &myLang) == 0))
887       {
888         if ((langBuf = (XeChar *)malloc((strlen(myLang) + 6)
889                                         * sizeof(XeChar)))
890             != (XeChar *)NULL)
891         {
892           sprintf(langBuf, "LANG=%s", myLang);
893           channel->envp = SPC_Putenv(langBuf, channel->envp);
894           free(langBuf);
895         }
896
897         free(myLang);
898       }
899
900       _DtLcxCloseDb(&db);
901     }
902   }
903 }
904
905 /*----------------------------------------------------------------------+*/
906 int Client_Application_Spawn(protocol_request_ptr prot)
907 /*----------------------------------------------------------------------+*/
908 {
909   SPC_Channel_Ptr channel=prot->channel;
910   int retval;
911
912   print_protocol_request((XeString)"--> APPLICATION_SPAWN", prot);
913   READ_APPLICATION_SPAWN(prot->dataptr,
914                          channel->path,
915                          channel->context_dir,
916                          channel->argv, channel->envp);
917
918   Merge_Lang_Var(channel);
919
920   channel->IOMode |= SPCIO_DEALLOC_ARGV;
921
922   channel->envp=SPC_Merge_Envp(channel->envp, default_environment);
923
924   retval=XeSPCExecuteProcess(channel);
925   if(retval==SPC_ERROR)
926     return(SPC_ERROR);
927
928   return(channel->pid);
929 }
930
931 /*----------------------------------------------------------------------+*/
932 int Client_Application_Signal(protocol_request_ptr prot)
933 /*----------------------------------------------------------------------+*/
934 {
935   XeString signame;
936   int sig = 0;
937   SPC_Channel_Ptr channel=prot->channel;
938
939   print_protocol_request((XeString)"--> APPLICATION_SIGNAL", prot);
940   READ_STRING_NO_COPY(prot->dataptr, signame);
941
942   if (client_connection->protocol_version >= 2) {
943       if ( (sig = XeNameToSignal( signame )) == XE_SIG_NOT_IN_TABLE ) {
944           SPC_Error(SPC_Bad_Signal_Name, signame);
945           return (SPC_ERROR);
946       }
947   } else {
948       /* Must be the old 1.0, 1.1 protocol, assume its a number.       */
949       /* Note however, that this is not portable as the signal numbers */
950       /* differ from system to system.  This should really be an error */
951
952       /* 1.0 code allowed signal 0 (check if process alive), but its     */
953       /* part of XPG3 so we don't honor it .. nobody ever used it anyway */
954
955       int ok = sscanf(signame, "%x", &sig);  /* WRITE_INT (%x) was used */
956       if ( (ok == EOF) || (sig == 0) ) {
957           SPC_Error(SPC_Bad_Signal_Format, signame);
958           return (SPC_ERROR);
959       }
960   }
961
962   return(XeSPCSignalProcess(channel, sig));
963 }
964
965 /*----------------------------------------------------------------------+*/
966 int Client_Application_Data(protocol_request_ptr prot)
967 /*----------------------------------------------------------------------+*/
968 {
969   SPC_Channel_Ptr channel=prot->channel;
970   buffered_data_ptr pdata=prot->dataptr;
971
972   print_protocol_request((XeString)"--> APPLICATION_DATA", prot);
973
974   return(XeSPCWrite(channel,
975                     pdata->data+REQUEST_HEADER_LENGTH,
976                     pdata->len));
977 }
978
979 /*----------------------------------------------------------------------+*/
980 int Client_Server_Debug(protocol_request_ptr prot)
981 /*----------------------------------------------------------------------+*/
982 {
983   SPC_Channel_Ptr channel=prot->channel;
984   buffered_data_ptr pdata=prot->dataptr;
985   XeChar filename[MAXPATHLEN];
986   time_t timeval;
987
988   print_protocol_request((XeString)"--> SERVER_DEBUG", prot);
989   READ_DEBUG(pdata, filename);
990
991   if(SPC_Print_Protocol)
992     fclose(SPC_Print_Protocol);
993
994   SPC_Print_Protocol=fopen(filename, (XeString)"a+");
995
996   if(!SPC_Print_Protocol)
997     return(ERROR);
998
999   setbuf(SPC_Print_Protocol, NULL);
1000
1001   time(&timeval);
1002   fprintf(SPC_Print_Protocol, (XeString)"Begin protocol filedump: %s", ctime(&timeval));
1003   return(TRUE);
1004 }
1005
1006 /*
1007  ***
1008  *** The purpose of this routine is to filter out environment variables
1009  *** which should not get put into the environment.
1010  ***
1011 */
1012
1013 /*----------------------------------------------------------------------+*/
1014 void conditional_putenv(XeString env_str)
1015 /*----------------------------------------------------------------------+*/
1016 {
1017   if(strncmp(env_str, (XeString)"HOME=", 5) && /* HOME may be different */
1018      strncmp(env_str, (XeString)"PWD=", 4)     /* PWD set by chdir, may change */
1019                                                /* Should SHELL be on this list? */
1020      )
1021     {
1022       putenv(env_str);
1023     }
1024 }
1025
1026 /*----------------------------------------------------------------------+*/
1027 int Client_Environ_Reset(protocol_request_ptr prot)
1028 /*----------------------------------------------------------------------+*/
1029 {
1030   int num_vars;
1031   XeString envp[100];
1032   char **ret;
1033   int outlen, i;
1034
1035   ret = NULL;
1036   READ_ENVIRON_RESET(prot->dataptr, num_vars);
1037   if(num_vars < 100)
1038     ret = envp;
1039
1040   ret = SPC_Get_Multi_Packet(client_connection, prot,
1041                              ret, &outlen,
1042                              ENVIRON_RESET, "--> ENVIRON_RESET");
1043
1044   if(ret == NULL)
1045     return(SPC_ERROR);
1046
1047   for(i=0; i<outlen; i++)
1048     if(ret[i] && *ret[i])
1049       conditional_putenv(ret[i]);
1050
1051   if(ret != envp)
1052     free((char *)ret);
1053
1054   return(TRUE);
1055 }
1056
1057 /*----------------------------------------------------------------------+*/
1058 int Client_Reply_Devices(protocol_request_ptr prot)
1059 /*----------------------------------------------------------------------+*/
1060 {
1061   SPC_Channel_Ptr channel=prot->channel;
1062
1063   print_protocol_request((XeString)"--> QUERY_DEVICES", prot);
1064
1065   SPC_Write_Protocol_Request(client_connection, channel, DEVICE_REPLY,
1066                              channel->wires[STDIN]->master_name,
1067                              channel->wires[STDIN]->slave_name,
1068                              channel->wires[STDOUT]->master_name,
1069                              channel->wires[STDOUT]->slave_name,
1070                              channel->wires[STDERR]->master_name,
1071                              channel->wires[STDERR]->slave_name);
1072   return(TRUE);
1073 }
1074
1075 /*----------------------------------------------------------------------+*/
1076 int Client_Reply_Logfile(protocol_request_ptr prot)
1077 /*----------------------------------------------------------------------+*/
1078 {
1079   char *netfile;
1080   SPC_Channel_Ptr channel=prot->channel;
1081
1082   print_protocol_request((XeString)"--> QUERY_LOGFILE", prot);
1083
1084   if (SPC_client_version_number >= SPC_PROTOCOL_VERSION_CDE_BASE && 
1085       IS_SPCIO_USE_LOGFILE(channel->IOMode)) {
1086     netfile = tt_file_netfile (channel->logfile);
1087
1088     if (tt_ptr_error (netfile) != TT_OK) {
1089       SPC_Format_Log("+++> FAILURE: cannot create a 'netfile' name for the logfile.\n     (%s)", 
1090                      tt_status_message(tt_pointer_error(netfile)));
1091       return(SPC_ERROR);
1092     }
1093
1094     SPC_Write_Protocol_Request(client_connection, channel, LOGFILE_REPLY,
1095                                netfile, NULL, NULL);
1096     tt_free (netfile);
1097   }
1098   else
1099     SPC_Write_Protocol_Request(client_connection, channel, LOGFILE_REPLY,
1100                                channel->logfile, NULL, NULL);
1101
1102   return(TRUE);
1103 }
1104
1105 /*----------------------------------------------------------------------+*/
1106 int Client_Delete_Logfile(protocol_request_ptr prot)
1107 /*----------------------------------------------------------------------+*/
1108 {
1109   SPC_Channel_Ptr channel=prot->channel;
1110
1111   print_protocol_request((XeString)"--> DELETE_LOGFILE", prot);
1112
1113   return(XeSPCRemoveLogfile(channel));
1114 }
1115
1116 /*----------------------------------------------------------------------+*/
1117 int Client_Reset_Termio(protocol_request_ptr prot)
1118 /*----------------------------------------------------------------------+*/
1119 {
1120   /* This handles old 1.0 versions of the SPC code.  We used to send */
1121   /* an hp-ux version of the termio struct in a non-portable manner  */
1122   /* We need to be able to "eat" such an request if we get one.      */
1123
1124   print_protocol_request((XeString)"--> RESET_TERMIO", prot);
1125
1126   return(SPC_Get_Termio(prot));
1127 }
1128
1129 /*----------------------------------------------------------------------+*/
1130 int Client_Reset_Termios(protocol_request_ptr prot)
1131 /*----------------------------------------------------------------------+*/
1132 {
1133   print_protocol_request((XeString)"--> RESET_TERMIOS", prot);
1134
1135   return(SPC_Get_Termios(prot));
1136 }
1137
1138 /* New B.00 methods */
1139
1140 /*----------------------------------------------------------------------+*/
1141 int Client_Send_EOF(protocol_request_ptr prot)
1142 /*----------------------------------------------------------------------+*/
1143 {
1144   SPC_Channel_Ptr channel=prot->channel;
1145
1146   print_protocol_request((XeString)"--> SEND_EOF", prot);
1147
1148   return(XeSPCSendEOF(channel));
1149 }
1150
1151 int Client_Channel_Termios(protocol_request_ptr prot)
1152 {
1153   SPC_Channel_Ptr channel=prot->channel;
1154
1155   int connection, side;
1156   char buffer[1024];
1157   struct termios t;
1158
1159   print_protocol_request((XeString)"--> CHANNEL_TERMIOS", prot);
1160
1161   READ_TERMIOS(prot->dataptr, connection, side, buffer);
1162   SPC_Encode_Termios(buffer, &t);
1163   
1164   
1165   return(XeSPCSetTermio(channel, connection, side, &t));
1166 }
1167
1168 int Client_Enhanced_Spawn(protocol_request_ptr prot)
1169 {
1170   SPC_Channel_Ptr channel = prot->channel;
1171   int num_vars;
1172   XeString buf[100];
1173   char **ret;
1174   int numenv, numarg;
1175   int return_value, outlen, i;
1176
1177   ret = NULL;
1178   READ_ENVIRON_RESET(prot->dataptr, num_vars);
1179   if(num_vars < 100)
1180     ret = buf;
1181
1182   ret = SPC_Get_Multi_Packet(client_connection, prot,
1183                              ret, &outlen,
1184                              APP_B00_SPAWN, "--> APP_B00_SPAWN");
1185
1186   if(ret == NULL)
1187     return(SPC_ERROR);
1188
1189   channel->path = strdup(ret[0]);
1190   channel->context_dir  = strdup(ret[1]);
1191   
1192   numarg = atoi(ret[2]);
1193   numenv = atoi(ret[3]);
1194
1195   if(numarg == 0)
1196     channel->argv = NULL;
1197   else {
1198     channel->argv = &ret[4];
1199     ret[numarg+3] = NULL;
1200   }
1201
1202   if(numenv == 0)
1203     channel->envp = NULL;
1204   else {
1205     channel->envp = (char **)XeMalloc((numenv+1)*sizeof(char *));
1206     channel->envp[0] = NULL;
1207     channel->envp = SPC_Merge_Envp(channel->envp, &ret[numarg+4]);
1208
1209     Merge_Lang_Var(channel);
1210
1211     channel->envp = SPC_Merge_Envp(channel->envp, default_environment);
1212   }
1213   
1214   return_value = XeSPCExecuteProcess(channel);
1215
1216   /* Make the world safe for freeing the channel */
1217   
1218   channel->argv = NULL;
1219   if(channel->envp == &ret[numarg+4])
1220     channel->envp = NULL;
1221   
1222   for(i=0; i<outlen; i++)
1223     if(ret[i])
1224       free(ret[i]);
1225   
1226   if(ret != buf)
1227     free((char *)ret);
1228
1229   if(return_value==SPC_ERROR)
1230     return(SPC_ERROR);
1231   
1232   return(channel->pid);
1233 }
1234
1235 /*----------------------------------------------------------------------+*/
1236 void SPCD_Reply(SPC_Connection_Ptr connection,
1237                  protocol_request_ptr prot,
1238                  int retval,
1239                  int errval)
1240 /*----------------------------------------------------------------------+*/
1241 {
1242   if(retval==SPC_ERROR)
1243     retval= (-XeSPCErrorNumber);
1244
1245   SPC_Write_Reply(connection, prot, retval, errval);
1246 }
1247
1248 /*----------------------------------------------------------------------+*/
1249   /*
1250    * This function is invoked when the exit timer expires.
1251    * If sub-processes are running, return to select; 
1252    * otherwise exit.
1253    */
1254 #ifdef SA_HANDLER_INT_ARG
1255 void SPCD_Alarm_Handler(int not_used)
1256 #else
1257 void SPCD_Alarm_Handler()
1258 #endif /* SA_HANDLER_INT_ARG */
1259 /*----------------------------------------------------------------------+*/
1260 {
1261   int i;
1262
1263   if (exit_timeout == SPCD_NO_TIMER) 
1264     return;
1265
1266   if (SPC_pid_list != NULL) 
1267     for (i=0; SPC_pid_list[i] != 0; i++) {
1268       if (SPC_pid_list[i] != SPCD_DEAD_PROCESS) {
1269         /*
1270          * Have at least one sub- process running so reset the 
1271          * alarm and return to select.
1272          */
1273         (void) alarm (exit_timeout * 60);
1274         return;
1275       }
1276     }
1277
1278   /*
1279    * There are no sub-processes running.  Exit if a request is not pending
1280    */
1281   if (request_pending != SPCD_REQUEST_PENDING) {
1282     SPC_Format_Log((XeString)
1283                    "Exit timer expired after '%d' minutes of no activity.", 
1284                    exit_timeout);
1285     SPCD_Exit (0);
1286   }
1287 }