/* * CDE - Common Desktop Environment * * Copyright (c) 1993-2012, The Open Group. All rights reserved. * * These libraries and programs are free software; you can * redistribute them and/or modify them under the terms of the GNU * Lesser General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * These libraries and programs are distributed in the hope that * they will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public * License along with these libraries and programs; if not, write * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth * Floor, Boston, MA 02110-1301 USA */ //%% (c) Copyright 1993, 1994 Hewlett-Packard Company //%% (c) Copyright 1993, 1994 International Business Machines Corp. //%% (c) Copyright 1993, 1994 Sun Microsystems, Inc. //%% (c) Copyright 1993, 1994 Novell, Inc. //%% $TOG: mp_s_session.C /main/6 1999/09/10 19:15:11 mgreess $ /* * mp_s_session.C 1.25 29 Jul 1993 * * mp_s_session.cc - Server-only methods of the session object * * Copyright (c) 1990,1992 by Sun Microsystems, Inc. */ #include #include "mp/mp_global.h" #include "mp_s_message.h" #include "mp_s_mp.h" #include "mp/mp_mp.h" #include "mp_s_pattern.h" #include "mp_s_procid.h" #include "mp_rpc_server.h" #include "mp_rpc_implement.h" #include "mp_s_session.h" #include "mp/mp_desktop.h" #include "mp/mp_xdr_functions.h" #include "util/tt_global_env.h" #include "util/tt_host.h" #include "util/tt_enumname.h" #include "util/tt_port.h" #include "util/tt_gettext.h" #include #include "util/tt_port.h" #include #ifdef OPT_UNIX_SOCKET_RPC # include # include # include "mp_rpc_server_utils.h" #endif // OPT_UNIX_SOCKET_RPC // Use the parent class (_Tt_session) for construction and destruction. _Tt_s_session::_Tt_s_session () {} _Tt_s_session::~_Tt_s_session () { #ifdef OPT_UNIX_SOCKET_RPC if (_socket_name.len()) { // A UNIX domain socket that was bound to a pathname // and therefore must be removed by the owner when the session // server exits. This ensures that when the next session // server starts, it can choose the same pathname and bind // to it. The pathname cannot refer to a file existing // on the system. (void)unlink(_socket_name); } #endif // OPT_UNIX_SOCKET_RPC } // // Callback for i/o errors detected for the desktop session. The default // action is to exit immediately. // int _tt_xio_handler(Display * /* xdisp */) { exit(0); return 0; } #ifdef OPT_UNIX_SOCKET_RPC // // Utility routine to open a unix domain socket and bind it to the given // socket name. // // XXX: It would be more elegant to add unix domain support to the // _Tt_stream_socket class rather than have these special routines for // dealing with them. // static int s_open_unix_socket(char *socket_name) { /* open a unix socket */ int sock; struct sockaddr_un server_addr; sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) { _tt_syslog(0, LOG_ERR, "s_open_unix_socket(): socket(): %m"); return(-1); } memset(&server_addr, 0, sizeof(server_addr)); server_addr.sun_family = AF_UNIX; (void)unlink(socket_name); strcpy(server_addr.sun_path, socket_name); #if defined(_AIX) || defined(hpux) int servlen = strlen(server_addr.sun_path) + sizeof(server_addr.sun_fam\ ily); if (bind(sock, (struct sockaddr *)&server_addr,servlen) < 0) { #else if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_un)) < 0) { #endif return(-1); } if (listen(sock, 5) != 0) { return(-1); } return(sock); } // // Called in response to a client request for a new rpc connection. We // accept the connection on the unix socket and then use the standard // _Tt_rpc_server object, which acts accordingly if given an open file // descriptor in its constructor (see that method for more details) to // open the rpc connection. // void _Tt_s_session:: u_rpc_init() { int msgsock; _Tt_rpc_server_ptr server_object; // connection request came in for the unix socket msgsock = accept(_u_sock, (struct sockaddr *)0, 0); if (msgsock == -1) { return; } server_object = new _Tt_rpc_server(_rpc_program, TT_RPC_VERSION, msgsock, _auth); if (! server_object->init(_tt_service_rpc)) { _tt_syslog(0, LOG_ERR, "u_rpc_init: server_object->init(): 0"); } } #endif // OPT_UNIX_SOCKET_RPC // // This is the server-side init routine for the _Tt_session object. The // basic duties for this method are to advertise the server address // accordingly after initializing the rpc server object. If we are in a // desktop session then we first check for a session already running and // return an error code if we find one. // // Returns: // TT_OK // TT_ERR_SESSION Found a live session (diagnostic has been emitted) // TT_ERR_NOMP Could not initialize (diagnostic has been emitted) // Tt_status _Tt_s_session:: s_init() { _Tt_session rsession; Tt_status status; _Tt_string h; _Tt_desktop_lock_ptr dt_lock; // this is the server session for this session _is_server = 1; if (env() == _TT_ENV_X11) { if (_displayname.len() == 0) { _displayname = _tt_global->xdisplayname; } _desktop = new _Tt_desktop(); if (! _desktop->init(_displayname, _TT_DESKTOP_X11)) { return(TT_ERR_NOMP); } _desktop->set_error_handler((_Tt_dt_errfn)_tt_xio_handler); // set this field in the _Tt_s_mp object which will // then be used to add the fd for the desktop // connection to the list of fds that get polled. _tt_s_mp->xfd = _desktop->notify_fd(); // // Grab the X server. The grab is in effect until // dt_lock goes out of scope (i.e., when we return). // For proper test-and-set, the grab needs to start // before check_for_live_session() and end after // advertise_address(). // dt_lock = new _Tt_desktop_lock( _desktop ); // check for a session already running switch (status = check_for_live_session()) { case TT_OK: case TT_ERR_NOMP: // Muscle in on dead ttsessions status = TT_OK; break; case TT_ERR_SESSION: return TT_ERR_SESSION; default: _tt_syslog(0, LOG_ERR, "_Tt_s_session::check_for_live_session(): %s", _tt_enumname(status)); return TT_ERR_INTERNAL; } } _pid = getpid(); _server_uid = getuid(); _queued_messages = new _Tt_message_list(); _properties = new _Tt_session_prop_list; // Create an RPC server managing object which will // invoke the appropiate dispatch function on any RPC // requests. This object also allocates an RPC program // number which is then used to compose the session id to // be advertised to potential clients. _rpc_server = new _Tt_rpc_server(RPC_ANYSOCK, TT_RPC_VERSION, RPC_ANYSOCK, _auth); if (! _rpc_server->init(_tt_service_rpc)) { return(TT_ERR_NOMP); } _rpc_program = _rpc_server->program(); _rpc_version = _rpc_server->version(); // initializes our local host object which allows us to // inquire our host address so we can then advertise it to // clients. if (! _tt_global->get_local_host(_host)) { _tt_syslog(0, LOG_ERR,"get_local_host(): 0"); return(TT_ERR_NOMP); } if (parsed_address(_address_string) != TT_OK) { _tt_syslog(0, LOG_ERR,"parsed_address() != TT_OK"); return(TT_ERR_NOMP); } if (_TT_AUTH_ICEAUTH == _auth.auth_level() && (status = _auth.generate_auth_cookie()) != TT_OK) { return status; } if ((status = set_id()) == TT_OK) { // advertise our address according to the type of // session environment that we are in. if ((status = advertise_address()) == TT_OK) { #ifdef OPT_UNIX_SOCKET_RPC // open a unix domain socket for connection // requests and set the unix_fd field in the // _Tt_s_mp object to it so it can be added to // the list of file descriptors polled. _u_sock = s_open_unix_socket(local_socket_name()); _tt_s_mp->unix_fd = _u_sock; #endif // OPT_UNIX_SOCKET_RPC return(TT_OK); } else { return(status); } } else { return(status); } } // // Used to check whether there is already a live session. Only desktop // sessions need this check since process-tree sessions are always unique // since they are based on the process hierarchy. // // XXX: If the range of environments were to be extended to more than // just desktop and process-tree sessions we would need more abstract // methods for session environments that return whether this check is // necessary. // // Returns: // TT_OK Found no session // TT_ERR_SESSION Found a live session (diagnostic emitted) // TT_ERR_NOMP Found a dead session (diagnostic emitted) // Tt_status _Tt_s_session:: check_for_live_session() { _Tt_session rsession; Tt_status status; if (env() != _TT_ENV_X11) { return(TT_OK); } // try to find the address of an advertised session if (find_advertised_address(rsession._address_string) == TT_OK) { // found another server id, check to see that it's // running. rsession._desktop = _desktop; rsession._displayname = _displayname; status = rsession.client_session_init(); switch (status) { Tt_status ping_status; case TT_OK: switch (ping_status = rsession.ping()) { case TT_OK: case TT_ERR_INVALID: case TT_ERR_NO_MATCH: case TT_ERR_UNIMP: _tt_syslog(stderr, LOG_ERR, catgets(_ttcatd, 2, 6, "Found another session run" "ning (host=%s, pid=%d)"), (char *)(rsession.host()->name()), rsession._pid); return(TT_ERR_SESSION); default: _tt_syslog(0, LOG_ERR, "_Tt_session::ping(): %s", _tt_enumname(ping_status)); return TT_ERR_INTERNAL; case TT_ERR_NOMP: break; // and fall through } // Fall through case TT_ERR_NOMP: {const char *hostname = ""; if (! rsession.host().is_null()) { hostname = rsession.host()->name(); } _tt_syslog(stderr, LOG_ERR, catgets(_ttcatd, 2, 7, "Can't contact alleged session " "(host=%s, pid=%d); " "assuming it crashed..."), hostname, rsession._pid); return(TT_ERR_NOMP);} case TT_ERR_INVALID: _tt_syslog(stderr, LOG_ERR, catgets(_ttcatd, 2, 8, "Can't parse advertised session id;" " overwriting it with my own...")); return(TT_ERR_NOMP); case TT_WRN_NOTFOUND: case TT_ERR_ACCESS: case TT_ERR_NO_MATCH: return(TT_OK); default: _tt_syslog(0, LOG_ERR, "_Tt_session::client_session_init(): %s", _tt_enumname(status)); return(TT_ERR_INTERNAL); } } return(TT_OK); } // // Advertises a session procid so that any clients that come up within // the appropiate domain will find the session id. // Tt_status _Tt_s_session:: advertise_address() { _Tt_string s; _Tt_string prop(TT_XATOM_NAME); _Tt_string cde_prop(TT_CDE_XATOM_NAME); _Tt_string xdisp("DISPLAY"); switch(env()) { case _TT_ENV_X11: // advertise our address by setting a special property // on our desktop session. if (_desktop->set_prop(cde_prop, _address_string) && _desktop->set_prop(prop, _address_string)) { s = xdisp.cat("=").cat(_displayname); (void)putenv(strdup((char *)s)); return(TT_OK); } return(TT_ERR_INTERNAL); case _TT_ENV_PROCESS_TREE: // advertise our address by exporting a special // environment variable. if (_tt_put_all_env_var (2, _address_string, (char*)cde_prop, (char*)prop) != 2) { return(TT_ERR_INTERNAL); } return(TT_OK); case _TT_ENV_LAST: default: return(TT_ERR_INTERNAL); } } // // This function is called to notify the session object that a new // pattern has been registered by some procid. This means that for any // queued messages, there may now be a procid that is suitable to receive // the message so this function iterates through all the queued messages // attempting to deliver the message. // // XXX: For efficiency, this method might take an argument which is the // new pattern and then delivery of the queued messages could be // restricted to just matching against the new pattern. // void _Tt_s_session:: pattern_added() { _Tt_message_list_cursor qmsgs; _Tt_s_message *sm; /* try to deliver session queued msgs */ qmsgs.reset(_queued_messages); while (qmsgs.next()) { sm = (_Tt_s_message *)(*qmsgs).c_pointer(); // set state to "sent" sm->set_state(TT_SENT); _Tt_msg_trace trace( **qmsgs, TTDR_SESSION_JOIN ); if (sm->deliver( trace )) { qmsgs.remove(); } else { // couldn't successfully deliver so revert // state to queued. sm->set_state(TT_QUEUED); } } } // // The given procid wishes to join this session. We iterate through the // procid's patterns and add our session id to them. We also invoke the // pattern_added method since we may have an opportunity to deliver more // queued messages. // Tt_status _Tt_s_session:: s_join(_Tt_s_procid_ptr &procid) { // update session-scoped handler patterns with session id mod_session_id_in_patterns(procid->patterns(), 1); procid->set_active(1); if (procid->is_active()) { // we've altered the patterns registered so we call // pattern_added() to attempt delivery of any // queued messages. pattern_added(); } return(TT_OK); } // // For each pattern in patterns that is session-scoped, this method will // add or delete (depending on add parameter) this session's id to the // session list for the pattern. // void _Tt_s_session:: mod_session_id_in_patterns(_Tt_pattern_list_ptr &patterns, int add) { _Tt_pattern_list_cursor pcursor; int scopes; pcursor.reset(patterns); while (pcursor.next()) { scopes = pcursor->scopes(); if (scopes&(1<add_session(address_string()); // set special pattern flag to // optimize session-scoped pattern // matching. pcursor->set_in_session(); } else { pcursor->del_session(address_string()); // clear special pattern flag to // optimize session-scoped pattern // matching. pcursor->clr_in_session(); } } } } // // The given procid wishes to quit this session. We remove our session id // from the procid's patterns. // Tt_status _Tt_s_session:: s_quit(_Tt_s_procid_ptr &procid) { // delete session from handler patterns mod_session_id_in_patterns(procid->patterns(), 0); return(TT_OK); } // // Adds a message to the queue of session-queued messages. // void _Tt_s_session:: queue_message(_Tt_message_ptr &m) { if (! m->handler_ptype().len()) { // can't queue a message with no handler ptype return; } _Tt_message_list_cursor qm; Tt_state qm_state; _Tt_string qm_ptype; // verify that only one message with m's id and handler_ptype // exists in the queue (in the same state as m) qm.reset(_queued_messages); while (qm.next()) { if (qm->is_equal(m)) { qm_state = qm->state(); if (qm_state == m->state()) { qm_ptype = qm->handler_ptype(); if (qm_ptype == m->handler_ptype()){ return; } } } } _queued_messages->append(m); } _Tt_message_list_ptr & _Tt_s_session:: queued_messages() { return(_queued_messages); }