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 libraries and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 //%% (c) Copyright 1993, 1994 Hewlett-Packard Company
24 //%% (c) Copyright 1993, 1994 International Business Machines Corp.
25 //%% (c) Copyright 1993, 1994 Sun Microsystems, Inc.
26 //%% (c) Copyright 1993, 1994 Novell, Inc.
27 //%% $TOG: mp_session.C /main/13 1999/09/16 13:46:46 mgreess $
29 * @(#)mp_session.C 1.84 96/01/10
31 * Tool Talk Message Passer (MP) - mp_session.cc
33 * Copyright (c) 1990,1992 by Sun Microsystems, Inc.
35 * Implementation of the _Tt_session class.
37 #include "util/tt_global_env.h"
38 #include "util/tt_xdr_version.h"
39 #include "util/tt_host.h"
40 #include "util/tt_port.h"
41 #include "util/tt_gettext.h"
42 #include "mp/mp_global.h"
43 #include "mp/mp_message.h"
45 #include "mp/mp_c_global.h"
46 #include "mp/mp_c_session.h"
47 #include "mp/mp_c_mp.h"
48 #include "mp/mp_pattern.h"
49 #include "mp/mp_rpc_client.h"
50 #include "mp/mp_rpc_interface.h"
51 #include "mp/mp_session.h"
52 #include "mp/mp_desktop.h"
53 #include "mp/mp_xdr_functions.h"
56 #ifdef OPT_UNIX_SOCKET_RPC
57 # include <sys/socket.h>
59 #endif // OPT_UNIX_SOCKET_RPC
66 _is_server = 0; // default server mode
79 if (! _desktop.is_null()) {
80 if (_is_server && _env == _TT_ENV_X11 && (_id.len() > 0)) {
81 if ( (! _desktop->del_prop(TT_XATOM_NAME))
82 || (! _desktop->del_prop(TT_CDE_XATOM_NAME)))
84 _tt_syslog( 0, LOG_WARNING,
85 catgets( _ttcatd, 1, 14,
86 "could not delete the X "
87 "root window property %s "
94 #ifdef OPT_UNIX_SOCKET_RPC
95 if (_is_server && _socket_name.len()) {
96 (void)unlink((char *)_socket_name);
103 #ifdef OPT_UNIX_SOCKET_RPC
105 // Returns the name of the socket file if the unix socket rpc option is
106 // enabled. Note that the name should be unique for all sessions running
107 // on the same machine so it has to be derived from the session id
108 // (which has the required uniqueness properties).
113 #define SPRFX "/tmp/.TT"
115 if (_socket_name.len() == 0) {
116 char *sname = (char *)malloc(_id.len() + strlen(SPRFX) + 1);
119 sprintf(sname,"%s%s", SPRFX, (char *)_id);
127 _socket_name = sname;
128 free((MALLOCTYPE *)sname);
131 return((char *)_socket_name);
135 // Opens a connection to the unix socket bound to the given socket name.
136 // If successful returns the fd for the connection. Otherwise, returns
140 c_open_unix_socket(char *socket_name)
143 struct sockaddr_un server_addr;
144 sock = socket(AF_UNIX, SOCK_STREAM, 0);
146 _tt_syslog( 0, LOG_ERR, "c_open_unix_socket(): socket(): %m" );
149 memset(&server_addr, 0, sizeof(server_addr));
150 server_addr.sun_family = AF_UNIX;
151 strcpy(server_addr.sun_path, socket_name);
152 #if defined(ultrix) || defined(_AIX) || defined(hpux)
153 int servlen = strlen(server_addr.sun_path) + sizeof(server_addr.sun_fam\
155 if (connect(sock, (sockaddr *)&server_addr, servlen) < 0) {
157 if (connect(sock, (sockaddr *)&server_addr, sizeof(sockaddr_un)) < 0) {
165 #endif // OPT_UNIX_SOCKET_RPC
169 // Called if an event comes in from our desktop connection.
172 desktop_event_callback()
174 if (! _desktop->process_event()) {
183 // Initializes a session with the assumption that the server session is
184 // already running. If the server session isn't running then this method
185 // will return an error as opposed to attempting to autostart a new
186 // session (see _Tt_session::c_init). This is intended as a lower-level
187 // init function used by c_init or whenever autostarting is not required.
191 // TT_ERR_ACCESS Could not init desktop (diagnostic emitted)
192 // TT_WRN_NOTFOUND No advertised address
193 // TT_ERR_INVALID Could not parse advertised address
194 // TT_ERR_NO_MATCH Advertised address's version is too new
195 // (diagnostic emitted)
196 // TT_ERR_ADDRESS Could not find advertised host
197 // TT_ERR_NOMP Could not init as rpc client
199 Tt_status _Tt_session::
200 client_session_init()
202 // this is a client of this session. Note that since this
203 // function may be called in the server, we can't use
204 // _tt_mp->in_server().
207 int rpc_init_done = 0;
212 if (env() == _TT_ENV_X11 && _desktop.is_null()) {
213 _desktop = new _Tt_desktop();
214 if (! _desktop->init(_displayname, _TT_DESKTOP_X11)) {
215 return(TT_ERR_ACCESS);
218 if (_address_string.len() == 0) {
219 if (find_advertised_address(_address_string) != TT_OK) {
220 return(TT_WRN_NOTFOUND);
223 if (_address_string.len() == 0) {
224 // Don't call parsed_address() with an empty string,
225 // or it will think we're trying to create an address
226 // string instead of parse it. parsed_address()
227 // shouldn't be this cute. XXX
228 return TT_ERR_INVALID;
230 if ((err = parsed_address(_address_string)) != TT_OK) {
231 _address_string = (char *)0;
234 if (_TT_AUTH_ICEAUTH == _auth.auth_level() &&
235 (err = _auth.retrieve_auth_cookie()) != TT_OK) {
238 if (! _tt_global->find_host(_hostaddr, _host, 1)) {
239 return(TT_ERR_ADDRESS);
242 if (_id.len() == 0) {
246 #ifdef OPT_UNIX_SOCKET_RPC
247 if (_tt_global->get_local_host(lh)) {
248 // if we are in the same host as the server session
249 // then we attempt to connect to its advertised unix
250 // socket to set up an rpc connection. If we succeed
251 // then we set rpc_init_done to 1 to prevent a normal
252 // tcp rpc connection from being made.
253 if (lh->stringaddr() == _host->stringaddr()) {
254 _u_sock = c_open_unix_socket(local_socket_name());
256 _rpc_client = new _Tt_rpc_client(_u_sock);
257 if (_rpc_client->init(lh, _rpc_program,
262 // Since we opened the fd ourselves,
263 // clnt_destroy won\'t close it
264 // unless we tell it it\'s OK.
265 clnt_control(_rpc_client->rpc_handle(),
272 #endif // OPT_UNIX_SOCKET_RPC
273 if (! rpc_init_done) {
274 _rpc_client = new _Tt_rpc_client();
275 if (! _rpc_client->init(_host, _rpc_program, _rpc_version,
276 _server_uid, _auth)) {
281 // Get and save the file descriptor for this session.
282 _tt_mp->save_session_fd(_rpc_client->socket());
288 // Verifies that the server side of the session is alive and is a
289 // ToolTalk session. Currently this is done by first invoking rpc
290 // procedure 0 to see if the session is responding to rpc requests. If
291 // that succeeds then we invoke rpc procedure TT_RPC_VRFY_SESSION which
292 // is a reasonably large number. The reply to this procedure should be
293 // the session id of the session we're talking to. This is a fairly good
294 // (but not foolproof!) method of verifying that this is a ToolTalk
297 // In fact it seems to be too picky, if the session id we have is
298 // different only in the IP address, it may just be an alternate
299 // IP address for the very same session.
303 // TT_ERR_NOMP Session unreachable or a hoaxer
304 // TT_ERR_NO_MATCH Session has wrong version
305 // TT_ERR_INVALID Could not parse advertised address
306 // TT_ERR_AUTHORIZATION RPC authorization error
308 Tt_status _Tt_session::
314 // We used to call the NULLPROC here first, but that seems
315 // to be a waste of a round-trip since it doesn't tell
316 // us anything that the TT_RPC_VRFY_SESSION call doesn't.
318 is_live = call((int)TT_RPC_VRFY_SESSION,
319 (xdrproc_t)xdr_void, 0,
320 (xdrproc_t)tt_xdr_string,
323 // We used to string-compare the returned session id with the one we
324 // have, but that was too picky since the IP address can usefully be
325 // different. Skip the check for now, perhaps we should check at
326 // least some of the components (UNIX pid?) We already know the
327 // transient RPC number is good, and most everything else is
328 // constant. We do make a minimal check that the id string is non
329 // null, this might catch the (unlikely) case where the program at
330 // the other end isn't a ttsession but does happen to have a
331 // procedure with a number the same as TT_RPC_VRFY_SESSION,
332 // although in that case I'd expect the RPC to fail in the
335 if (is_live == TT_OK && 0 == sid.len()) {
336 is_live = TT_ERR_NOMP;
340 if (is_live == TT_ERR_UNIMP) {
341 is_live = TT_ERR_INTERNAL;
344 // And finally, for reasons I haven't quite figured out,
345 // if the session id we have *isn't* equal to the one returned
346 // by ttsession, file scoped messages fail. I suspect
347 // pattern matching somewhere compares the ids.
348 // So, if the ttsession thinks its name is different, replace
349 // our idea of the name with its idea of its name.
351 if (is_live == TT_OK) {
360 // Invokes an rpc call named by the rpc_proc parameter on the server
361 // session and returns any results. If timeout is anything other than -1
362 // then it will be used as the timeout value. Otherwise a suitable
363 // timeout value is chosen. If rebind is 1 then a new rpc connection will
364 // be attempted if this rpc fails.
368 // TT_ERR_NOMP Could not talk to session
369 // TT_ERR_VERSION_MISMATCH RPC VERSMISMATCH
370 // TT_ERR_UNIMP rpc_proc not implemented by session
371 // TT_ERR_INVALID Could not parse address
372 // TT_ERR_AUTHORIZATION RPC authorization error
374 Tt_status _Tt_session::
376 xdrproc_t xdr_arg_fn, char *arg,
377 xdrproc_t xdr_res_fn, char *res,
378 int timeout, int rebind)
381 // Ensure global xdr version is set to proper value for this session.
383 int xdr_version_2_use = _rpc_version;
385 // Some rpc calls use an xdr version number that is greater
386 // than the version number of the rpc protocol they exist in.
387 // See e.g. _tt_rpc_add_pattern_with_context().
390 case TT_RPC_ADD_PATTERN_WITH_CONTEXT:
391 if (xdr_version_2_use < TT_CONTEXTS_XDR_VERSION) {
392 xdr_version_2_use = TT_CONTEXTS_XDR_VERSION;
396 _Tt_xdr_version ver(xdr_version_2_use);
398 clnt_stat rpc_status;
403 if (_rpc_client.is_null()) {
404 return(TT_ERR_INTERNAL);
409 case TT_RPC_HDISPATCH:
410 case TT_RPC_HUPDATE_MSG:
413 case TT_RPC_DISPATCH_2:
414 case TT_RPC_DISPATCH_2_WITH_CONTEXT:
415 case TT_RPC_UPDATE_MSG_2:
416 case TT_RPC_MSGREAD_2:
420 tmout = TT_RPC_TMOUT;
429 status = TT_ERR_NOMP;
430 while (processing && retry >= 0) {
432 // We sometimes hang if we call a dead session
433 rpc_status = RPC_CANTRECV;
434 if (! _rpc_client->init(_host, _rpc_program,
438 status = TT_ERR_NOMP;
444 rpc_status = _rpc_client->call(rpc_proc, xdr_arg_fn,
445 arg, xdr_res_fn, res,
448 switch (rpc_status) {
455 status = TT_ERR_NOMP;
458 case RPC_VERSMISMATCH:
459 case RPC_PROGVERSMISMATCH:
460 status = TT_ERR_VERSION_MISMATCH;
464 if (tmout == 0 || tmout == -1) {
465 // rpc always returns a timeout error if we're
466 // in message-passing mode
470 status = TT_ERR_NOMP;
476 status = TT_ERR_NOMP;
480 // try to rebind _rpc_client and try again
481 if (! _rpc_client->init(_host, _rpc_program,
485 status = TT_ERR_NOMP;
490 case RPC_PROCUNAVAIL:
491 // We get this if there is a mild version
492 // mismatch, e.g. a 1.1 library talking to
493 // a 1.0.x server, and one of the new
494 // API calls is used. This is relatively
495 // benign, just let the user know that
496 // his call is unimplemented in this system
497 status = TT_ERR_UNIMP;
501 status = TT_ERR_AUTHORIZATION;
505 status = TT_ERR_INTERNAL;
511 if (status == TT_ERR_NOMP &&
512 !_tt_mp->in_server() &&
513 !_tt_c_mp->default_c_session.is_null() &&
514 has_id(_tt_c_mp->default_c_session->address_string())) {
515 _tt_c_mp->default_c_session = NULL;
523 // Attempts to find the address of the server session. This is done
524 // according to what kind of environment or session type we're in.
526 Tt_status _Tt_session::
527 find_advertised_address(_Tt_string &session_addr)
531 if ((! _desktop->get_prop(TT_CDE_XATOM_NAME, session_addr)) &&
532 (! _desktop->get_prop(TT_XATOM_NAME, session_addr))) {
536 case _TT_ENV_PROCESS_TREE:
537 if (_address_string.len() == 0) {
538 session_addr = _tt_get_first_set_env_var(2, TT_CDE_XATOM_NAME, TT_XATOM_NAME);
540 session_addr = _address_string;
551 // Returns a number indicating what type of session we're in.
553 _Tt_env _Tt_session::
556 if (_tt_mp->in_server()) {
557 if (_env == _TT_ENV_LAST && getenv("DISPLAY")) {
561 } else if (_env != _TT_ENV_LAST) {
564 if (_tt_get_first_set_env_var(2, TT_CDE_XATOM_NAME, TT_XATOM_NAME)) {
565 return(_env = _TT_ENV_PROCESS_TREE);
566 } else if (getenv("DISPLAY")) {
567 return(_env = _TT_ENV_X11);
569 return(_TT_ENV_LAST);
576 // Sets the session-type for the session. This affects how the session id
577 // is advertised to potential clients.
580 set_env(_Tt_env session_env, _Tt_string arg)
595 // Returns the ToolTalk session id for the X session named by xdisp. Note
596 // that no actual contact is attempted.
598 _Tt_string _Tt_session::
599 Xid(_Tt_string xdisp)
601 return _desktop->session_name(xdisp);
606 print(FILE *fs) const
608 fprintf(fs,"_Tt_session::\n");
613 // Used to set or get the address string of a session as well as set
614 // appropiately the _rpc_version field of the session. If addr_string is
615 // null, then parsed_address will set the address string of the session.
616 // In this case, it sets _rpc_version to the default TT_RPC_VERSION. If
617 // addr_string is not null then parsed_address will attempt to parse the
618 // string and extract the information in it. In this case, if the
619 // addr_string happens to be an fcs1 version of session addresses then
620 // _rpc_version is set to 1, otherwise it is set to TT_RPC_VERSION.
622 // Note that the intended convention for address strings is that only the
623 // information that is known to this method will be parsed out of them.
624 // If there is any information beyond that then it is left uninterpreted.
625 // This means that future ToolTalk versions are free to append
626 // information to the end of an address string (as is already done
627 // below) without compromising compatibility with older clients.
631 // TT_ERR_INVALID Could not parse address
632 // TT_ERR_NO_MATCH Address version is too new (diagnostic emitted)
634 Tt_status _Tt_session::
635 parsed_address(_Tt_string &addr_string)
637 #define IPVADDRLEN 16
638 char session_host[IPVADDRLEN];
640 int junk_version = 1;
642 const char *addr_format_fmt = "%%ld %%d %%d %%d %%lu %%%ds %%d";
643 char addr_format[32];
644 const char *fcs1_addr_format_fmt = "%%ld %%d %%d %%d %%lu %%%ds";
645 char fcs1_addr_format[32];
648 // Note: the fcs 1.0 version of tooltalk uses the first 6
649 // fields in addr_format. Changing these first 6 fields
650 // compromises binary compatibility with the 1.0 version.
651 // Additional fields can be added at the end of addr_format as
652 // long as if the parsing fails then the old format
653 // (fcs1_addr_format) is tried.
655 _rpc_version = TT_RPC_VERSION;
656 if (addr_string.len() == 0) {
659 // set address string
660 if (_host.is_null()) {
661 return(TT_ERR_INTERNAL);
663 if (_host->stringaddr().len() == 0) {
664 return(TT_ERR_INTERNAL);
667 ipaddr_len = _host->stringaddr().len();
668 if (ipaddr_len > IPVADDRLEN) ipaddr_len = IPVADDRLEN;
670 sprintf(fcs1_addr_format, fcs1_addr_format_fmt, ipaddr_len);
671 sprintf(addr_format, addr_format_fmt, ipaddr_len);
673 // put version number for format first (this is so we
674 // can change the format later)
675 sprintf(strid, "%02d ", _TT_XATOM_VERSION);
676 sprintf(strid+3, addr_format,
680 (int)_auth.auth_level(),
682 (char *)_host->stringaddr(),
686 if ((status = _auth.set_sessionid(
690 _rpc_version)) != TT_OK) {
691 _tt_syslog(0, LOG_ERR,"_auth.set_sessionid() != TT_OK");
695 // get (parse) address string
696 char *str = (char *)addr_string;
698 sprintf(fcs1_addr_format, fcs1_addr_format_fmt, IPVADDRLEN);
699 sprintf(addr_format, addr_format_fmt, IPVADDRLEN);
701 // check version number of format first
703 // XXX: note that if _TT_XATOM_VERSION is ever changed
704 // then backward compatibility is seriously
707 if (atoi(str) != _TT_XATOM_VERSION) {
708 _tt_syslog( 0, LOG_ERR,
709 catgets( _ttcatd, 1, 15,
710 "address version is %d, but "
711 "I only understand %d! (address: "
713 atoi(str), _TT_XATOM_VERSION,
714 addr_string.operator const char *() );
715 return TT_ERR_NO_MATCH;
718 // XXX: Originally, the fcs1_addr_format put in a
719 // field describing the rpc version of the server.
720 // However, the fcs1 version of client_session_init
721 // erroneously initialized the rpc connection with the
722 // *server* rpc version rather than its own. Thus we
723 // can't use the rpc version field in fcs1_addr_format
724 // because then old fcs1.0 clients would mistakenly
725 // initialize themselves as version 1+ clients. Thus
726 // (sigh) we have to add a field at the end of
727 // fcs1_addr_format that is the real rpc version of
728 // the server. This is the reason that the first rpc
729 // version parsed from addr_format is thrown away.
732 long long_server_uid;
733 _Tt_auth_level auth_level;
734 if (7 != sscanf(str+3, addr_format,
737 &junk_version, /* always 1 ... */
743 // new format scan failed. Try to parse the
744 // string for the old format
746 if (6 != sscanf(str+3, fcs1_addr_format,
753 _pid = (pid_t) long_pid;
754 _server_uid = (uid_t) long_server_uid;
755 return(TT_ERR_INVALID);
760 _pid = (pid_t) long_pid;
761 _server_uid = (uid_t) long_server_uid;
762 _hostaddr = session_host;
763 _auth.set_auth_level(auth_level);
764 if ((status = _auth.set_sessionid(
768 _rpc_version)) != TT_OK) {
769 _tt_syslog(0, LOG_ERR,"_auth.set_sessionid() != TT_OK");
779 // Sets the id of a session. Note that addressing information (the rpc
780 // number and version) are not part of the id. This makes it possible in
781 // the future to provide server address rebinding.
783 Tt_status _Tt_session::
791 if (id != (char *)0) {
795 // construct a suitable DISPLAY name from the id
796 // which should have the format "X <host> <server_num>"
797 if (sscanf((char *)id, "X %s %d", host, &svnum) != 2) {
798 return(TT_ERR_SESSION);
800 /* We _cannot_ set _displayname based solely on host and svnum,
801 * because :0 is NOT the same as 127.0.0.1:0 as far as X11
802 * is concerned: by default, it will only accept connections
803 * to the former. (XOpenDisplay etc. will fail if you try the below!)
804 sprintf(dpname, "%s:%d", host, svnum);
805 _displayname = dpname;
807 if (! _displayname.len()) {
808 _displayname = _tt_global->xdisplayname;
815 // A process-tree session id is just an address string
817 _address_string = _id.mid(2, _id.len());
818 _env = _TT_ENV_PROCESS_TREE;
821 return(TT_ERR_SESSION);
825 // set the "id" which is the publicly visible *logical*
826 // address of this session.
827 ssid = _tt_get_first_set_env_var(2, TT_CDE_START_SID, TT_START_SID);
828 if (ssid != (char *)0) {
834 if (! _displayname.len()) {
835 _displayname = _tt_global->xdisplayname;
837 _id = Xid(_displayname);
839 case _TT_ENV_PROCESS_TREE:
842 // sami said originally:
843 // we need to include the address string in
844 // the id of a process-tree session because
845 // there is no alternate means of finding
846 // the address (ie. for X11 the id could be
847 // rid of address info because the X server
848 // served as the alternate way of finding
850 // rfm sez, 16 June 94:
851 // Of course that alternate way was a bad
852 // idea since it means other users can't
853 // send messages using the session because
854 // they can't contact the X server.
855 _id = process_tree_id();
859 return(TT_ERR_INTERNAL);
869 // Returns a process-tree id for this session. This is essentially just
870 // the address of the session preceded by a "P". The purpose of this is
871 // that whenever this exact session needs to be advertised to a
872 // persistent medium (as is done with file-scope messages) this version
873 // of the session id is used so that if the session goes down and comes
874 // up again, the session id stored on disk will become stale.
876 _Tt_string _Tt_session::
879 _Tt_string result = "P ";
881 return(result.cat(_address_string));
888 return(_id.xdr(xdrs) && _address_string.xdr(xdrs));
892 _Tt_string _Tt_session::
895 return(_address_string);
898 _Tt_string _Tt_session::
901 return(_displayname);
906 _tt_session_address(_Tt_object_ptr &o)
908 return ((_Tt_session *)o.c_pointer())->address_string();
913 // Returns 1 if the given id is the same as the id of this session or if
914 // it is a process-tree id then it has the same address as this session.
917 has_id(const _Tt_string &id)
927 if (id == _address_string) {
932 _Tt_string addr = id.mid(2,id.len());
933 return(addr == _address_string);
940 // ...for strings list
942 has_id(const _Tt_string_list_ptr slist_p)
944 _Tt_string_list_cursor c(slist_p);