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 //%% (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 //%% $XConsortium: mp_s_mp.C /main/6 1996/05/09 20:30:03 drk $
30 * @(#)mp_s_mp.C 1.42 95/09/18
32 * Copyright 1990,1993 Sun Microsystems, Inc. All rights reserved.
34 #include "mp_s_global.h"
37 #include "mp_s_procid.h"
38 #include "mp_self_procid.h"
39 #include "mp_rpc_implement.h"
40 #include "mp_rpc_server.h"
41 #include "mp_s_session.h"
42 #include "mp/mp_xdr_functions.h"
45 #include "mp/mp_arg.h"
46 #include "mp_s_pattern.h"
47 #include "mp_signature.h"
48 #include "mp_s_message.h"
49 #include "mp_typedb.h"
50 #include "mp/mp_file.h"
51 #include "util/tt_global_env.h"
52 #include "util/tt_base64.h"
53 #include "util/tt_host.h"
54 #include "util/tt_port.h"
56 #include <sys/resource.h>
60 #include <util/tt_gettext.h>
62 #include <sys/types.h>
66 // global pointer to the _Tt_s_mp object. There should only be one
67 // instance of this object.
68 _Tt_s_mp *_tt_s_mp = (_Tt_s_mp *)0;
73 _flags |= (1<<_TT_MP_IN_SERVER);
74 initial_session = initial_s_session = new _Tt_s_session;
76 _active_fds = new _Tt_int_rec_list();
77 _active_fds_procids = new _Tt_string_list();
78 _mp_start_time = (int)time(0);
84 max_active_messages = 2000;
87 ptable = new _Tt_ptype_table(_tt_ptype_ptid, 50);
88 otable = new _Tt_otype_table(_tt_otype_otid, 50);
89 sigs = new _Tt_sigs_by_op_table(_tt_sigs_by_op_op, 250);
90 opful_pats = new _Tt_patlist_table(_tt_patlist_op, 250);
91 opless_pats = new _Tt_pattern_list();
92 active_procs = new _Tt_s_procid_table(_tt_procid_id, 250);
94 when_last_observer_registered = 1;
95 update_args.message = new _Tt_s_message();
96 _self = (_Tt_s_procid *)new _Tt_self_procid();
104 // Emulate a tt_close() for _self.
105 // XXX Commented out because currently s_remove_procid() neither:
106 // - deletes this session from the interest list, nor
107 // - sends an on_exit message (since _self submits none)
109 // if (! _self.is_null()) {
110 // s_remove_procid( _self );
115 // Initializes a _Tt_s_mp object. This entails initializing
116 // the initial session which is the default session that other clients
117 // connect to. This method will also initialize the global _Tt_s_mp
118 // object if the session is initialized. This is important since the
119 // _Tt_s_mp::init method assumes that the session has been initialized.
120 // XXX: the existence of the s_init and init methods is left over from
121 // an earlier incomplete split of _Tt_mp into server and client subclasses.
122 // Really, there should be just one virtual init method.
131 // initialize "initial" session which is the server session in
132 // server mode and the "default" session in client mode.
134 status = initial_s_session->s_init();
135 if (status == TT_OK) {
147 // Initializes the _Tt_s_mp object. This entails putting the two special
148 // "pseudo-procids" into the _active_fds and _active_fds_procids lists
149 // (see comment for _Tt_s_mp::find_proc)
155 // put in fd for X connection
156 _active_fds->push(new _Tt_int_rec(xfd));
157 _active_fds_procids->push(_Tt_string("X"));
159 #ifdef OPT_UNIX_SOCKET_RPC
160 /* XXX: UNIX_SOCKET */
162 // put in fd for local rpc connections
163 _active_fds->push(new _Tt_int_rec(unix_fd));
164 _active_fds_procids->push(_Tt_string("U"));
166 /* XXX: UNIX_SOCKET */
167 #endif // OPT_UNIX_SOCKET_RPC
173 // Initialize our _Tt_self_procid and register patterns for the
174 // messages we are interested in.
177 _Tt_s_mp::init_self()
179 // Grab the global mutex to lock out other threads
181 _tt_global->grab_mutex();
183 // Increment the counter for the number of RPC calls
184 // handled during the life of this process. This has
185 // to be done here since init_self() ultimately calls
186 // something which calls updateFileSystemEntries(),
187 // yet it's not done through an API or RPC call. Thus,
188 // this line here is somewhat of a bandaid. XXX
189 _tt_global->event_counter++;
191 // Use the lame do-loop hack to avoid repeating the drop_mutex
192 // code after every possible failure...
194 Tt_status status = TT_OK;;
198 // tt_open(), tt_fd()
200 Tt_status status = _self->init();
201 if (status != TT_OK) {
204 status = add_procid( _self );
205 if (status != TT_OK) {
209 status = _handle_Session_Trace();
210 if (status != TT_OK) {
213 status = _observe_Saved();
214 if (status != TT_OK) {
219 _tt_global->drop_mutex();
225 _Tt_s_mp::_handle_Session_Trace()
228 // tt_pattern_create(), tt_pattern_category_set(),
229 // tt_pattern_scope_add(), tt_pattern_session_add(),
230 // tt_pattern_op_add(), tt_pattern_arg_add()
232 _Tt_s_pattern_ptr pat = _Tt_self_procid::s_pattern_create();
233 Tt_status status = pat->set_category( TT_HANDLE );
234 if (status != TT_OK) {
237 status = pat->add_message_class( TT_REQUEST );
238 if (status != TT_OK) {
241 status = pat->add_scope( TT_SESSION );
242 if (status != TT_OK) {
245 status = pat->add_session( _tt_s_mp->initial_s_session->address_string() );
246 if (status != TT_OK) {
249 status = pat->add_op( "Session_Trace" );
250 if (status != TT_OK) {
253 _Tt_arg_ptr arg = new _Tt_arg( TT_IN, "string" );
254 status = pat->add_arg( arg );
255 if (status != TT_OK) {
258 pat->server_callback = _Tt_self_procid::handle_Session_Trace;
260 // tt_pattern_register()
262 status = _self->add_pattern( pat );
263 if (status != TT_OK) {
269 // _Tt_s_procid_ptr s_proc = (_Tt_s_procid *)_self.c_pointer();
270 // status = _tt_s_mp->initial_s_session->s_join( s_proc );
271 // if (status != TT_OK) {
278 _Tt_s_mp::_observe_Saved()
281 // tt_pattern_create(), tt_pattern_category_set(),
282 // tt_pattern_scope_add(), tt_pattern_op_add(), tt_pattern_arg_add()
284 _Tt_s_pattern_ptr pat = _Tt_self_procid::s_pattern_create();
285 Tt_status status = pat->set_category( TT_OBSERVE );
286 if (status != TT_OK) {
289 status = pat->add_message_class( TT_NOTICE );
290 if (status != TT_OK) {
293 status = pat->add_scope( TT_BOTH );
294 if (status != TT_OK) {
297 status = pat->add_session( _tt_s_mp->initial_s_session->address_string() );
298 if (status != TT_OK) {
301 _Tt_string_list_ptr ttpath = _Tt_typedb::tt_path();
302 if (ttpath.is_null()) {
305 _Tt_string_list_cursor pathC( ttpath );
306 while (pathC.next()) {
307 status = pat->add_netfile( *pathC, 1 );
308 if (status != TT_OK) {
312 status = pat->add_op( "Saved" );
313 if (status != TT_OK) {
316 _Tt_arg_ptr arg = new _Tt_arg( TT_IN, "File" );
317 status = pat->add_arg( arg );
318 if (status != TT_OK) {
321 pat->server_callback = _Tt_self_procid::observe_Saved;
323 // tt_pattern_register()
325 status = _self->add_pattern( pat );
326 if (status != TT_OK) {
329 status = pat->join_files(_tt_s_mp->initial_s_session->process_tree_id());
330 if (status != TT_OK) {
338 // Installs a list of signatures. Each signature is installed in a table
339 // called sigs_table that maps op names to lists of signatures with that
340 // same opname. This cuts down on the number of signatures that need to
341 // be examined to dispatch a message.
344 install_signatures(_Tt_signature_list_ptr &s)
346 _Tt_signature_list_cursor sigC(s);
347 _Tt_sigs_by_op_ptr sigs_byop;
349 while (sigC.next()) {
350 sigs_byop = (_Tt_sigs_by_op *)0;
351 if (! sigs->lookup(sigC->op(),sigs_byop)) {
352 sigs_byop = new _Tt_sigs_by_op(sigC->op());
353 sigs_byop->sigs = new _Tt_signature_list();
354 sigs->insert(sigs_byop);
356 sigs_byop->sigs->push(*sigC);
361 remove_signatures(const _Tt_ptype &ptype)
363 _Tt_sigs_by_op_table_cursor sigs_byopC(sigs);
365 while (sigs_byopC.next()) {
366 _Tt_signature_list_cursor sigC(sigs_byopC->sigs);
367 while (sigC.next()) {
368 if (sigC->ptid() == ptype.ptid()) {
376 remove_signatures(const _Tt_otype &otype)
378 _Tt_sigs_by_op_table_cursor sigs_byopC(sigs);
380 while (sigs_byopC.next()) {
381 _Tt_signature_list_cursor sigC(sigs_byopC->sigs);
382 while (sigC.next()) {
383 if (sigC->otid() == otype.otid()) {
391 // Iterates through the given table of ptypes and installs all the
392 // signatures contained in each ptype.
395 install_ptable(_Tt_ptype_table_ptr &p)
397 _Tt_ptype_table_cursor ptypes;
400 ptypes.reset(ptable);
401 while (ptypes.next()) {
402 remove_signatures(**ptypes);
403 install_signatures(ptypes->hsigs());
404 install_signatures(ptypes->osigs());
410 // Iterates through the given table of otypes and installs all the
411 // signatures contained in each otype.
414 install_otable(_Tt_otype_table_ptr &o)
416 _Tt_otype_table_cursor otypes;
419 otypes.reset(otable);
420 while (otypes.next()) {
421 remove_signatures(**otypes);
422 install_signatures(otypes->hsigs());
423 install_signatures(otypes->osigs());
429 // It is important that the mp contain exactly one object for each
430 // procid that is registered because there is state that is contained
431 // in this instance of a procid that shouldn't be duplicated. This
432 // method takes a generic \(neither server nor client\) procid and
434 // the _Tt_s_procid object that the mp has for the given id or else creates
435 // a new object if create_ifnot is equal to 1. When a procid is
436 // initialized, it's signalling fd is added to two parallel lists,
437 // _active_fds and _active_fds_procids. _active_fds contains a list
438 // of fds whereas _active_fds_procids contains a list of procid ids.
439 // Each position in the two lists corresponds to each other. This is
440 // equivalent to having a single list of records where each record
441 // contains an fd and a procid id. These lists are used by the
442 // _Tt_s_mp::main_loop to poll all the fds in the list and match them
443 // with their respective procids.
445 // The _active_fds_procids list can contain two "pseudo-procids" that
446 // are used to keep track of some special fds. These can be "U" for
447 // the special unix socket fd (used if OPT_UNIX_SOCKET_RPC is defined)
448 // that clients use to establish a unix socket rpc connection, and "X"
449 // for the fd that represents the connection to our desktop session.
450 // See _Tt_s_mp::main_loop to see how they are used.
452 // XXX: Replacing the parallel list structure with a list of records
453 // would increase readability.
457 const _Tt_procid_ptr &procid,
458 _Tt_s_procid_ptr &proc_returned,
464 if (! procid.is_null()) {
465 if (! _last_proc_hit.is_null()) {
466 if (_last_proc_hit->is_equal(procid)) {
467 proc_returned = _last_proc_hit;
471 active_procs->lookup(procid->id(),sp);
479 sp = new _Tt_s_procid(procid);
481 /* procid not found, add it to list */
482 if (sp->init() == TT_OK) {
496 // Called in order to generate a unique key for a new procid. This key is
497 // prepended to a procid's session in order to form an id that is unique
498 // among all procids joined to the server's session.
500 _Tt_string _Tt_s_mp::
503 int key = _next_procid_key++;
504 _Tt_string result(_tt_base64_encode((unsigned long)key));
506 result = result.cat(".").cat(_tt_base64_encode((unsigned long)_mp_start_time));
512 // This is the main loop of the message server. This method is
513 // responsible for servicing events such as rpc requests, disconnect
514 // signals from procids, connection requests for unix socket rpc
515 // requests, and events generated from our desktop connection (if
516 // applicable). The main loop checks for the values of exit_main_loop
517 // and of fin and fout (see comments in bin/ttsession/mp_server.C)
518 // before invoking the _Tt_rpc_server::run method which will block on
519 // rpc requests and polling on the supplied list of file descriptors
520 // supplied in the _active_fds list.
525 _Tt_int_rec_list_cursor fds;
526 _Tt_string_list_cursor fd_procid;
529 while (! exit_main_loop && (fin == fout)) {
530 switch (initial_s_session->_rpc_server->run_until(&exit_main_loop,
536 case _TT_RPCSRV_TMOUT:
538 case _TT_RPCSRV_FDERR:
539 // this error code is returned if any of the
540 // file descriptors in _active_fds has input
541 // pending. In the case of file descriptors
542 // associated with procid signalling channels
543 // this means that the connection to them was
544 // lost, in the case of the file descriptor
545 // being associated with the desktop
546 // connection this means that there was an
547 // event generated by the desktop server.
548 // Otherwise, if the file descriptor was the
549 // one associated with the unix socket rpc
550 // socket then this is a new connection
553 // Note that the _Tt_rpc_server::run method
554 // will indicate which fd was active by
555 // negating its value thus the test for being
556 // less than zero indicates that was the fd we
559 fds.reset(_active_fds);
560 fd_procid.reset(_active_fds_procids);
561 while (fds.next() && fd_procid.next()) {
563 if (*fd_procid == "X") {
566 if (initial_session->desktop_event_callback()==-1) {
571 #ifdef OPT_UNIX_SOCKET_RPC
572 } else if ((0 - fds->val)==unix_fd) {
573 // connection request
574 // for local rpc transport.
577 initial_s_session->u_rpc_init();
578 #endif // OPT_UNIX_SOCKET_RPC
580 // signalling channel
584 // connection was lost
586 // signalling channel
589 if (active_procs->lookup(*fd_procid,sp)) {
590 // Before cleaning up,
593 sp->send_on_exit_messages();
595 active_procs->remove(sp->id());
611 // Returns 1 if there exist file-scope patterns for the given pathname.
612 // Uses the two parallel lists _file_scope_paths and
613 // _file_scope_refcounts which form a logical list of records of file
614 // pathnames to number of patterns registered.
616 // XXX: Use of parallel lists is confusing. Recoding using a single list
617 // of records would improve readability.
620 in_file_scope(const _Tt_string &f)
622 if (_file_scope_refcounts.is_null()) {
625 _Tt_int_rec_list_cursor refcounts(_file_scope_refcounts);
626 _Tt_string_list_cursor paths(_file_scope_paths);
628 while (refcounts.next() && paths.next()) {
639 // Adds (subtracts) number of file-scope patterns registered for the
640 // given pathname if add_scope is 1 (0). If the refcount of patterns goes
641 // to 0 then the pathname is removed from the _file_scope_refcounts and
642 // _file_scope_paths lists. See comment for _Tt_s_mp::in_file_scope for
643 // an explanation of these lists.
646 mod_file_scope(const _Tt_string &f, int add_scope)
648 if (_file_scope_refcounts.is_null()) {
649 _file_scope_refcounts = new _Tt_int_rec_list();
650 _file_scope_paths = new _Tt_string_list();
653 _Tt_int_rec_list_cursor refcounts(_file_scope_refcounts);
654 _Tt_string_list_cursor paths(_file_scope_paths);
656 while (refcounts.next() && paths.next()) {
658 refcounts->val += (add_scope ? 1 : -1);
659 if (refcounts->val == 0) {
667 _file_scope_refcounts->push(new _Tt_int_rec(1));
668 _file_scope_paths->push(f);
674 _Tt_s_mp::add_procid(
675 _Tt_s_procid_ptr &proc
678 _active_fds->push(new _Tt_int_rec(-1));
679 _active_fds_procids->push(proc->id());
680 active_procs->insert(proc);
681 _last_proc_hit = proc;
687 // Called in response to a client invoking the tt_close api call. This
688 // method will deallocate any resources that this procid holds and will
689 // remove the procid reference from any global data-structures.
692 s_remove_procid(_Tt_s_procid &proc)
694 proc.cancel_on_exit_messages();
695 // de-activate this procid
697 // remove from list of active procs
698 active_procs->remove(proc.id());