2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
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)
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
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
24 * File: main.c $TOG: main.c /main/8 1999/09/30 15:31:41 mgreess $
27 * (c) Copyright 1988, Hewlett-Packard Company, all rights reserved.
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. *
35 #include <bms/sbport.h>
37 #include <time.h> /* ctime() */
41 #include <unistd.h> /* initgroups() */
42 #include <string.h> /* basename() on DEC */
44 # include <libgen.h> /* basename() everywhere else. */
46 #include <sys/param.h> /* MAXPATHLEN */
49 #include <bms/MemoryMgr.h>
50 #include <bms/XeUserMsg.h>
51 #include <bms/usersig.h>
55 #include <SPC/spc-proto.h>
56 #include <XlationSvc.h>
57 #include <LocaleXlate.h>
61 #include "spcd_event.h"
63 static char *MOUNT_POINT = "DTMOUNTPOINT";
65 /* Forward declarations */
66 /** ------------------ **/
67 static void Merge_Lang_Var(SPC_Channel_Ptr channel);
69 void SPCD_Handle_Client_Data(void *channel, int *source, SbInputId *id );
70 int SPCD_Initialize(void);
71 void SPCD_Exit(int exitval);
72 void SPCD_Handle_Application_Data(void *chn, XeString text, int size, int conn_type );
73 void SPCD_Termination_Handler(SPC_Channel_Ptr channel, int pid, int type, int cause, void *data );
74 int Client_Abort(protocol_request_ptr prot);
75 int Client_Register(protocol_request_ptr prot);
76 int Client_Unregister(protocol_request_ptr prot);
77 int Client_Channel_Open(protocol_request_ptr prot);
78 int Client_Channel_Close(protocol_request_ptr prot);
79 int Client_Channel_Reset(protocol_request_ptr prot);
80 int Client_Channel_Attach(protocol_request_ptr prot);
81 int Client_Application_Spawn(protocol_request_ptr prot);
82 int Client_Application_Signal(protocol_request_ptr prot);
83 int Client_Application_Data(protocol_request_ptr prot);
84 int Client_Server_Debug(protocol_request_ptr prot);
85 int Client_Environ_Reset(protocol_request_ptr prot);
86 int Client_Reply_Devices(protocol_request_ptr prot);
87 int Client_Reply_Logfile(protocol_request_ptr prot);
88 int Client_Delete_Logfile(protocol_request_ptr prot);
89 int Client_Reset_Termio(protocol_request_ptr prot);
90 int Client_Reset_Termios(protocol_request_ptr prot);
91 int Client_Protocol_Version(protocol_request_ptr prot);
92 int Client_Reply_Protocol(protocol_request_ptr prot);
93 int Client_Reply_Hostinfo(protocol_request_ptr prot);
95 /* New B.00 methods */
97 int Client_Send_EOF(protocol_request_ptr prot);
98 int Client_Channel_Termios(protocol_request_ptr prot);
99 int Client_Enhanced_Spawn(protocol_request_ptr prot);
101 #if defined(_AIX) || defined(DEC)
102 # define SA_HANDLER_INT_ARG
103 #endif /* _AIX || DEC */
105 void conditional_putenv(XeString env_str);
106 void SPCD_Reply(SPC_Connection_Ptr connection, protocol_request_ptr prot, int retval, int errval );
107 #ifdef SA_HANDLER_INT_ARG
108 void SPCD_Alarm_Handler (int);
110 void SPCD_Alarm_Handler (void);
111 #endif /* SA_HANDLER_INT_ARG */
115 protocol_request_handler spcd_protocol_dispatch_table[NREQS]={
116 Client_Application_Data, /* APPLICATION_DATA */
117 NULL, /* APPLICATION_STDOUT */
118 NULL, /* APPLICATION_STDERR */
119 Client_Abort, /* ABORT */
120 Client_Register, /* REGISTER */
121 Client_Unregister, /* UNREGISTER */
122 Client_Channel_Open, /* CHANNEL_OPEN */
123 Client_Channel_Close, /* CHANNEL_CLOSE */
124 Client_Channel_Reset, /* CHANNEL_RESET */
125 Client_Channel_Attach, /* CHANNEL_ATTACH */
126 Client_Application_Spawn, /* APPLICATION_SPAWN */
127 Client_Application_Signal, /* APPLICATION_SIGNAL */
128 NULL, /* APPLICATION_DIED */
129 NULL, /* SERVER_ERROR */
131 Client_Server_Debug, /* SERVER_DEBUG */
132 Client_Environ_Reset, /* ENVIRON_RESET */
133 Client_Reply_Devices, /* QUERY_DEVICES */
134 NULL, /* DEVICE_REPLY */
135 Client_Reply_Logfile, /* QUERY_LOGFILE */
136 NULL, /* LOGFILE_REPLY */
137 Client_Delete_Logfile, /* DELETE_LOGFILE */
138 Client_Reset_Termio, /* RESET_TERMIO (obsolete) */
139 Client_Reset_Termios, /* RESET_TERMIOS */
140 Client_Send_EOF, /* CHANNEL_SEND_EOF */
141 Client_Channel_Termios, /* CHANNEL_TERMIOS */
142 Client_Enhanced_Spawn, /* APP_B00_SPAWN */
146 * HomeDir will be set to "HOME=pwent->pw_dir".
148 * ShellDir will be set to "SHELL=pwent->pw_shell".
150 XeChar HomeDir[MAXPATHLEN + 6];
151 XeChar ShellDir[MAXPATHLEN + 7];
152 XeString *default_environment;
154 int client_validated=0;
155 int SPCD_Abort_Okay = FALSE;
157 SPC_Connection_Ptr client_connection;
159 SPC_Channel client_channel;
162 * This variable contains the number of minutes in the daemon's
163 * exit timer. If the daemon has no activity within this period
164 * of time and there are no sub-processes running, the daemon
167 static int exit_timeout = SPCD_DEFAULT_TIMEOUT;
170 * The following variable is used by the timer code to indicate
171 * if a request is currently being serviced. This is necessary
172 * because of the following scenario:
174 * t0 - a request is made
175 * t1 - the request is serviced
176 * t2 - the timer expires
177 * t3 - the timer handler sees no sub-processes running so it exits
178 * t4 - request is done, reply to client
180 * This variable is set to SPCD_REQUEST_PENDING at t0 and to
181 * SPCD_NO_REQUST_PENDING at t4. If at t3 the variable is set
182 * to SPCD_REQUEST_PENDING, the alarm will be reset and the
183 * daemon will continue.
185 static int request_pending = SPCD_NO_REQUEST_PENDING;
187 /*----------------------------------------------------------------------+*/
188 int main(int argc, XeString *argv)
189 /*----------------------------------------------------------------------+*/
191 /* Parse the command line and set globals accordingly. */
192 XeString log_path = NULL;
193 Boolean terminate_flag = FALSE;
195 struct sigaction alarm_vector;
200 * The SPC library needs to know this is a SPC 'daemon' process
201 * to ensure 'SPC_Initialize()' installs a SIGCLD handler.
203 SPC_who_am_i = SPC_I_AM_A_DAEMON;
204 SPCD_Authentication_Dir = NULL;
206 /* set up log file path */
207 log_path = XeSBTempPath((XeString)"DTSPCD.log");
209 freopen("/dev/null", "w", stderr);
211 /* Process arguments and set flags. */
212 for (i=1; i < argc; i++) {
214 if (!strcmp ("-log", argv[i])) {
215 /* Log mode. Print information to a log file */
216 /* Open an error log with whatever name the library wants to use */
217 SPC_Open_Log(log_path, FALSE);
220 else if (!strcmp ("-debug", argv[i])) {
221 /* Debug mode. Print protocol information to a log file */
222 /* Open an error log with whatever name the library wants to use */
223 SPC_Open_Log(log_path, FALSE);
224 SPC_Print_Protocol = spc_logF;
225 freopen(log_path, "a", stderr);
226 setbuf(stderr, NULL);
229 else if (!strcmp ("-auth_dir", argv[i])) {
231 * Used to override the default directory for authentication file
235 SPC_Format_Log((XeString)"Authentication directory set to '%s'.",
237 SPCD_Authentication_Dir = strdup (argv[i]);
241 else if (!strcmp ("-mount_point", argv[i])) {
243 * Mount point for the filename mapping system.
247 (void) sprintf (tmp, "%s=%s", MOUNT_POINT, argv[i]);
248 if (putenv (tmp) == 0) {
249 SPC_Format_Log((XeString)"Mount point set to '%s'.", argv[i]);
250 SPC_mount_point_env_var = (char *) malloc (strlen (tmp) + 1);
251 (void) strcpy (SPC_mount_point_env_var, tmp);
254 SPC_Format_Log((XeString)"Failed to add the mount point '%s' to the environment.", tmp);
258 else if (!strcmp ("-timeout", argv[i])) {
260 * The timeout is specified, so use it instead of the default.
264 exit_timeout = atoi(argv[i]);
265 SPC_Format_Log((XeString)"Setting the exit timer to '%s' minutes.",
272 * Unknown command option
274 SPC_Format_Log((XeString)"Command line option '%s' unrecognized.",
279 /* free strings allocated for path */
283 * Initialize the i/o function pointers.
285 SbAddInput_hookfn = SPCD_AddInput;
286 SbAddException_hookfn = SPCD_AddException;
287 SbRemoveInput_hookfn = SPCD_RemoveInput;
288 SbRemoveException_hookfn = SPCD_RemoveException;
289 SbMainLoopUntil_hookfn = SPCD_MainLoopUntil;
290 SbBreakMainLoop_hookfn = SPCD_BreakMainLoop;
292 /* Initialization Commands */
293 if (SPCD_Initialize()==SPC_ERROR)
296 client_connection=SPC_Start_Daemon(FALSE);
297 if(client_connection==SPC_ERROR)
300 client_channel.connection = client_connection;
302 SPC_XtAddInput(&client_channel,
303 &client_connection->termination_id,
304 client_connection->sid,
305 SPCD_Handle_Client_Data,
308 if (exit_timeout != SPCD_NO_TIMER) {
309 alarm_vector.sa_handler = SPCD_Alarm_Handler;
310 alarm_vector.sa_flags = 0;
311 (void) sigaction (SIGALRM, &alarm_vector, (struct sigaction *)NULL);
312 (void) alarm (exit_timeout * 60);
316 * The daemon's mount point environment variable needs to be
317 * saved. It will be used to override the client's mount point
318 * setting or the mount point specified in any of the environment
321 if (SPC_mount_point_env_var == NULL)
322 if ((pch = getenv (MOUNT_POINT)) != NULL) {
323 SPC_mount_point_env_var = (char *) malloc (strlen (pch) +
324 strlen (MOUNT_POINT) + 2);
325 (void) sprintf (SPC_mount_point_env_var, "%s=%s", MOUNT_POINT, pch);
328 XeCall_SbMainLoopUntil(&terminate_flag);
332 /*----------------------------------------------------------------------+*/
333 void SPCD_Handle_Client_Data(void *channel,
334 int *UNUSED_PARM(source),
335 SbInputId *UNUSED_PARM(id))
336 /*----------------------------------------------------------------------+*/
338 protocol_request_ptr prot;
340 SPC_Connection_Ptr connection=((SPC_Channel_Ptr)channel)->connection;
342 request_pending = SPCD_REQUEST_PENDING;
344 prot=SPC_Read_Protocol(connection);
348 /* Check for valid client (in other words, that we have registered
351 if((!client_validated) && (prot->request_type != REGISTER)) {
352 SPC_Error(SPC_Client_Not_Valid);
353 if (exit_timeout != SPCD_NO_TIMER)
354 (void) alarm (exit_timeout * 60);
355 request_pending = SPCD_REQUEST_PENDING;
359 SPCD_Abort_Okay = TRUE;
360 retval=SPC_Dispatch_Protocol(prot, spcd_protocol_dispatch_table);
361 SPCD_Abort_Okay = FALSE;
362 if(REPLY_EXPECTED(prot->request_type, TRUE) != NO_REPLY_VAL)
363 SPCD_Reply(connection, prot, retval, errno);
364 SPC_Free_Protocol_Ptr(prot);
367 * Reset the alarm and go back to select.
369 if (exit_timeout != SPCD_NO_TIMER)
370 (void) alarm (exit_timeout * 60);
372 request_pending = SPCD_NO_REQUEST_PENDING;
375 /*----------------------------------------------------------------------+*/
376 int SPCD_Initialize(void)
377 /*----------------------------------------------------------------------+*/
379 XeString sys_env_file = NULL;
381 /* Do initialization for SPC */
382 if(SPC_Initialize()==SPC_ERROR)
385 /* Do Daemon specific initialization */
389 XeProgName=SPCD_PROG_NAME;
392 (XeString *) malloc(DEFAULT_ENVP_SIZE * sizeof(XeString));
393 default_environment[0]=NULL;
396 * First add the installed environment file.
398 sys_env_file = (XeString) malloc (strlen(SPCD_ENV_INSTALL_DIRECTORY) +
399 strlen(SPCD_ENV_FILE) + 3);
400 (void) sprintf (sys_env_file, "%s/%s",
401 SPCD_ENV_INSTALL_DIRECTORY,
403 default_environment=SPC_Add_Env_File(sys_env_file,default_environment);
406 * Now add the configured environment file.
408 sys_env_file = (XeString) realloc (sys_env_file,
409 strlen(SPCD_ENV_CONFIG_DIRECTORY) +
410 strlen(SPCD_ENV_FILE) + 3);
411 (void) sprintf (sys_env_file, "%s/%s",
412 SPCD_ENV_CONFIG_DIRECTORY,
415 default_environment=SPC_Add_Env_File(sys_env_file,default_environment);
422 /*----------------------------------------------------------------------+*/
423 void SPCD_Exit(int exitval)
424 /*----------------------------------------------------------------------+*/
428 if (SPC_logfile_list != NULL)
429 for (i = 0; SPC_logfile_list[i] != NULL; i++)
430 (void) unlink (SPC_logfile_list[i]);
432 SPC_Format_Log((XeString)"Exiting server. Retval: %d", exitval);
438 /*----------------------------------------------------------------------+*/
439 void SPCD_Handle_Application_Data(void * chn,
443 /*----------------------------------------------------------------------+*/
445 SPC_Channel_Ptr channel= (SPC_Channel_Ptr) chn;
446 SPC_Write_Protocol_Request(client_connection, channel,
447 CONNECTOR_TO_PROT(conn_type), text, size);
451 /*----------------------------------------------------------------------+*/
452 void SPCD_Termination_Handler(SPC_Channel_Ptr channel,
453 int UNUSED_PARM(pid),
454 int UNUSED_PARM(type),
455 int UNUSED_PARM(cause),
456 void * UNUSED_PARM(data))
457 /*----------------------------------------------------------------------+*/
459 /* Write a termination protocol request */
461 SPC_Write_Protocol_Request(client_connection, channel,
462 APPLICATION_DIED, channel->status);
464 /* That's all, folks! */
469 *** Protocol request handlers
473 /*----------------------------------------------------------------------+*/
474 int Client_Abort(protocol_request_ptr prot)
475 /*----------------------------------------------------------------------+*/
477 return(print_protocol_request((XeString)"--> ABORT", prot));
480 #define FREE_USER_PASS(a, b) free(a); free(b);
482 /*----------------------------------------------------------------------+*/
483 int Client_Register(protocol_request_ptr prot)
484 /*----------------------------------------------------------------------+*/
490 struct passwd *pwent;
491 XeString tmpfile, tmppath;
492 XeChar buffer[MAXPATHLEN * 2];
495 int free_netfile = 0;
496 char *spc_prefix = "/.SPC_";
498 char tmpnam_buf[L_tmpnam + 1];
500 print_protocol_request((XeString)"--> REGISTER", prot);
503 READ_REGISTER(prot->dataptr, username, passwd, proto_ver, hostinfo);
505 if(strcmp(username, "") != 0) {
506 SPC_Format_Log ( "+++> Starting authentication for user '%s'\n from host '%s'.",
509 * We have a username so generate a temp filename and send it
510 * back to the client after creating the proper path for the file.
512 if(SPCD_Authentication_Dir)
514 * Use the directory specified on the command line.
516 tmppath = strdup(SPCD_Authentication_Dir);
519 * Use the $HOME directory if it can be retrieved from the
522 if(!(pwent=getpwnam(username))) {
523 SPC_Format_Log ("+++> FAILURE: username '%s' is unknown.", username);
524 SPC_Write_Protocol_Request(client_connection, NULL, LOGFILE_REPLY,
525 FAILED_FILE_NAME, NULL, NULL);
526 FREE_USER_PASS(username, passwd);
527 SPC_Error(SPC_Bad_Username);
531 tmppath = (XeString)(pwent->pw_dir);
533 if ((lstat (tmppath, &buf) != 0) ||
534 (!S_ISDIR(buf.st_mode)) ||
535 (!(buf.st_mode & S_IRUSR)))
537 * Use the default directory.
539 tmppath = XeSBTempPath(XeString_NULL);
544 * tempnam(3) has side effects caused by permissions of the directory
545 * given or the TMPDIR envirnoment variable. Because of these side
546 * effects, the function may return "/tmp/.SPC_xxxxxx" and ignore
547 * tmppath. The protocol will fail when this occurs. The fix is
548 * to construct the tmpfile name.
551 spc_suffix = basename(tmpnam_buf); /* Don't free result - not alloc'd! */
553 /* Allocate space for tmppath, spc_prefix, and spc_suffix. */
554 tmpfile = (char *)malloc((strlen(tmppath) + strlen(spc_prefix) +
555 strlen(spc_suffix) + 1) * sizeof(char));
556 sprintf(tmpfile, "%s%s%s", tmppath, spc_prefix, spc_suffix);
561 * No username was supplied (this could happen with pre-CDE
562 * clients) so generate a temp filename.
564 if(SPCD_Authentication_Dir)
565 tmppath = strdup(SPCD_Authentication_Dir);
567 tmppath = XeSBTempPath(XeString_NULL);
568 tmpfile = tempnam(tmppath, ".SPC_" );
572 SPC_Format_Log ("+++> FAILURE: NULL username.");
573 SPC_Write_Protocol_Request(client_connection, NULL, LOGFILE_REPLY,
574 FAILED_FILE_NAME, NULL, NULL);
575 FREE_USER_PASS(username, passwd);
576 SPC_Error(SPC_Bad_Username);
582 SPC_Write_Protocol_Request(client_connection, NULL, LOGFILE_REPLY,
583 FAILED_FILE_NAME, NULL, NULL);
584 SPC_Format_Log("+++> FAILURE: cannot malloc.");
585 SPC_Error(SPC_Out_Of_Memory);
590 * Store info about the client (protocol version & host type)
591 * For Pre A.01, this will be defaulted to (hpux 7.0 s300)
593 client_connection->hostinfo = hostinfo;
596 (void) sscanf(proto_ver, "%d", &client_connection->protocol_version);
597 SPC_client_version_number = client_connection->protocol_version;
601 /* The client didn't send a protocol_version so set it to "1". */
602 SPC_client_version_number = 1;
604 SPC_Format_Log(" Client protocol version is '%d'.",
605 SPC_client_version_number);
606 SPC_Format_Log("+++> Authentication file is '%s'.", tmpfile);
609 * For non-CDE clients, the clients expect the daemon to send
610 * a "real" file name. Beginning with 'SPC_PROTOCOL_VERSION_CDE_BASE'
611 * the clients are expecting a "netfile" name.
615 if (SPC_client_version_number >= SPC_PROTOCOL_VERSION_CDE_BASE) {
616 netfile = tt_file_netfile (tmpfile);
618 if (tt_ptr_error (netfile) != TT_OK) {
619 SPC_Write_Protocol_Request(client_connection, NULL, LOGFILE_REPLY,
620 FAILED_FILE_NAME, NULL, NULL);
621 SPC_Format_Log("+++> FAILURE: cannot create a cannonical file name for the authentication file.\n (%s)",
622 tt_status_message(tt_pointer_error(netfile)));
624 SPC_Error(SPC_Bad_Authentication);
628 SPC_Format_Log(" Authentication 'netfile' is '%s'.", netfile);
631 /* As we send the next packet, include protocol and host info about */
632 /* us (the server). If going to a pre A.02 client, it will be */
635 SPC_Write_Protocol_Request(client_connection, NULL, LOGFILE_REPLY,
636 netfile, SPC_PROTOCOL_VERSION_STR,
637 SPC_LocalHostinfo());
639 prot=SPC_Filter_Connection(client_connection, NULL, REGISTER, TRUE);
642 sprintf(buffer, (XeString)"--> REGISTER (%s)", netfile);
643 print_protocol_request(buffer, prot);
644 SPC_Free_Protocol_Ptr(prot);
646 /* Was the client able to create the authentication */
647 /* file in the temp directory? */
649 if(lstat(tmpfile, &buf)==ERROR) {
651 SPC_Write_Protocol_Request(client_connection, NULL, LOGFILE_REPLY,
652 FAILED_FILE_NAME, NULL, NULL);
653 SPC_Format_Log("+++> FAILURE: lstat authentication file '%s'.", tmpfile);
654 SPC_Format_Log("+++> FAILURE: lstat() returned error '%s'\n",
659 SPC_Error(SPC_Bad_Authentication);
663 if (S_ISLNK(buf.st_mode))
664 { /* somebody is jerkin us around */
665 SPC_Write_Protocol_Request(client_connection, NULL, LOGFILE_REPLY,
666 FAILED_FILE_NAME, NULL, NULL);
667 SPC_Format_Log("+++> FAILURE: lstat authentication file '%s' is a symlink! Possible compromise attempt.", tmpfile);
671 SPC_Error(SPC_Bad_Authentication);
676 * If the file does not have the setuid bit set then return failure.
678 * Note that if the protocol_version is < 2, this bit will
679 * not be set and the client will not be able to connect.
681 if(!(buf.st_mode & S_ISUID)) {
682 SPC_Write_Protocol_Request(client_connection, NULL, LOGFILE_REPLY,
683 FAILED_FILE_NAME, NULL, NULL);
684 SPC_Format_Log("+++> FAILURE: authentication file '%s' does not have the setuid bit set.",
689 SPC_Error(SPC_Bad_Permission);
697 * Is uid associated with the authentication file created by the
698 * client present in our password file?
700 if(!(pwent=getpwuid(buf.st_uid))) {
701 SPC_Write_Protocol_Request(client_connection, NULL, LOGFILE_REPLY,
702 FAILED_FILE_NAME, NULL, NULL);
703 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.",
705 SPC_Error(SPC_Bad_Username);
712 * Comapre the user name in the request with the user name in
713 * the passwd file. They must be the same to continue.
715 if (strcmp (pwent->pw_name, username) != 0) {
716 SPC_Write_Protocol_Request(client_connection, NULL, LOGFILE_REPLY,
717 FAILED_FILE_NAME, NULL, NULL);
718 SPC_Format_Log("+++> FAILURE: the request is for username '%s' with uid '%d'\n but this uid has name '%s' in the password file.",
722 SPC_Error(SPC_Bad_Password);
728 client_validated=TRUE;
731 * Need to initialize the group access list if a username
732 * was supplied in the request.
734 if ((initgroups(username, pwent->pw_gid)) == -1) {
735 SPC_Format_Log("+++> FAILURE: initgroups ('%s', '%s')",
740 /* We have authenticated ourselves. Set the process identifiers of
741 this process to the looked up ones. */
742 setgid(pwent->pw_gid);
743 setuid(pwent->pw_uid);
745 Xechdir(pwent->pw_dir);
746 sprintf(HomeDir, (XeString)"HOME=%s", pwent->pw_dir);
747 sprintf(ShellDir, (XeString)"SHELL=%s", pwent->pw_shell);
751 spc_user_environment_file=(XeString)XeMalloc(MAXPATHLEN);
752 sprintf(spc_user_environment_file, (XeString)"%s/%s/%s", pwent->pw_dir,
753 SPCD_ENV_HOME_DIRECTORY, SPCD_ENV_FILE);
755 SPC_Add_Env_File(spc_user_environment_file, default_environment);
757 SPC_Write_Protocol_Request(client_connection, NULL, LOGFILE_REPLY,
758 PASSED_FILE_NAME, NULL, NULL);
763 /*----------------------------------------------------------------------+*/
764 int Client_Unregister(protocol_request_ptr prot)
765 /*----------------------------------------------------------------------+*/
767 return(print_protocol_request((XeString)"--> UNREGISTER", prot));
770 /*----------------------------------------------------------------------+*/
771 int Client_Channel_Open(protocol_request_ptr prot)
772 /*----------------------------------------------------------------------+*/
775 SPC_Channel_Ptr channel;
777 print_protocol_request((XeString)"--> CHANNEL_OPEN", prot);
778 READ_OPEN(prot->dataptr, iomode);
780 /* Don't ever wanna wait, do line-oriented reads,
781 use the toolkit, or execute as a system command */
783 /* We don't do system commands because the client side has
784 already converted the channel into the proper form for us */
786 iomode &= ~(SPCIO_WAIT | SPCIO_LINEORIENTED |
787 SPCIO_USE_XTOOLKIT | SPCIO_SYSTEM);
789 /* However, we do always want our termination to be synchronous */
791 iomode |= SPCIO_SYNC_TERMINATOR;
793 channel=XeSPCOpen(NULL, iomode);
794 if(channel==SPC_ERROR)
797 XeSPCAddInput(channel, SPCD_Handle_Application_Data, channel);
798 XeSPCRegisterTerminator(channel, SPCD_Termination_Handler, channel);
800 return((int) channel);
803 /*----------------------------------------------------------------------+*/
804 int Client_Channel_Close(protocol_request_ptr prot)
805 /*----------------------------------------------------------------------+*/
807 SPC_Channel_Ptr channel=prot->channel;
809 print_protocol_request((XeString)"--> CHANNEL_CLOSE", prot);
811 if(IS_ACTIVE(channel)) {
814 Uh-oh. We have recieved a close request, but the channel is
815 active. We kill the process, and explicitly wait for the
816 process to terminate.
819 XeSPCSignalProcess(channel, SIGKILL);
820 SPC_Wait_For_Termination(channel);
823 return(XeSPCClose(channel));
826 /*----------------------------------------------------------------------+*/
827 int Client_Channel_Reset(protocol_request_ptr prot)
828 /*----------------------------------------------------------------------+*/
830 SPC_Channel_Ptr channel=prot->channel;
832 print_protocol_request((XeString)"--> CHANNEL_RESET", prot);
834 return(XeSPCReset(channel));
837 /*----------------------------------------------------------------------+*/
838 int Client_Channel_Attach(protocol_request_ptr prot)
839 /*----------------------------------------------------------------------+*/
841 SPC_Channel_Ptr channel=prot->channel;
844 print_protocol_request((XeString)"--> ATTACH", prot);
846 READ_ATTACH(prot->dataptr, pid);
847 return(XeSPCAttach(channel, pid));
851 Merge_Lang_Var(SPC_Channel_Ptr channel)
855 if ((stdLang = SPC_Getenv((XeString)"LANG", channel->envp))
858 _DtXlateDb db = NULL;
859 char platform[_DtPLATFORM_MAX_LEN];
865 if (_DtLcxOpenAllDbs(&db) == 0)
867 if ((_DtXlateGetXlateEnv(db, platform, &execVer, &compVer) == 0) &&
868 (_DtLcxXlateStdToOp(db, platform, compVer, DtLCX_OPER_SETLOCALE,
869 stdLang, NULL, NULL, NULL, &myLang) == 0))
871 if ((langBuf = (XeChar *)malloc((strlen(myLang) + 6)
875 sprintf(langBuf, "LANG=%s", myLang);
876 channel->envp = SPC_Putenv(langBuf, channel->envp);
888 /*----------------------------------------------------------------------+*/
889 int Client_Application_Spawn(protocol_request_ptr prot)
890 /*----------------------------------------------------------------------+*/
892 SPC_Channel_Ptr channel=prot->channel;
895 print_protocol_request((XeString)"--> APPLICATION_SPAWN", prot);
896 READ_APPLICATION_SPAWN(prot->dataptr,
898 channel->context_dir,
899 channel->argv, channel->envp);
901 Merge_Lang_Var(channel);
903 channel->IOMode |= SPCIO_DEALLOC_ARGV;
905 channel->envp=SPC_Merge_Envp(channel->envp, default_environment);
907 retval=XeSPCExecuteProcess(channel);
908 if(retval==SPC_ERROR)
911 return(channel->pid);
914 /*----------------------------------------------------------------------+*/
915 int Client_Application_Signal(protocol_request_ptr prot)
916 /*----------------------------------------------------------------------+*/
920 SPC_Channel_Ptr channel=prot->channel;
922 print_protocol_request((XeString)"--> APPLICATION_SIGNAL", prot);
923 READ_STRING_NO_COPY(prot->dataptr, signame);
925 if (client_connection->protocol_version >= 2) {
926 if ( (sig = XeNameToSignal( signame )) == XE_SIG_NOT_IN_TABLE ) {
927 SPC_Error(SPC_Bad_Signal_Name, signame);
931 /* Must be the old 1.0, 1.1 protocol, assume its a number. */
932 /* Note however, that this is not portable as the signal numbers */
933 /* differ from system to system. This should really be an error */
935 /* 1.0 code allowed signal 0 (check if process alive), but its */
936 /* part of XPG3 so we don't honor it .. nobody ever used it anyway */
938 int ok = sscanf(signame, "%x", &sig); /* WRITE_INT (%x) was used */
939 if ( (ok == EOF) || (sig == 0) ) {
940 SPC_Error(SPC_Bad_Signal_Format, signame);
945 return(XeSPCSignalProcess(channel, sig));
948 /*----------------------------------------------------------------------+*/
949 int Client_Application_Data(protocol_request_ptr prot)
950 /*----------------------------------------------------------------------+*/
952 SPC_Channel_Ptr channel=prot->channel;
953 buffered_data_ptr pdata=prot->dataptr;
955 print_protocol_request((XeString)"--> APPLICATION_DATA", prot);
957 return(XeSPCWrite(channel,
958 pdata->data+REQUEST_HEADER_LENGTH,
962 /*----------------------------------------------------------------------+*/
963 int Client_Server_Debug(protocol_request_ptr prot)
964 /*----------------------------------------------------------------------+*/
966 SPC_Channel_Ptr channel=prot->channel;
967 buffered_data_ptr pdata=prot->dataptr;
968 XeChar filename[MAXPATHLEN];
971 print_protocol_request((XeString)"--> SERVER_DEBUG", prot);
972 READ_DEBUG(pdata, filename);
974 if(SPC_Print_Protocol)
975 fclose(SPC_Print_Protocol);
977 SPC_Print_Protocol=fopen(filename, (XeString)"a+");
979 if(!SPC_Print_Protocol)
982 setbuf(SPC_Print_Protocol, NULL);
985 fprintf(SPC_Print_Protocol, (XeString)"Begin protocol filedump: %s", ctime(&timeval));
991 *** The purpose of this routine is to filter out environment variables
992 *** which should not get put into the environment.
996 /*----------------------------------------------------------------------+*/
997 void conditional_putenv(XeString env_str)
998 /*----------------------------------------------------------------------+*/
1000 if(strncmp(env_str, (XeString)"HOME=", 5) && /* HOME may be different */
1001 strncmp(env_str, (XeString)"PWD=", 4) /* PWD set by chdir, may change */
1002 /* Should SHELL be on this list? */
1009 /*----------------------------------------------------------------------+*/
1010 int Client_Environ_Reset(protocol_request_ptr prot)
1011 /*----------------------------------------------------------------------+*/
1019 READ_ENVIRON_RESET(prot->dataptr, num_vars);
1023 ret = SPC_Get_Multi_Packet(client_connection, prot,
1025 ENVIRON_RESET, "--> ENVIRON_RESET");
1030 for(i=0; i<outlen; i++)
1031 if(ret[i] && *ret[i])
1032 conditional_putenv(ret[i]);
1040 /*----------------------------------------------------------------------+*/
1041 int Client_Reply_Devices(protocol_request_ptr prot)
1042 /*----------------------------------------------------------------------+*/
1044 SPC_Channel_Ptr channel=prot->channel;
1046 print_protocol_request((XeString)"--> QUERY_DEVICES", prot);
1048 SPC_Write_Protocol_Request(client_connection, channel, DEVICE_REPLY,
1049 channel->wires[STDIN]->master_name,
1050 channel->wires[STDIN]->slave_name,
1051 channel->wires[STDOUT]->master_name,
1052 channel->wires[STDOUT]->slave_name,
1053 channel->wires[STDERR]->master_name,
1054 channel->wires[STDERR]->slave_name);
1058 /*----------------------------------------------------------------------+*/
1059 int Client_Reply_Logfile(protocol_request_ptr prot)
1060 /*----------------------------------------------------------------------+*/
1063 SPC_Channel_Ptr channel=prot->channel;
1065 print_protocol_request((XeString)"--> QUERY_LOGFILE", prot);
1067 if (SPC_client_version_number >= SPC_PROTOCOL_VERSION_CDE_BASE &&
1068 IS_SPCIO_USE_LOGFILE(channel->IOMode)) {
1069 netfile = tt_file_netfile (channel->logfile);
1071 if (tt_ptr_error (netfile) != TT_OK) {
1072 SPC_Format_Log("+++> FAILURE: cannot create a 'netfile' name for the logfile.\n (%s)",
1073 tt_status_message(tt_pointer_error(netfile)));
1077 SPC_Write_Protocol_Request(client_connection, channel, LOGFILE_REPLY,
1078 netfile, NULL, NULL);
1082 SPC_Write_Protocol_Request(client_connection, channel, LOGFILE_REPLY,
1083 channel->logfile, NULL, NULL);
1088 /*----------------------------------------------------------------------+*/
1089 int Client_Delete_Logfile(protocol_request_ptr prot)
1090 /*----------------------------------------------------------------------+*/
1092 SPC_Channel_Ptr channel=prot->channel;
1094 print_protocol_request((XeString)"--> DELETE_LOGFILE", prot);
1096 return(XeSPCRemoveLogfile(channel));
1099 /*----------------------------------------------------------------------+*/
1100 int Client_Reset_Termio(protocol_request_ptr prot)
1101 /*----------------------------------------------------------------------+*/
1103 /* This handles old 1.0 versions of the SPC code. We used to send */
1104 /* an hp-ux version of the termio struct in a non-portable manner */
1105 /* We need to be able to "eat" such an request if we get one. */
1107 print_protocol_request((XeString)"--> RESET_TERMIO", prot);
1109 return(SPC_Get_Termio(prot));
1112 /*----------------------------------------------------------------------+*/
1113 int Client_Reset_Termios(protocol_request_ptr prot)
1114 /*----------------------------------------------------------------------+*/
1116 print_protocol_request((XeString)"--> RESET_TERMIOS", prot);
1118 return(SPC_Get_Termios(prot));
1121 /* New B.00 methods */
1123 /*----------------------------------------------------------------------+*/
1124 int Client_Send_EOF(protocol_request_ptr prot)
1125 /*----------------------------------------------------------------------+*/
1127 SPC_Channel_Ptr channel=prot->channel;
1129 print_protocol_request((XeString)"--> SEND_EOF", prot);
1131 return(XeSPCSendEOF(channel));
1134 int Client_Channel_Termios(protocol_request_ptr prot)
1136 SPC_Channel_Ptr channel=prot->channel;
1138 int connection, side;
1142 print_protocol_request((XeString)"--> CHANNEL_TERMIOS", prot);
1144 READ_TERMIOS(prot->dataptr, connection, side, buffer);
1145 SPC_Encode_Termios(buffer, &t);
1148 return(XeSPCSetTermio(channel, connection, side, &t));
1151 int Client_Enhanced_Spawn(protocol_request_ptr prot)
1153 SPC_Channel_Ptr channel = prot->channel;
1158 int return_value, outlen, i;
1161 READ_ENVIRON_RESET(prot->dataptr, num_vars);
1165 ret = SPC_Get_Multi_Packet(client_connection, prot,
1167 APP_B00_SPAWN, "--> APP_B00_SPAWN");
1172 channel->path = strdup(ret[0]);
1173 channel->context_dir = strdup(ret[1]);
1175 numarg = atoi(ret[2]);
1176 numenv = atoi(ret[3]);
1179 channel->argv = NULL;
1181 channel->argv = &ret[4];
1182 ret[numarg+3] = NULL;
1186 channel->envp = NULL;
1188 channel->envp = (char **)XeMalloc((numenv+1)*sizeof(char *));
1189 channel->envp[0] = NULL;
1190 channel->envp = SPC_Merge_Envp(channel->envp, &ret[numarg+4]);
1192 Merge_Lang_Var(channel);
1194 channel->envp = SPC_Merge_Envp(channel->envp, default_environment);
1197 return_value = XeSPCExecuteProcess(channel);
1199 /* Make the world safe for freeing the channel */
1201 channel->argv = NULL;
1202 if(channel->envp == &ret[numarg+4])
1203 channel->envp = NULL;
1205 for(i=0; i<outlen; i++)
1212 if(return_value==SPC_ERROR)
1215 return(channel->pid);
1218 /*----------------------------------------------------------------------+*/
1219 void SPCD_Reply(SPC_Connection_Ptr connection,
1220 protocol_request_ptr prot,
1223 /*----------------------------------------------------------------------+*/
1225 if(retval==SPC_ERROR)
1226 retval= (-XeSPCErrorNumber);
1228 SPC_Write_Reply(connection, prot, retval, errval);
1231 /*----------------------------------------------------------------------+*/
1233 * This function is invoked when the exit timer expires.
1234 * If sub-processes are running, return to select;
1237 #ifdef SA_HANDLER_INT_ARG
1238 void SPCD_Alarm_Handler(int not_used)
1240 void SPCD_Alarm_Handler()
1241 #endif /* SA_HANDLER_INT_ARG */
1242 /*----------------------------------------------------------------------+*/
1246 if (exit_timeout == SPCD_NO_TIMER)
1249 if (SPC_pid_list != NULL)
1250 for (i=0; SPC_pid_list[i] != NULL; i++) {
1251 if (SPC_pid_list[i] != SPCD_DEAD_PROCESS) {
1253 * Have at least one sub- process running so reset the
1254 * alarm and return to select.
1256 (void) alarm (exit_timeout * 60);
1262 * There are no sub-processes running. Exit if a request is not pending
1264 if (request_pending != SPCD_REQUEST_PENDING) {
1265 SPC_Format_Log((XeString)
1266 "Exit timer expired after '%d' minutes of no activity.",