/* * 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. //%% $XConsortium: mp_s_mp.C /main/6 1996/05/09 20:30:03 drk $ /* * * @(#)mp_s_mp.C 1.42 95/09/18 * * Copyright 1990,1993 Sun Microsystems, Inc. All rights reserved. */ #include "mp_s_global.h" #include "mp_s_mp.h" #include "mp/mp_mp.h" #include "mp_s_procid.h" #include "mp_self_procid.h" #include "mp_rpc_implement.h" #include "mp_rpc_server.h" #include "mp_s_session.h" #include "mp/mp_xdr_functions.h" #include "mp_ptype.h" #include "mp_otype.h" #include "mp/mp_arg.h" #include "mp_s_pattern.h" #include "mp_signature.h" #include "mp_s_message.h" #include "mp_typedb.h" #include "mp/mp_file.h" #include "util/tt_global_env.h" #include "util/tt_base64.h" #include "util/tt_host.h" #include "util/tt_port.h" #include #include #include #include #include #include #include #include #include #include // global pointer to the _Tt_s_mp object. There should only be one // instance of this object. _Tt_s_mp *_tt_s_mp = (_Tt_s_mp *)0; _Tt_s_mp:: _Tt_s_mp() : _Tt_mp() { _flags |= (1<<_TT_MP_IN_SERVER); initial_session = initial_s_session = new _Tt_s_session; _active_fds = new _Tt_int_rec_list(); _active_fds_procids = new _Tt_string_list(); _mp_start_time = (int)time(0); _min_timeout = -1; _next_procid_key = 0; xfd = -1; exit_main_loop = 0; max_active_messages = 2000; fin = 0; fout = 0; ptable = new _Tt_ptype_table(_tt_ptype_ptid, 50); otable = new _Tt_otype_table(_tt_otype_otid, 50); sigs = new _Tt_sigs_by_op_table(_tt_sigs_by_op_op, 250); opful_pats = new _Tt_patlist_table(_tt_patlist_op, 250); opless_pats = new _Tt_pattern_list(); active_procs = new _Tt_s_procid_table(_tt_procid_id, 250); now = 1; when_last_observer_registered = 1; update_args.message = new _Tt_s_message(); _self = (_Tt_s_procid *)new _Tt_self_procid(); map_ptypes = 0; unix_cred_chk_flag = 0; garbage_collector_pid = 0; _next_garbage_run = 0; } _Tt_s_mp:: ~_Tt_s_mp() { // // Emulate a tt_close() for _self. // XXX Commented out because currently s_remove_procid() neither: // - deletes this session from the interest list, nor // - sends an on_exit message (since _self submits none) // // if (! _self.is_null()) { // s_remove_procid( _self ); // } } // // Initializes a _Tt_s_mp object. This entails initializing // the initial session which is the default session that other clients // connect to. This method will also initialize the global _Tt_s_mp // object if the session is initialized. This is important since the // _Tt_s_mp::init method assumes that the session has been initialized. // XXX: the existence of the s_init and init methods is left over from // an earlier incomplete split of _Tt_mp into server and client subclasses. // Really, there should be just one virtual init method. // Tt_status _Tt_s_mp:: s_init() { Tt_status status; active_messages = 0; // initialize "initial" session which is the server session in // server mode and the "default" session in client mode. status = initial_s_session->s_init(); if (status == TT_OK) { status = init(); } return(status); } // // Initializes the _Tt_s_mp object. This entails putting the two special // "pseudo-procids" into the _active_fds and _active_fds_procids lists // (see comment for _Tt_s_mp::find_proc) // Tt_status _Tt_s_mp:: init() { if (xfd != -1) { // put in fd for X connection _active_fds->push(new _Tt_int_rec(xfd)); _active_fds_procids->push(_Tt_string("X")); } #ifdef OPT_UNIX_SOCKET_RPC /* XXX: UNIX_SOCKET */ if (unix_fd != -1) { // put in fd for local rpc connections _active_fds->push(new _Tt_int_rec(unix_fd)); _active_fds_procids->push(_Tt_string("U")); } /* XXX: UNIX_SOCKET */ #endif // OPT_UNIX_SOCKET_RPC return(TT_OK); } // // Initialize our _Tt_self_procid and register patterns for the // messages we are interested in. // Tt_status _Tt_s_mp::init_self() { // Grab the global mutex to lock out other threads _tt_global->grab_mutex(); // Increment the counter for the number of RPC calls // handled during the life of this process. This has // to be done here since init_self() ultimately calls // something which calls updateFileSystemEntries(), // yet it's not done through an API or RPC call. Thus, // this line here is somewhat of a bandaid. XXX _tt_global->event_counter++; // Use the lame do-loop hack to avoid repeating the drop_mutex // code after every possible failure... Tt_status status = TT_OK;; do { // // tt_open(), tt_fd() // Tt_status status = _self->init(); if (status != TT_OK) { break; } status = add_procid( _self ); if (status != TT_OK) { break; } _self->set_fd(); status = _handle_Session_Trace(); if (status != TT_OK) { break; } status = _observe_Saved(); if (status != TT_OK) { break; } } while (0); _tt_global->drop_mutex(); return TT_OK; } Tt_status _Tt_s_mp::_handle_Session_Trace() { // // tt_pattern_create(), tt_pattern_category_set(), // tt_pattern_scope_add(), tt_pattern_session_add(), // tt_pattern_op_add(), tt_pattern_arg_add() // _Tt_s_pattern_ptr pat = _Tt_self_procid::s_pattern_create(); Tt_status status = pat->set_category( TT_HANDLE ); if (status != TT_OK) { return status; } status = pat->add_message_class( TT_REQUEST ); if (status != TT_OK) { return status; } status = pat->add_scope( TT_SESSION ); if (status != TT_OK) { return status; } status = pat->add_session( _tt_s_mp->initial_s_session->address_string() ); if (status != TT_OK) { return status; } status = pat->add_op( "Session_Trace" ); if (status != TT_OK) { return status; } _Tt_arg_ptr arg = new _Tt_arg( TT_IN, "string" ); status = pat->add_arg( arg ); if (status != TT_OK) { return status; } pat->server_callback = _Tt_self_procid::handle_Session_Trace; // // tt_pattern_register() // status = _self->add_pattern( pat ); if (status != TT_OK) { return status; } // // tt_session_join() // // _Tt_s_procid_ptr s_proc = (_Tt_s_procid *)_self.c_pointer(); // status = _tt_s_mp->initial_s_session->s_join( s_proc ); // if (status != TT_OK) { // return status; // } return TT_OK; } Tt_status _Tt_s_mp::_observe_Saved() { // // tt_pattern_create(), tt_pattern_category_set(), // tt_pattern_scope_add(), tt_pattern_op_add(), tt_pattern_arg_add() // _Tt_s_pattern_ptr pat = _Tt_self_procid::s_pattern_create(); Tt_status status = pat->set_category( TT_OBSERVE ); if (status != TT_OK) { return status; } status = pat->add_message_class( TT_NOTICE ); if (status != TT_OK) { return status; } status = pat->add_scope( TT_BOTH ); if (status != TT_OK) { return status; } status = pat->add_session( _tt_s_mp->initial_s_session->address_string() ); if (status != TT_OK) { return status; } _Tt_string_list_ptr ttpath = _Tt_typedb::tt_path(); if (ttpath.is_null()) { return TT_ERR_NOMEM; } _Tt_string_list_cursor pathC( ttpath ); while (pathC.next()) { status = pat->add_netfile( *pathC, 1 ); if (status != TT_OK) { return status; } } status = pat->add_op( "Saved" ); if (status != TT_OK) { return status; } _Tt_arg_ptr arg = new _Tt_arg( TT_IN, "File" ); status = pat->add_arg( arg ); if (status != TT_OK) { return status; } pat->server_callback = _Tt_self_procid::observe_Saved; // // tt_pattern_register() // status = _self->add_pattern( pat ); if (status != TT_OK) { return status; } status = pat->join_files(_tt_s_mp->initial_s_session->process_tree_id()); if (status != TT_OK) { return status; } return TT_OK; } // // Installs a list of signatures. Each signature is installed in a table // called sigs_table that maps op names to lists of signatures with that // same opname. This cuts down on the number of signatures that need to // be examined to dispatch a message. // void _Tt_s_mp:: install_signatures(_Tt_signature_list_ptr &s) { _Tt_signature_list_cursor sigC(s); _Tt_sigs_by_op_ptr sigs_byop; while (sigC.next()) { sigs_byop = (_Tt_sigs_by_op *)0; if (! sigs->lookup(sigC->op(),sigs_byop)) { sigs_byop = new _Tt_sigs_by_op(sigC->op()); sigs_byop->sigs = new _Tt_signature_list(); sigs->insert(sigs_byop); } sigs_byop->sigs->push(*sigC); } } void _Tt_s_mp:: remove_signatures(const _Tt_ptype &ptype) { _Tt_sigs_by_op_table_cursor sigs_byopC(sigs); while (sigs_byopC.next()) { _Tt_signature_list_cursor sigC(sigs_byopC->sigs); while (sigC.next()) { if (sigC->ptid() == ptype.ptid()) { sigC.remove(); } } } } void _Tt_s_mp:: remove_signatures(const _Tt_otype &otype) { _Tt_sigs_by_op_table_cursor sigs_byopC(sigs); while (sigs_byopC.next()) { _Tt_signature_list_cursor sigC(sigs_byopC->sigs); while (sigC.next()) { if (sigC->otid() == otype.otid()) { sigC.remove(); } } } } // // Iterates through the given table of ptypes and installs all the // signatures contained in each ptype. // void _Tt_s_mp:: install_ptable(_Tt_ptype_table_ptr &p) { _Tt_ptype_table_cursor ptypes; ptable = p; ptypes.reset(ptable); while (ptypes.next()) { remove_signatures(**ptypes); install_signatures(ptypes->hsigs()); install_signatures(ptypes->osigs()); } } // // Iterates through the given table of otypes and installs all the // signatures contained in each otype. // void _Tt_s_mp:: install_otable(_Tt_otype_table_ptr &o) { _Tt_otype_table_cursor otypes; otable = o; otypes.reset(otable); while (otypes.next()) { remove_signatures(**otypes); install_signatures(otypes->hsigs()); install_signatures(otypes->osigs()); } } // // It is important that the mp contain exactly one object for each // procid that is registered because there is state that is contained // in this instance of a procid that shouldn't be duplicated. This // method takes a generic \(neither server nor client\) procid and // returns either // the _Tt_s_procid object that the mp has for the given id or else creates // a new object if create_ifnot is equal to 1. When a procid is // initialized, it's signalling fd is added to two parallel lists, // _active_fds and _active_fds_procids. _active_fds contains a list // of fds whereas _active_fds_procids contains a list of procid ids. // Each position in the two lists corresponds to each other. This is // equivalent to having a single list of records where each record // contains an fd and a procid id. These lists are used by the // _Tt_s_mp::main_loop to poll all the fds in the list and match them // with their respective procids. // // The _active_fds_procids list can contain two "pseudo-procids" that // are used to keep track of some special fds. These can be "U" for // the special unix socket fd (used if OPT_UNIX_SOCKET_RPC is defined) // that clients use to establish a unix socket rpc connection, and "X" // for the fd that represents the connection to our desktop session. // See _Tt_s_mp::main_loop to see how they are used. // // XXX: Replacing the parallel list structure with a list of records // would increase readability. // int _Tt_s_mp::find_proc( const _Tt_procid_ptr &procid, _Tt_s_procid_ptr &proc_returned, int create_ifnot ) { _Tt_s_procid_ptr sp; if (! procid.is_null()) { if (! _last_proc_hit.is_null()) { if (_last_proc_hit->is_equal(procid)) { proc_returned = _last_proc_hit; return(1); } } active_procs->lookup(procid->id(),sp); } if (!sp.is_null()) { _last_proc_hit = sp; proc_returned = sp; return(1); } if (create_ifnot) { sp = new _Tt_s_procid(procid); /* procid not found, add it to list */ if (sp->init() == TT_OK) { proc_returned = sp; add_procid( sp ); return(1); } else { return(0); } } else { return(0); } } // // Called in order to generate a unique key for a new procid. This key is // prepended to a procid's session in order to form an id that is unique // among all procids joined to the server's session. // _Tt_string _Tt_s_mp:: alloc_procid_key() { int key = _next_procid_key++; _Tt_string result(_tt_base64_encode((unsigned long)key)); result = result.cat(".").cat(_tt_base64_encode((unsigned long)_mp_start_time)); return(result); } // // This is the main loop of the message server. This method is // responsible for servicing events such as rpc requests, disconnect // signals from procids, connection requests for unix socket rpc // requests, and events generated from our desktop connection (if // applicable). The main loop checks for the values of exit_main_loop // and of fin and fout (see comments in bin/ttsession/mp_server.C) // before invoking the _Tt_rpc_server::run method which will block on // rpc requests and polling on the supplied list of file descriptors // supplied in the _active_fds list. // void _Tt_s_mp:: main_loop() { _Tt_int_rec_list_cursor fds; _Tt_string_list_cursor fd_procid; _Tt_s_procid_ptr sp; while (! exit_main_loop && (fin == fout)) { switch (initial_s_session->_rpc_server->run_until(&exit_main_loop, _min_timeout, _active_fds)) { case _TT_RPCSRV_ERR: case _TT_RPCSRV_OK: break; case _TT_RPCSRV_TMOUT: break; case _TT_RPCSRV_FDERR: // this error code is returned if any of the // file descriptors in _active_fds has input // pending. In the case of file descriptors // associated with procid signalling channels // this means that the connection to them was // lost, in the case of the file descriptor // being associated with the desktop // connection this means that there was an // event generated by the desktop server. // Otherwise, if the file descriptor was the // one associated with the unix socket rpc // socket then this is a new connection // request. // // Note that the _Tt_rpc_server::run method // will indicate which fd was active by // negating its value thus the test for being // less than zero indicates that was the fd we // were looking for. fds.reset(_active_fds); fd_procid.reset(_active_fds_procids); while (fds.next() && fd_procid.next()) { if (fds->val < 0) { if (*fd_procid == "X") { // X event came in fds->val *= -1; if (initial_session->desktop_event_callback()==-1) { exit_main_loop = 1; xfd = -2; break; } #ifdef OPT_UNIX_SOCKET_RPC } else if ((0 - fds->val)==unix_fd) { // connection request // for local rpc transport. fds->val = unix_fd; initial_s_session->u_rpc_init(); #endif // OPT_UNIX_SOCKET_RPC } else { // signalling channel // became active for // some procid. This // means the // connection was lost // since the // signalling channel // is a write-only // connection. if (active_procs->lookup(*fd_procid,sp)) { // Before cleaning up, // send any on_exit // messages. sp->send_on_exit_messages(); sp->set_active(-1); active_procs->remove(sp->id()); } fds.remove(); fd_procid.remove(); } } } break; default: break; } } } // // Returns 1 if there exist file-scope patterns for the given pathname. // Uses the two parallel lists _file_scope_paths and // _file_scope_refcounts which form a logical list of records of file // pathnames to number of patterns registered. // // XXX: Use of parallel lists is confusing. Recoding using a single list // of records would improve readability. // int _Tt_s_mp:: in_file_scope(const _Tt_string &f) { if (_file_scope_refcounts.is_null()) { return(0); } _Tt_int_rec_list_cursor refcounts(_file_scope_refcounts); _Tt_string_list_cursor paths(_file_scope_paths); while (refcounts.next() && paths.next()) { if (*paths == f) { return(1); } } return(0); } // // Adds (subtracts) number of file-scope patterns registered for the // given pathname if add_scope is 1 (0). If the refcount of patterns goes // to 0 then the pathname is removed from the _file_scope_refcounts and // _file_scope_paths lists. See comment for _Tt_s_mp::in_file_scope for // an explanation of these lists. // void _Tt_s_mp:: mod_file_scope(const _Tt_string &f, int add_scope) { if (_file_scope_refcounts.is_null()) { _file_scope_refcounts = new _Tt_int_rec_list(); _file_scope_paths = new _Tt_string_list(); } _Tt_int_rec_list_cursor refcounts(_file_scope_refcounts); _Tt_string_list_cursor paths(_file_scope_paths); while (refcounts.next() && paths.next()) { if (*paths == f) { refcounts->val += (add_scope ? 1 : -1); if (refcounts->val == 0) { refcounts.remove(); paths.remove(); } return; } } if (add_scope) { _file_scope_refcounts->push(new _Tt_int_rec(1)); _file_scope_paths->push(f); } } Tt_status _Tt_s_mp::add_procid( _Tt_s_procid_ptr &proc ) { _active_fds->push(new _Tt_int_rec(-1)); _active_fds_procids->push(proc->id()); active_procs->insert(proc); _last_proc_hit = proc; return TT_OK; } // // Called in response to a client invoking the tt_close api call. This // method will deallocate any resources that this procid holds and will // remove the procid reference from any global data-structures. // Tt_status _Tt_s_mp:: s_remove_procid(_Tt_s_procid &proc) { proc.cancel_on_exit_messages(); // de-activate this procid proc.set_active(0); // remove from list of active procs active_procs->remove(proc.id()); return(TT_OK); }