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: start.c /main/9 1998/04/06 13:36:49 mgreess $ */
31 #if (defined(USL) || defined(__uxp__) || defined(linux) || defined(CSRG_BASED)) && !defined(_NFILE)
32 #define _NFILE FOPEN_MAX
35 #if !defined(linux) && !defined(__FreeBSD__)
36 extern char *sys_errlist[];
41 static int check_ims_opt(/* ptr */);
42 static char *find_session_resfile(/* res_type */);
43 static int check_selection(/* sel */);
44 static int build_run_env(/* sel */);
45 static int run_ims(/* sel */);
46 static int invoke_ims(/* sel */);
47 static void on_sig_chld(/* sig */);
48 static bool is_ims_running(/* renv, ims */);
49 static int settle_ims(/* sel */);
50 static Window property_owner(/* prop_atom, prop_str */);
52 static void catch_alarm(/* sig */);
53 static int try_connection(/* sel */);
54 static int create_dummy_ic(/* xim */);
55 # endif /* old_hpux */
61 UserSelection *sel = &userSel;
62 OpStateVal oldOpState = OpState;
64 DPR(("ximsStart(): OpState=%s OpErrCode=%s[%d]\n",
65 StateName(), error_name(OpErrCode), OpErrCode));
67 OpState = State_Start;
70 if (DebugLvl > 1) pr_UserSelection(sel);
74 if (oldOpState == State_Select_Err) {
75 /* don't change OpErrCode */
76 OpState = State_Start_Err;
79 if (oldOpState == State_Select_Canceled) {
80 clear_UserSelection(sel);
82 } else { /* save selection */
83 if (!(OpFlag & FLAG_NOSAVE) && (sel->flag & F_SELECT_CHANGED)) {
84 if (save_user_selection(sel, NULL) != NoError) {
85 DPR(("save_user_selection(): failed\n"));
86 put_xims_warnmsg(ErrSaveSelection, 0, 0, 0);
87 /* ret = ErrSaveSelection; */
94 OpState = State_Start_Err;
98 if ((ret = check_selection(sel)) != NoError) {
99 if (ret == ErrIsNone || ret == ErrNotRun) {
100 build_run_env(sel); /* for make_new_environ() */
103 OpState = State_Start_Done;
109 if (useWINDOW()) /* initilaize Xt */
115 OpState = ret == NoError ? State_Start_Done : State_Start_Err;
123 OpStateVal oldOpState = OpState;
124 UserSelection *sel = &userSel;
125 struct timeval interval;
129 DPR(("ximsWait(): OpState=%s OpErrCode=%s[%d]\n",
130 StateName(), error_name(OpErrCode), OpErrCode));
132 OpState = State_Wait;
134 if (oldOpState == State_Start_Err) {
135 /* don't change OpErrCode */
136 OpState = State_Wait_Err;
140 if (!is_waiting() || (OpFlag & FLAG_NOWAIT)) {
144 if (im_mod_available(sel->renv) != 1) {
147 xt_start_waiting(); /* never returns unless failed */
152 interval.tv_sec = Opt.Interval / 1000;
153 interval.tv_usec = (Opt.Interval % 1000) * 1000;
154 start_tm = time((time_t) 0);
156 while (is_waiting()) {
157 select(0, 0, 0, 0, &interval); /* usleep */
158 lapse = (int) time((time_t) 0) - start_tm;
160 if (im_mod_available(sel->renv) != 0 || lapse >= Opt.Timeout) {
161 DPR(("ximsWait(tmout=%d): wait done (%d sec.)\n",
162 Opt.Timeout, lapse));
175 UserSelection *sel = &userSel;
177 DPR(("ximsWaitDone():\tOpState=%s OpErrCode=%s[%d]\n",
178 StateName(), error_name(OpErrCode), OpErrCode));
182 ret = sel->renv->status;
185 sel->status = ErrImsTimeout;
186 put_xims_log("'%s' timed-out.", sel->name, 0, 0);
190 sel->status = NoError;
192 if ((OpFlag & FLAG_CONNECT)
193 || (sel->ent->ims->flags & F_TRY_CONNECT)) {
194 sel->status = try_connection(sel);
196 # endif /* old_hpux */
199 case ErrImsConnecting:
200 case ErrImsConnectDone:
201 sel->status = NoError;
205 /* put_xims_log("'%s' aborted.", sel->name, 0, 0); */
206 case ErrImsExecution:
212 if (sel->status != NoError) {
213 OpErrCode = sel->status;
214 DPR(("ximsWaitDone(): OpErrCode=%s[%d]\n",
215 error_name(OpErrCode), OpErrCode));
220 settle_ims(sel); /* clear WM_COMMAND property */
222 OpState = OpErrCode == NoError ? State_Wait_Done : State_Wait_Err;
229 return userSel.renv && userSel.renv->status == ErrImsWaiting;
232 void set_sig_chld(enable)
235 DPR(("set_sig_chld(%s)\n", enable ? "Enabled" : "Disabled"));
236 signal(SIGCHLD, enable ? on_sig_chld : SIG_IGN);
239 int im_mod_available(renv)
245 if (!(renv = userSel.renv))
248 if (renv->status != ErrImsWaiting)
252 if (DebugLvl >= 1) putc('.', LogFp), fflush(LogFp);
255 owner = property_owner(&renv->prop_atom, renv->atom_name);
259 #ifdef ReconfirmProperty
261 int wait_period = 2; /* 2 sec. */
263 DPR(("im_mod_available(): [RECONFIRM] sleep(%d)\n", wait_period));
265 if (property_owner(&renv->prop_atom, renv->atom_name) == None) {
266 DPR(("\t[RECONFIRM] owner lost\n"));
270 #endif /* ReconfirmProperty */
273 if (DebugLvl >= 1) putc('\n', LogFp), fflush(LogFp);
275 DPR2(("check_im_mod(): wait done\n"));
276 renv->status = ErrImsWaitDone;
284 /* ***** IMS options ***** */
286 int mk_ims_option(ptr, sel)
291 FileSel *fsel = sel->fsel;
292 ImsConf *ims = sel->ent->ims;
295 if (ims->flags & F_NO_OPTION) /* not applicable */
299 if (fsel->com_opt /* common option */
300 && check_ims_opt(fsel->com_opt)) {
301 /* bp = strcpyx(bp, fsel->com_opt); */
302 bp += expand_string(fsel->com_opt, bp, BUFSIZ, ims);
305 if (fsel->opts) { /* individual option */
307 for (op = fsel->opts; *op; op++) {
308 if (strcmp((*op)->ims_name, sel->name) == 0
309 && check_ims_opt((*op)->opt_str)) {
310 /* bp = strcpyx(bp, (*op)->opt_str); */
311 bp += expand_string((*op)->opt_str, bp, BUFSIZ, ims);
317 if (Opt.ImsOption && check_ims_opt(Opt.ImsOption)) {
318 /* bp = strcpyx(bp, Opt.ImsOption); */
319 bp += expand_string(Opt.ImsOption, bp, BUFSIZ, ims);
321 if (sel->iconic > 0 && strstr(ptr, STR_ICONIC_OPT) == NULL) {
322 bp = strcpyx(bp, STR_ICONIC_OPT);
326 return (int) (bp - ptr);
329 static int check_ims_opt(ptr)
332 /* option string must not contain any of shell's metacaharacters */
333 if (strpbrk(ptr, "`'\"#;&()|<>\n")) {
334 put_xims_log("ims option ignored: %s", ptr, 0, 0);
335 DPR(("\tshell's meta-char in option \"%s\" -- ignored\n", ptr));
342 /* ******** resource ******** */
344 #define RES_TYPE_DT 0
345 #define RES_TYPE_VUE 1
347 static bool resource_loaded = False;
353 char *sess_res, *res_file;
355 if (resource_loaded) {
356 DPR2(("load_resources: already done -- not loaded\n"));
361 if (!empty) { /* load if RESOURCE_MANAGER is empty */
362 DPR2(("load_resources: RESOURCE_MANGER is not empty -- not loaded\n"));
366 sess_res = res_file = NULL;
367 if (!(OpFlag & FLAG_NORESOURCE)) {
369 sess_res = find_session_resfile(RES_TYPE_DT);
373 sess_res = find_session_resfile(RES_TYPE_VUE);
375 # endif /* old_hpux */
376 if (sess_res && !is_readable(sess_res, False)) {
382 if (Opt.ResourceFile && is_readable(Opt.ResourceFile, False))
383 res_file = Opt.ResourceFile;
385 DPR(("load_resources():\tsess='%s' res='%s'\n", sess_res, res_file));
387 if (!isDT() && !sess_res && !res_file) return False;
388 if ((ret = open_display()) != NoError) return False;
390 ret = merge_RM(sess_res, res_file);
391 resource_loaded = True;
393 if (sess_res) FREE(sess_res);
398 int restore_resources()
400 if (!resource_loaded) {
401 DPR2(("restore_resources: not loaded yet -- not restored\n"));
404 resource_loaded = False;
408 static char *find_session_resfile(res_type)
411 char path[MAXPATHLEN];
412 char **ls = (char **) 0, **pp;
416 if (res_type == RES_TYPE_DT) { /* DT */
417 res = Conf.dt ? (Conf.dt)->resPath : NULL;
420 else if (res_type == RES_TYPE_VUE && Conf.vue) { /* VUE */
421 VueEnv *vue = Conf.vue;
424 expand_string(vue->uselite, path, MAXPATHLEN, (ImsConf *)0);
425 if (access(path, R_OK) == 0)
429 # endif /* old_hpux */
431 if (!res) return NULL;
432 if (ls = parse_strlist(res, ':')) {
433 for (pp = ls; *pp; pp++) {
434 expand_string(*pp, path, MAXPATHLEN, (ImsConf *)0);
435 if (access(path, R_OK) == 0) {
442 /* DPR2(("find_session_resfile(): '%s'\n", path)); */
450 /* ***** local functions ***** */
452 static int check_selection(sel)
457 if (!sel->name || !sel->list)
458 ret = ErrNoSelection;
459 else if (sel->ims_idx < 0 || sel->ims_idx >= sel->list->num_ent)
460 ret = ErrNoSelection;
461 else if (sel->status != NoError)
463 else if (strcmp(sel->name, NAME_NONE) == 0)
464 ret = sel->status = ErrIsNone;
465 else if (sel->ent->ims->flags & F_NO_SERVER)
466 ret = sel->status = ErrNotRun;
467 else if (OpFlag & FLAG_NOSTART)
468 ret = sel->status = ErrNotRun;
473 static int build_run_env(sel)
482 char envbuf[BUFSIZ], optbuf[BUFSIZ];
484 ImsConf *ims = sel->ent->ims;
485 char *cmd_param = ims->cmd_param ? ims->cmd_param : "";
487 renv = ALLOC(1, RunEnv);
489 renv->is_remote = sel->host_type == HOST_REMOTE ? True : False;
491 /* proto, im_mod & atom */
492 proto = renv->proto = default_protocol(ims);
493 if (p = Conf.xmod[proto]) {
495 expand_string(p, buf, BUFSIZ, ims);
496 renv->im_mod = NEWSTR(buf);
498 if (p = Conf.atom[proto]) {
500 expand_string(p, buf, BUFSIZ, ims);
501 renv->atom_name = NEWSTR(buf);
502 } else { /* copy im_mod, instead */
503 renv->atom_name = NEWSTR(renv->im_mod);
505 if ((p = renv->atom_name) && strchr(p, '#')) {
506 while (p = strchr(p, '#')) /* replace '#' with '@' */
509 # endif /* old_hpux */
513 renv->pid = (pid_t) 0;
514 renv->status = NoError;
515 renv->wait_status = 0;
519 if (sel->status != NoError) /* ErrIsNone or ErrNotRun */
526 optbuf[0] = envbuf[0] = 0;
527 log_path = Opt.LogPath;
531 bp = strcpyx(bp, "XMODIFIERS='");
532 bp = strcpyx(bp, ENV_MOD_IM);
533 bp = strcpyx(bp, renv->im_mod);
534 bp = strcpyx(bp, "'");
536 /* Local, LANG & DISPLAY have been already set to *environ by putenv() */
539 mk_ims_option(optbuf, sel);
541 /* len = sysconf(_SC_ARG_MAX) / 2; len = Max(len, BUFSIZ); */
542 len = strlen(envbuf) + strlen(ims->cmd_path)
543 + strlen(cmd_param) + strlen(optbuf) + strlen(log_path) + 40;
545 /* for local execution */
546 renv->cmdbuf = ALLOC(len, char);
547 sprintf(renv->cmdbuf, " %s; export XMODIFIERS; exec %s %s %s >> %s 2>&1 ",
548 envbuf, ims->cmd_path, cmd_param, optbuf, log_path);
550 /* timeout & interval */
551 if (Opt.Timeout > 0) tmout = Opt.Timeout;
552 else if (ims->timeout > 0) tmout = ims->timeout;
553 else tmout = DEFAULT_TIMEOUT;
554 if (Opt.Interval > 0) intv = Opt.Interval;
555 else if (ims->interval > 0) intv = ims->interval;
556 else intv = DEFAULT_INTERVAL;
557 tmout = Max(tmout, MIN_TIMEOUT);
558 intv = Max(intv, MIN_INTERVAL);
559 if (intv/1000 > tmout) intv = tmout * 1000;
560 /* else if (intv/10 < tmout) intv = tmout * 10; */
565 if (DebugLvl >= 1) pr_RunEnv(sel->renv);
566 DPR(("build_run_env(): Timeout=%d (sec) Interval=%d (msec)\n",
573 static int run_ims(sel)
578 if ((ret = open_display()) != NoError)
581 if (is_ims_running(sel->renv, sel->ent->ims)) {
582 sel->status = ErrImsRunning;
583 DPR(("run_ims(): '%s' is already running\n", sel->name));
589 if (sel->renv->is_remote) {
590 /* ret = set_host_acss(sel->hostname); */
592 ret = exec_remote_ims(sel);
596 ret = invoke_ims(sel);
601 static int invoke_ims(sel)
604 RunEnv *renv = sel->renv;
611 if (pid == (pid_t) -1) {
612 put_xims_log("fork failed [%s]",
613 (errno <= sys_nerr) ? sys_errlist[errno] : NULL, 0, 0);
617 return renv->status = ErrImsExecution;
619 if (pid == (pid_t) 0) { /* child */
620 for (i = 0; i < _NFILE; i++)
623 #if defined(__osf__) || defined(CSRG_BASED)
628 execl(SH_PATH, "sh", "-c", renv->cmdbuf, NULL);
630 put_xims_log("%s: exec failed [%s]", SH_PATH,
631 (errno <= sys_nerr) ? sys_errlist[errno] : NULL, 0, 0);
632 /* perror(SH_PATH); */
639 renv->wait_status = 0;
640 renv->status = ErrImsWaiting;
642 put_xims_log("'%s' started for %s", sel->name, userEnv.displayname, 0);
644 DPR(("invoke_ims(%s): pid=%d\n", sel->name, pid));
649 static void on_sig_chld(sig)
655 RunEnv *renv = userSel.renv;
659 #ifdef _XPG4_EXTENDED
660 pid = wait3(&wait_status, WNOHANG, (struct rusage *)NULL);
662 pid = waitpid((pid_t) -1, &wait_status, WNOHANG);
663 #endif /* _XPG4_EXTENDED */
664 } while (pid == -1 && errno == EINTR);
666 DPR(("\ton_sig_chld(): pid=%d errno=%d\n", pid, errno));
671 signal(SIGCHLD, on_sig_chld);
673 if (WIFEXITED(wait_status)) {
674 cause = ErrImsAborted;
675 } else if (WIFSIGNALED(wait_status)) {
676 cause = ErrImsAborted;
677 } else { /* WIFSTOPPED(wait_status) */
681 if (renv->pid == pid) {
682 if (renv->status == ErrImsWaiting || renv->status == ErrImsConnecting) {
683 renv->status = cause;
684 renv->wait_status = wait_status;
685 DPR(("on_sig_chld(): '%s' aborts (wait_status=%#x)\n",
686 userSel.name, wait_status));
687 put_xims_log("'%s' aborted.", userSel.name, 0, 0);
692 DPR(("\tsig_chld: renv->state=%s is not ErrImsWaiting\n",
693 error_name(renv->status)));
699 DPR(("\tsig_chld: pid=%d != renv->pid=%d\n", pid, renv->pid));
706 static bool is_ims_running(renv, ims)
714 if (prop_str = ims->property) {
717 if (!(prop_str = renv->atom_name))
719 atomp = &renv->prop_atom;
722 owner = property_owner(atomp, prop_str);
724 DPR2(("is_ims_running(): prop='%s'[%d] owned by %#x\n",
725 prop_str, atomp ? *atomp : -1, owner));
728 /* check primary server name for XIM */
729 if (owner == None && !ims->property && renv->proto == Proto_XIM
730 && ims->server_name2 && strstr(renv->atom_name, ims->server_name2) {
731 char buf[BUFSIZ], *p;
733 prop_str = strcpy(buf, renv->atom_name);
734 if (p = strchr(prop_str, '=')) {
735 strcpy(p + 1, ims->servername);
737 owner = property_owner(atomp, prop_str);
738 DPR2(("is_ims_running(): prop='%s'[%d] owned by %#x\n",
739 prop_str, atomp ? *atomp : -1, owner));
744 return owner == None ? False : True;
748 static int settle_ims(sel)
756 ImsConf *ims = sel->ent->ims;
759 if (prop_str = ims->property) {
761 owner = property_owner(atomp, prop_str);
764 else if (sel->renv) {
765 if (prop_str = sel->renv->atom_name)
766 owner = search_clear_cmd_property(prop_str);
771 clear_cmd_property(owner);
777 static Window property_owner(prop_atom, prop_str)
781 Atom property = prop_atom ? *prop_atom : None;
783 if (property == None) {
784 property = XInternAtom(Dpy, prop_str, True);
785 if (property == None)
788 *prop_atom = property;
790 return XGetSelectionOwner(Dpy, property);
795 /* ***** try_connection ***** */
799 static jmp_buf jmp_env;
800 static Window dmy_win = 0; /* temporary window used for XCreateIC() */
802 static void catch_alarm(sig)
805 signal(SIGALRM, SIG_IGN);
810 static int try_connection(sel)
813 RunEnv *renv = sel->renv;
814 ImsConf *ims = sel->ent->ims;
815 char envbuf[BUFSIZ], *bp;
817 int ic_ok, retry_cnt;
818 static char *saved_xmod = NULL;
823 DPR(("try_connection(%s):\n", sel->name));
825 if (sel->status != NoError || !renv->im_mod)
828 renv->status = ErrImsConnecting;
833 bp = strcpyx(envbuf, ENV_MOD_IM);
834 bp = strcpyx(bp, renv->im_mod);
835 saved_xmod = XSetLocaleModifiers(envbuf);
836 DPR(("\tXSetLocaleModifiers(%s)\n", envbuf));
839 if (setjmp(jmp_env) == 0) {
840 signal(SIGALRM, catch_alarm);
843 for (retry_cnt = 0; !ic_ok && retry_cnt <= MAX_RETRY; retry_cnt++) {
844 if (retry_cnt) sleep(retry_cnt * retry_cnt);
847 last_time = time((time_t)0);
849 xim = XOpenIM(Dpy, (XrmDatabase)0, ims->servername, ims->classname);
851 DPR(("try_connection(%d): XOpenIM() OK [%d sec.]",
852 retry_cnt, time((time_t)0) - last_time));
854 last_time = time((time_t)0);
856 ic_ok = create_dummy_ic(xim);
857 DPR(("\tXCreateIC() %s [%d sec.]\n",
858 ic_ok ? "OK" : "Failed", time((time_t)0) - last_time));
859 XCloseIM(xim); xim = 0;
861 DPR(("try_connection(%d): XOpenIM() failed.\n", retry_cnt));
865 } else { /* long_jmp() by alarm [timeout] */
866 alarm(0); signal(SIGALRM, SIG_IGN);
867 DPR(("try_connection(): XOpenIM() & XCreateIC() timed-out.\n"));
869 XDestroyWindow(Dpy, dmy_win); dmy_win = 0;
871 /* neither XDestroyIC() nor XCloseIM() should be called */
875 alarm(0); signal(SIGALRM, SIG_IGN);
877 /* restore XMODIFIERS */
879 DPR2(("\tXSetLocaleModifiers(save='%s')\n", saved_xmod));
880 XSetLocaleModifiers(saved_xmod);
884 renv->status = ErrImsConnectDone;
886 return ic_ok ? NoError : ErrImsTimeout; /* ErrImsConnect; */
889 static int create_dummy_ic(xim)
894 XIMStyles *im_styles;
897 unsigned long fg, bg;
899 XVaNestedList status_attr;
901 scr = DefaultScreen(Dpy);
902 fg = BlackPixel(Dpy, scr);
903 bg = WhitePixel(Dpy, scr);
904 dmy_win = XCreateSimpleWindow(Dpy, RootWindow(Dpy, scr),
905 0, 0, 1, 1, 0, bg, fg);
907 /* search (PreeditNothing | StatusNothing [or StatusArea]) style */
909 style = (XIMStyle) 0;
910 im_styles = (XIMStyles *) 0;
911 if (XGetIMValues(xim, XNQueryInputStyle, &im_styles, NULL)) {
912 DPR(("create_dummy_ic(): XGetIMValues(XNQueryInutStyle) failed.\n"));
915 if (!im_styles || !im_styles->count_styles) {
916 DPR(("create_dummy_ic(): No input styles supported on IMS.\n"));
917 if (im_styles) XFree(im_styles);
920 if ((int)im_styles->count_styles > 0) {
922 for (i = 0, alt = -1; i < (int)im_styles->count_styles; i++)
923 if (im_styles->supported_styles[i] & XIMPreeditNothing) {
924 if (im_styles->supported_styles[i] & XIMStatusNothing) {
925 style = im_styles->supported_styles[i];
927 } else if (im_styles->supported_styles[i] & XIMStatusArea) {
931 if (!style && alt >= 0) style = im_styles->supported_styles[alt];
935 DPR(("create_dummy_ic(): 'PreeditNothing' styles not supported.\n"));
937 /* style = XIMPreeditNothing | XIMStatusNothing; */
941 status_attr = (XVaNestedList) 0;
942 if (style & XIMStatusArea) {
943 area.x = area.y = 0; area.width = area.height = 1;
944 status_attr = XVaCreateNestedList(NULL,
952 ic = XCreateIC(xim, XNInputStyle, style,
953 XNClientWindow, dmy_win,
954 XNStatusAttributes, status_attr,
955 XNFocusWindow, dmy_win,
958 /* if (fset) XFreeFontSet(Dpy, fset); */
959 if (ic) XDestroyIC(ic);
962 if (dmy_win) XDestroyWindow(Dpy, dmy_win);
965 return ic ? True : False;
968 # endif /* old_hpux */