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
23 /* @(#)$TOG: remote.c /main/9 1998/04/06 13:36:26 mgreess $ */
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include <sys/socket.h>
33 #include <sys/errno.h>
37 #include <X11/Xproto.h> /* for X_ChangeHosts */
38 #include <X11/Xatom.h> /* for XA_STRING */
40 #if !defined(linux) && !defined(__FreeBSD__)
41 extern char *sys_errlist[];
44 static char *conf_msg_id = STR_CONFDATA;
46 #define CONF_MSG_ID_LEN strlen(conf_msg_id)
47 #define CONF_MSG_DATASIZE_LEN 8
48 #define CONF_MSG_HEADER_LEN (CONF_MSG_ID_LEN + CONF_MSG_DATASIZE_LEN)
49 #define CONF_MSG_SIZE_MAX (8*1024 - 64) /* < PIPE_BUF (PIPSIZ) */
51 static int mk_remote_conf(/* list, locale, ims_name, status, confbuf, conflen */);
52 static char *mk_ims_ent(/* bp, idx, ent */);
53 static int parse_ims_list(/* ptr, list */);
54 static int parse_remote_conf(/* listp, locale, confbuf, conflen */);
55 static int prepare_action(/* act_typ, av, ac */);
56 static int read_property(/* prop, type, format, remove, datapp, lenp */);
61 _DTIMSSTART_MAIN _DTIMSSTART_STATUS _DTIMSSTART_DATA
64 _DTIMSSTART_STATUS (format: 32) _DTIMSSTART_DATA (format: 8)
67 message_type: _DTIMSSTART_STATUS
69 data.l[0]: _REMOTE_CONF or _REMOTE_RUN
71 data.l[2-4]: 0 (unused)
74 _NONE _INIT _REMOTE_CONF _REMOTE_RUN
77 DtImsGetRemoteConf DtImsRunRemoteIms
83 * set _STATUS property to _INIT
84 * set _DATA property to _INIT
86 <get remote configuratuon (DtImsGetRemoteConf)>
87 * clear _DATA property
88 * change _STATUS property to _REMOTE_CONF
89 * set additional command line options to _DATA property
90 * invoke DtImsGetRemoteConf action
92 [on remote dtimsstart]
94 - check _STATUS property whether its value is _REMOTE_CONF
95 - set collected data to _DATA property of owner of _MAIN (Replace)
96 - send ClientMessage of ErrorCode to owner of _MAIN
99 * receive ClientMessage from remote dtimsstart (or action finished)
100 * change _STATUS property to _INIT
101 * read data from _DATA property (Delete)
102 * parse ErrCode and data (conf data or ErrorPath)
103 * update selection window
105 <run IMS on remote host (DtImsRunRemoteIms)>
106 * change _STATUS property to _REMOTE_RUN
107 * set additional command line options to _DATA property
108 * invoke DtImsRunRemoteIms action
109 * popdown selection window (if sucessfully invoked)
111 [on remote dtimsstart]
112 - check _STATUS property whether its value is _REMOTE_RUN
113 - invoke IMS and wait its preparation
114 - set ErrorPath to _DATA property of owner of _MAIN (Replace)
115 - send ClientMessage of ErrorCode to owner of _MAIN
118 * receive ClientMessage from remote dtimsstart (or action finished)
119 * change _STATUS property to _INIT
120 * parse ErrCode and data (ErrorPath)
123 * disown _MAIN property
129 #ifdef NEED_STRCASECMP
131 * In case strcasecmp is not provided by the system here is one
132 * which does the trick.
135 strcasecmp(register const char *s1,
136 register const char *s2)
141 c1 = isupper(*s1) ? tolower(*s1) : *s1;
142 c2 = isupper(*s2) ? tolower(*s2) : *s2;
148 return (int) (*s1 - *s2);
152 int put_remote_conf(locale, ims_name)
153 char *locale, *ims_name;
156 int msg_status = NoError;
158 char confbuf[CONF_MSG_SIZE_MAX];
159 ImsList *list = (ImsList *) 0;
161 DPR(("put_remote_conf(locale=%s, ims=%s)\n", locale, ims_name));
163 ret = get_ims_list(&list, NULL, True);
166 ret = mk_remote_conf(list, locale, ims_name, msg_status, confbuf, &conflen);
172 ret = set_remote_confdata(confbuf, conflen);
174 send_dtims_msg(WIN_ST_REMOTE_CONF, ret);
177 NotifyErrCode(NoError);
178 (void) fwrite((void *)confbuf, (size_t)conflen, (size_t)1, stdout);
185 int get_remote_conf(listp, hostname, locale, ims_name)
187 char *hostname, *locale, *ims_name;
194 char *CDE_locale = NULL;
197 locale = userEnv.real_locale ? userEnv.real_locale : userEnv.locale;
199 CDE_locale = userEnv.CDE_locale;
202 DPR2(("get_remote_conf(%s, %s, %s)\n",
203 hostname, CDE_locale, ims_name ? ims_name : "<all>"));
205 DPR2(("get_remote_conf(%s, %s, %s)\n",
206 hostname, locale, ims_name ? ims_name : "<all>"));
209 /* Try to first use the CDE locale, else fallback to the locale. */
211 opts[n++] = "-CDE_locale";
212 opts[n++] = CDE_locale;
216 opts[n++] = "-locale";
223 opts[n++] = ims_name;
228 for (i = 0; i < DebugLvl; i++) opts[n++] = "-debug";
234 ret = prepare_action(ACT_GETREMCONF, opts, num_opts);
235 if (ret != NoError) return ret;
237 ret = invoke_action(Conf.action[ACT_GETREMCONF], hostname);
238 change_window_status(WIN_ST_INIT);
240 if (ret != NoError) return ret;
242 ret = read_remote_confdata(&confbuf, &conflen);
243 if (ret != NoError) return ret;
245 if (ret == NoError) {
246 ret = parse_remote_conf(listp, locale, confbuf, conflen);
248 if (ims_name && ret == ErrRemoteNoIms)
249 ret = ErrRemoteMissIms;
256 #define PUT_DATA(nm, val) *bp++ = ' ', bp = strcpyx(bp, (nm)), \
257 *bp++ = '=', bp = strcpyx(bp, (val)), *bp++ = '\n'
259 static int mk_remote_conf(list, locale, ims_name, status, confbuf, conflen)
262 char *locale, *ims_name, *confbuf;
274 DPR(("mk_remote_conf(locale=%s, ims=%s)\n", locale, ims_name));
277 if (DebugLvl >= 2 && list) pr_ImsList(list);
280 bp = confbuf + CONF_MSG_HEADER_LEN;
283 if (status == NoError) {
284 for (i = 0; i < list->num_ent; i++) {
285 ent = list->elist[i];
286 if ((ims_name && strcmp(ent->name, ims_name))
287 || (ent->ims && (ent->ims->flags & F_NO_REMOTE)))
288 ent->status = ErrRemoteIms;
293 status = ErrRemoteNoIms;
296 bp = strcpyx(bp, "ImsList: "); bp = strcpyx(bp, locale); *bp++ = '\n';
297 sprintf(var, "%ld", (long) status);
301 sprintf(var, "%ld", (long) num_ent);
303 if (list->elist[list->default_idx]->status != ErrRemoteIms) {
304 PUT_DATA("df", list->elist[list->default_idx]->name);
306 sprintf(var, "%ld", (long) (list->def_selmode));
309 for (i = j = 0; i < list->num_ent; i++)
310 if (list->elist[i]->status != ErrRemoteIms)
311 bp = mk_ims_ent(bp, j++, list->elist[i]);
313 bp = strcpyx(bp, "END"); *bp++ = '\n';
315 data_sz = bp - (confbuf + CONF_MSG_HEADER_LEN);
317 /* header (conf_msg_id & data_sz) */
319 memset((void *) bp, (int) ' ', (size_t) CONF_MSG_HEADER_LEN);
320 memcpy((void *) bp, conf_msg_id, CONF_MSG_ID_LEN);
321 sprintf(sz_ptr, "%ld", (long) data_sz);
322 len = strlen(sz_ptr);
323 bp = confbuf + CONF_MSG_HEADER_LEN - 1 - len;
324 memcpy((void *) bp, (void *) sz_ptr, (size_t) len);
325 confbuf[CONF_MSG_HEADER_LEN - 1] = '\n';
327 *conflen = CONF_MSG_HEADER_LEN + data_sz;
329 DPR2(("mk_remote_conf(): conflen=%d data_sz=%d\n confbuf=%s",
330 *conflen, data_sz, confbuf));
335 static char *mk_ims_ent(bp, idx, ent)
340 ImsConf *ims = ent->ims;
343 sprintf(val, "%ld", (long) idx);
344 bp = strcpyx(bp, "Ent-"); bp = strcpyx(bp, val); *bp++ = '\n';
345 PUT_DATA("nm", ent->name);
346 sprintf(val, "%ld", (long) ent->status);
348 sprintf(val, "%ld", (long) ims->flags);
350 sprintf(val, "%ld", (long) ims->protocols);
352 if (ent->label) { PUT_DATA("lb", ent->label); }
355 sprintf(val, "%ld", (long) ims->timeout);
360 sprintf(val, "%ld", (long) ims->interval);
363 if (ims->servername) { PUT_DATA("sn", ims->servername); }
364 if (ims->servername2) { PUT_DATA("sN", ims->servername2); }
365 if (ims->classname) { PUT_DATA("cn", ims->classname); }
366 if (ims->property) { PUT_DATA("pp", ims->property); }
367 if (ims->cmd_path) { PUT_DATA("cp", ims->cmd_path); }
368 if (ims->cmd_param) { PUT_DATA("cr", ims->cmd_param); }
369 if (ims->env_set) { PUT_DATA("es", ims->env_set); }
370 if (ims->env_unset) { PUT_DATA("eu", ims->env_unset); }
371 if (ims->env_pass) { PUT_DATA("ep", ims->env_pass); }
378 static int parse_ims_list(ptr, list)
382 register char *bp = ptr;
390 list->default_idx = -1;
391 list->def_selmode = SEL_MODE_NOAUTO;
396 while (np = strchr(bp, '\n')) {
402 if (strncmp(bp, "Ent-", 4) == 0) {
404 if (list->num_ent <= 0) {
405 DPR(("parse_ims_list(): ImsEnt: list->num_ent=%d\n", list->num_ent));
409 list->elist = ALLOC(list->num_ent, ImsEnt *);
410 if (num_ent >= list->num_ent) {
411 DPR(("parse_ims_list(): too many entry: '%s'\n", bp));
414 ent = list->elist[num_ent] = ALLOC(1, ImsEnt);
415 ims = ent->ims = ALLOC(1, ImsConf);
417 } else if (strncmp(bp, "END", 3) == 0) {
419 } else if (bp[0] == ' ' && bp[3] == '=') {
422 if (strncmp(bp, "ST", 2) == 0) list->status = atoi(vp);
423 else if (strncmp(bp, "ne", 2) == 0) list->num_ent = atoi(vp);
424 else if (strncmp(bp, "df", 2) == 0) def_name = vp;
425 else if (strncmp(bp, "sm", 2) == 0) list->def_selmode = atoi(vp);
428 DPR(("parse_ims_list(): ImsEnt: list->elist[%d]=%%x\n", num_ent, ent));
432 else if (strncmp(bp, "nm", 2) == 0) { RENEWSTR(ent->name, vp); }
433 else if (strncmp(bp, "lb", 2) == 0) { RENEWSTR(ent->label, vp); }
434 else if (strncmp(bp, "st", 2) == 0) { ent->status = atoi(vp); }
435 else if (strncmp(bp, "fg", 2) == 0) { ims->flags = atoi(vp); }
436 else if (strncmp(bp, "pr", 2) == 0) { ims->protocols = atoi(vp); }
437 else if (strncmp(bp, "to", 2) == 0) { ims->timeout = atoi(vp); }
438 else if (strncmp(bp, "it", 2) == 0) { ims->interval = atoi(vp); }
439 else if (strncmp(bp, "sn", 2) == 0) { RENEWSTR(ims->servername, vp); }
440 else if (strncmp(bp, "sN", 2) == 0) { RENEWSTR(ims->servername2, vp); }
441 else if (strncmp(bp, "cn", 2) == 0) { RENEWSTR(ims->classname, vp); }
442 else if (strncmp(bp, "pp", 2) == 0) { RENEWSTR(ims->property, vp); }
443 else if (strncmp(bp, "cp", 2) == 0) { RENEWSTR(ims->cmd_path, vp); }
444 else if (strncmp(bp, "cr", 2) == 0) { RENEWSTR(ims->cmd_param, vp); }
445 else if (strncmp(bp, "es", 2) == 0) { RENEWSTR(ims->env_set, vp); }
446 else if (strncmp(bp, "eu", 2) == 0) { RENEWSTR(ims->env_unset, vp); }
447 else if (strncmp(bp, "ep", 2) == 0) { RENEWSTR(ims->env_pass, vp); }
449 DPR(("parse_ims_list(): invalid line '%s'\n", bp - 1));
452 DPR(("parse_ims_list(): invalid line '%s'\n", bp));
458 if (num_ent != list->num_ent) {
459 DPR(("parse_ims_list(): num_ent(%d) != list->num_ent(%d)\n",
460 num_ent, list->num_ent));
464 list->num_ent = num_ent;
465 list->default_idx = -1;
467 for (i = 0; i < num_ent; i++) { /* check indispensable entry */
468 ent = list->elist[i];
469 if (ent->status == NoError)
470 ent->status = check_ims_conf(ent->ims, ent->name);
473 if (def_name) { /* set default_idx */
474 for (i = 0; i < num_ent; i++)
475 if (strcmp(list->elist[i]->name, def_name) == 0) {
476 list->default_idx = i;
486 static int parse_remote_conf(listp, locale, confbuf, conflen)
488 char *locale, *confbuf;
496 DPR(("parse_remote_conf(%s)\n", locale));
498 if (conflen < (int) CONF_MSG_HEADER_LEN /* check header */
499 || strncmp(confbuf, conf_msg_id, CONF_MSG_ID_LEN)) {
502 confbuf[CONF_MSG_HEADER_LEN - 1] = 0; /* <= '\n' */
503 bp = confbuf + CONF_MSG_ID_LEN;
504 while (*bp == ' ') bp++;
505 if (!str_to_int(bp, &data_sz) || data_sz < 0) {
507 } else if (conflen < data_sz + (int) CONF_MSG_HEADER_LEN) {
508 DPR(("\tconflen(%d) != data_sz(%d) + HDR\n", conflen, data_sz));
509 data_sz = conflen - CONF_MSG_HEADER_LEN;
515 if (ret != NoError && conflen > (int) CONF_MSG_HEADER_LEN) {
516 if (!confbuf[CONF_MSG_HEADER_LEN - 1])
517 confbuf[CONF_MSG_HEADER_LEN - 1] = '@';
518 confbuf[conflen] = 0;
519 DPR(("\tinvalid header[len=%d]: %s\n", conflen, confbuf));
522 if (ret != NoError) return ErrRemoteAction;
524 bp = confbuf + CONF_MSG_HEADER_LEN;
526 if (strncmp(bp, "ImsList:", 8)
527 /* || strncmp(bp + 9, locale, strlen(locale)) */
528 || !(bp = strchr(bp, '\n'))) {
529 return ErrRemoteAction;
531 /* confbuf[conflen] = 0; */
533 list = ALLOC(1, ImsList);
534 ret = parse_ims_list(bp, list);
536 if (ret != NoError || list->num_ent == 0) {
539 list = (ImsList *) 0;
540 ret = ErrRemoteNoIms;
548 int exec_remote_ims(sel)
552 int n, num_opts, binc;
559 char *ims_name = sel->name;
560 ImsConf *ims = sel->ent->ims;
563 DPR(("exec_remote_ims(): '%s' on %s\n", ims_name, sel->hostname));
567 bp = tmpbuf; tmpbuf[0] = 0;
569 opts[n++] = "-ims"; opts[n++] = ims_name;
570 opts[n++] = "-notify";
571 opts[n++] = "-nosave";
572 opts[n++] = "-nowindow";
575 binc = expand_string(bp, "%L", BUFSIZ, 0);
576 opts[n++] = "-locale";
577 opts[n++] = bp; bp += binc + 1;
578 binc = expand_string(bp, "%d.%s", BUFSIZ, 0);
579 opts[n++] = "-display";
580 opts[n++] = bp; bp += binc = 1;
586 for (i = 0; i < DebugLvl; i++) opts[n++] = "-debug";
591 if (OpFlag & FLAG_NOWAIT) opts[n++] = "-nowait";
592 if (OpFlag & FLAG_NOTIMEOUT) opts[n++] = "-notimeout";
593 if (OpFlag & FLAG_CONNECT) opts[n++] = "-connect";
594 if (Opt.Timeout > 0) {
595 sprintf(val, "%ld", (long)Opt.Timeout);
596 np = strcpyx(bp, val);
597 opts[n++] = "-timeout";
598 opts[n++] = bp; bp = np + 1;
600 if (Opt.Interval > 0) {
601 sprintf(val, "%ld", (long)Opt.Interval);
602 np = strcpyx(bp, val);
603 opts[n++] = "-interval";
604 opts[n++] = bp; bp = np + 1;
606 if (mk_ims_option(bp, sel)) {
607 sprintf(val, "%ld", (long)Opt.Interval);
608 np = strcpyx(bp, val);
609 opts[n++] = "-imsopt";
610 opts[n++] = bp; bp = np + 1;
617 set_remote_env(envbuf, ims->env_pass);
619 ret = prepare_action(ACT_RUNREMIMS, opts, num_opts);
620 if (ret != NoError) return ret;
622 ret = invoke_action(Conf.action[ACT_RUNREMIMS], sel->hostname);
623 change_window_status(WIN_ST_INIT);
625 if (ret != NoError) return ret;
627 if (ret == NoError) {
629 ret = get_window_data(&ac, &av);
632 if (ret != NoError) return ret;
635 if (ret == NoError) {
636 put_xims_log("'%s' started for %s on %s.",
637 sel->name, userEnv.displayname, sel->hostname);
640 DPR2(("exec_remote_ims(): ret=%s[%d]\n", error_name(ret), ret));
646 int check_hostname(hostname)
649 int host_type = HOST_UNKNOWN;
650 char *local = userEnv.hostname;
652 unsigned long addr = 0L;
653 static unsigned long local_addr = 0L;
655 if (!hostname || !*hostname || strcasecmp(hostname, "local") == 0
656 || strcasecmp(hostname, userEnv.hostname) == 0) {
657 host_type = HOST_LOCAL;
658 } else { /* compare inet address */
660 if ((hp = gethostbyname(local)) && hp->h_addrtype == AF_INET) {
661 local_addr = *((unsigned long *) hp->h_addr_list[0]);
663 DPR(("check_hostname(%s)\tgethostbyname() failed\n", local));
664 host_type = HOST_REMOTE;
667 if (host_type == HOST_UNKNOWN) {
668 if ((hp = gethostbyname(hostname)) && hp->h_addrtype == AF_INET) {
669 addr = *((unsigned long *) hp->h_addr_list[0]);
670 if (addr == local_addr)
671 host_type = HOST_LOCAL;
673 host_type = HOST_REMOTE;
675 DPR(("check_hostname(%s)\tunknown\n", hostname));
676 host_type = HOST_UNKNOWN;
681 DPR(("check_hostname(%s): [%s] addr=%#x, local=%#x\n", hostname,
682 host_type == HOST_LOCAL ? "LOCAL" :
683 (host_type == HOST_REMOTE ? "REMOTE" : "UNKNOWN"),
691 int set_remote_confdata(confbuf, conflen)
697 av[0] = confbuf; av[1] = NULL;
699 return set_window_data(1, av);
702 int read_remote_confdata(confbuf, conflen)
710 ret = get_window_data(&ac, &av);
711 /* if (ac != 1) { FREE av[i]; return ErrBabData; } */
714 *conflen = strlen(av[0]);
720 static int prepare_action(act_typ, av, ac)
727 ret = init_window_env();
728 if (ret != NoError) return ret;
732 change_window_status(WIN_ST_REMOTE_CONF);
736 change_window_status(WIN_ST_REMOTE_RUN);
739 default: return ErrInternal;
742 ret = set_window_data(ac, av);
748 int get_window_status()
754 if (winEnv.atom_status == None || winEnv.atom_owner == None)
757 if (winEnv.atom_owner == None) return WIN_ST_NONE;
759 if (winEnv.atom_owner == XtWindow(winEnv.TopW))
760 return winEnv.status;
763 win_st = WIN_ST_NONE;
764 if (read_property(winEnv.atom_status, XA_INTEGER, 32, False,
765 (char **)&datap, &len) == True && len > 0) {
772 int change_window_status(status)
775 if (winEnv.atom_status == None || winEnv.atom_owner == None)
778 winEnv.status = status;
780 (void)XChangeProperty(winEnv.Dpy, winEnv.atom_owner,
781 winEnv.atom_status, XA_INTEGER,
782 32, PropModeReplace, (unsigned char *)&status, 1);
784 XSync(winEnv.Dpy, False);
786 DPR(("change_window_status(): new status=%d\n", status));
790 int set_window_data(ac, av)
796 register char *buf, *bp;
798 if (winEnv.atom_data == None || winEnv.atom_owner == None)
804 printf("set_window_data() av[%d] = { ", ac);
805 for (i = 0; i < ac; i++)
806 printf("\"%s\", ", av[i]);
811 for (i = 0, nbytes = 1; i < ac; i++)
812 nbytes += strlen(av[i]) + 1;
813 if (bp = buf = XtMalloc(nbytes)) { /* copy args into single buffer */
814 for (i = 0; i < ac; i++) {
816 (void) strcpy(bp, av[i]);
817 bp += strlen(av[i]) + 1;
821 (void)XChangeProperty(winEnv.Dpy, winEnv.atom_owner,
822 winEnv.atom_data, XA_STRING, 8,
823 PropModeReplace, (unsigned char *)buf, nbytes);
824 XSync(winEnv.Dpy, False);
827 DPR(("set_window_data(): len=%d data=\"%s\"\n", nbytes, buf));
833 int get_window_data(acp, avp)
843 if (winEnv.atom_data == None || winEnv.atom_owner == None)
846 if (read_property(winEnv.atom_data, XA_STRING, 8, True, &data, &len) != True) {
849 return ErrRemoteData;
854 for (i = 1; i < len - 1; i++) if (data[i] == '\0') ac++;
855 av = (char **) ALLOC(ac + 1, char *);
861 av[j++] = NEWSTR(data);
862 for (i = 1; i < len - 1; i++)
863 if (data[i] == '\0') {
864 av[j++] = NEWSTR(data + i + 1);
874 printf("get_window_data() av[%d] = { ", ac);
875 for (i = 0; i < ac; i++)
876 printf("\"%s\", ", av[i]);
888 static int read_property(prop, type, format, del_flag, datapp, lenp)
893 unsigned char **datapp;
898 unsigned long bytesafter;
902 (void)XGetWindowProperty(winEnv.Dpy, winEnv.atom_owner,
903 prop, 0L, 1000000L, del_flag, type,
904 &realtype, &realformat, lenp,
905 &bytesafter, datapp);
907 if (realtype == None) {
909 } else if (realtype != type) { /* wrong type */
911 } else if (realformat != format) { /* wrong format */
912 if (*datapp != NULL) XtFree((char *)*datapp);