Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / lib / tt / slib / mp_s_mp.C
1 //%%  (c) Copyright 1993, 1994 Hewlett-Packard Company                  
2 //%%  (c) Copyright 1993, 1994 International Business Machines Corp.    
3 //%%  (c) Copyright 1993, 1994 Sun Microsystems, Inc.                   
4 //%%  (c) Copyright 1993, 1994 Novell, Inc.                             
5 //%%  $XConsortium: mp_s_mp.C /main/6 1996/05/09 20:30:03 drk $                                                         
6 /*
7  *
8  * @(#)mp_s_mp.C        1.42    95/09/18
9  *
10  * Copyright 1990,1993 Sun Microsystems, Inc.  All rights reserved.
11  */
12 #include "mp_s_global.h"
13 #include "mp_s_mp.h"
14 #include "mp/mp_mp.h"
15 #include "mp_s_procid.h"
16 #include "mp_self_procid.h"
17 #include "mp_rpc_implement.h"
18 #include "mp_rpc_server.h"
19 #include "mp_s_session.h"
20 #include "mp/mp_xdr_functions.h"
21 #include "mp_ptype.h"
22 #include "mp_otype.h"
23 #include "mp/mp_arg.h"
24 #include "mp_s_pattern.h"
25 #include "mp_signature.h"
26 #include "mp_s_message.h"
27 #include "mp_typedb.h"
28 #include "mp/mp_file.h"
29 #include "util/tt_global_env.h"
30 #include "util/tt_base64.h"
31 #include "util/tt_host.h"
32 #include "util/tt_port.h"
33 #include <errno.h>
34 #include <sys/resource.h>
35 #include <time.h>
36 #include <unistd.h>
37 #include <nl_types.h>
38 #include <util/tt_gettext.h>
39 #include <tt_const.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <fcntl.h>
43
44 // global pointer to the _Tt_s_mp object. There should only be one
45 // instance of this object.
46 _Tt_s_mp *_tt_s_mp = (_Tt_s_mp *)0;
47
48 _Tt_s_mp::
49 _Tt_s_mp() : _Tt_mp()
50 {
51         _flags |= (1<<_TT_MP_IN_SERVER);
52         initial_session = initial_s_session = new _Tt_s_session;
53         
54         _active_fds = new _Tt_int_rec_list();
55         _active_fds_procids = new _Tt_string_list();
56         _mp_start_time = (int)time(0);
57
58         _min_timeout = -1;
59         _next_procid_key = 0;
60         xfd = -1;
61         exit_main_loop = 0;
62         max_active_messages = 2000;
63         fin = 0;
64         fout = 0;
65         ptable = new _Tt_ptype_table(_tt_ptype_ptid, 50);
66         otable = new _Tt_otype_table(_tt_otype_otid, 50);
67         sigs = new _Tt_sigs_by_op_table(_tt_sigs_by_op_op, 250);
68         opful_pats = new _Tt_patlist_table(_tt_patlist_op, 250);
69         opless_pats = new _Tt_pattern_list();
70         active_procs = new _Tt_s_procid_table(_tt_procid_id, 250);
71         now = 1;
72         when_last_observer_registered = 1;
73         update_args.message = new _Tt_s_message();
74         _self = (_Tt_s_procid *)new _Tt_self_procid();
75 }
76
77
78 _Tt_s_mp::
79 ~_Tt_s_mp()
80 {
81         //
82         // Emulate a tt_close() for _self.
83         // XXX Commented out because currently s_remove_procid() neither:
84         // - deletes this session from the interest list, nor
85         // - sends an on_exit message (since _self submits none)
86         //
87         // if (! _self.is_null()) {
88         //      s_remove_procid( _self );
89         // }
90 }
91
92 // 
93 // Initializes a _Tt_s_mp object. This entails initializing
94 // the initial session which is the default session that other clients
95 // connect to. This method will also initialize the global _Tt_s_mp
96 // object if the session is initialized. This is important since the
97 // _Tt_s_mp::init method assumes that the session has been initialized.
98 // XXX: the existence of the s_init and init methods is left over from
99 // an earlier incomplete split of _Tt_mp into server and client subclasses.
100 // Really, there should be just one virtual init method.
101 // 
102 Tt_status _Tt_s_mp::
103 s_init()
104 {
105         Tt_status               status;
106
107         active_messages = 0;
108
109         // initialize "initial" session which is the server session in
110         // server mode and the "default" session in client mode.
111         
112         status = initial_s_session->s_init();
113         if (status == TT_OK) {
114                 status = init();
115         }
116
117         return(status);
118 }
119
120
121
122
123
124 // 
125 // Initializes the _Tt_s_mp object. This entails putting the two special
126 // "pseudo-procids" into the _active_fds and _active_fds_procids lists
127 // (see comment for _Tt_s_mp::find_proc)
128 // 
129 Tt_status _Tt_s_mp::
130 init()
131 {
132         if (xfd != -1) {
133                 // put in fd for X connection
134                 _active_fds->push(new _Tt_int_rec(xfd));
135                 _active_fds_procids->push(_Tt_string("X"));
136         }
137 #ifdef OPT_UNIX_SOCKET_RPC
138         /* XXX: UNIX_SOCKET */          
139         if (unix_fd != -1) {
140                 // put in fd for local rpc connections
141                 _active_fds->push(new _Tt_int_rec(unix_fd));
142                 _active_fds_procids->push(_Tt_string("U"));
143         }               
144         /* XXX: UNIX_SOCKET */          
145 #endif  // OPT_UNIX_SOCKET_RPC
146
147         return(TT_OK);
148 }
149
150 //
151 // Initialize our _Tt_self_procid and register patterns for the
152 // messages we are interested in.
153 //
154 Tt_status
155 _Tt_s_mp::init_self()
156 {
157         // Grab the global mutex to lock out other threads
158
159         _tt_global->grab_mutex();
160
161         // Increment the counter for the number of RPC calls
162         // handled during the life of this process.  This has
163         // to be done here since init_self() ultimately calls
164         // something which calls updateFileSystemEntries(),
165         // yet it's not done through an API or RPC call.  Thus,
166         // this line here is somewhat of a bandaid. XXX
167         _tt_global->event_counter++;
168
169         // Use the lame do-loop hack to avoid repeating the drop_mutex
170         // code after every possible failure...
171         
172         Tt_status status = TT_OK;;
173
174         do {
175                 //
176                 // tt_open(), tt_fd()
177                 //
178                 Tt_status status = _self->init();
179                 if (status != TT_OK) {
180                         break;
181                 }
182                 status = add_procid( _self );
183                 if (status != TT_OK) {
184                         break;
185                 }
186                 _self->set_fd();
187                 status = _handle_Session_Trace();
188                 if (status != TT_OK) {
189                         break;
190                 }
191                 status = _observe_Saved();
192                 if (status != TT_OK) {
193                         break;
194                 }
195         } while (0);
196
197         _tt_global->drop_mutex();
198
199         return TT_OK;
200 }
201
202 Tt_status
203 _Tt_s_mp::_handle_Session_Trace()
204 {
205         //
206         // tt_pattern_create(), tt_pattern_category_set(),
207         // tt_pattern_scope_add(), tt_pattern_session_add(),
208         // tt_pattern_op_add(), tt_pattern_arg_add()
209         //
210         _Tt_s_pattern_ptr pat = _Tt_self_procid::s_pattern_create();
211         Tt_status status = pat->set_category( TT_HANDLE );
212         if (status != TT_OK) {
213                 return status;
214         }
215         status = pat->add_message_class( TT_REQUEST );
216         if (status != TT_OK) {
217                 return status;
218         }
219         status = pat->add_scope( TT_SESSION );
220         if (status != TT_OK) {
221                 return status;
222         }
223         status = pat->add_session( _tt_s_mp->initial_s_session->address_string() );
224         if (status != TT_OK) {
225                 return status;
226         }
227         status = pat->add_op( "Session_Trace" );
228         if (status != TT_OK) {
229                 return status;
230         }
231         _Tt_arg_ptr arg = new _Tt_arg( TT_IN, "string" );
232         status = pat->add_arg( arg );
233         if (status != TT_OK) {
234                 return status;
235         }
236         pat->server_callback = _Tt_self_procid::handle_Session_Trace;
237         //
238         // tt_pattern_register()
239         //
240         status = _self->add_pattern( pat );
241         if (status != TT_OK) {
242                 return status;
243         }
244         //
245         // tt_session_join()
246         //
247         // _Tt_s_procid_ptr s_proc = (_Tt_s_procid *)_self.c_pointer();
248         // status = _tt_s_mp->initial_s_session->s_join( s_proc );
249         // if (status != TT_OK) {
250         //      return status;
251         // }
252         return TT_OK;
253 }
254
255 Tt_status
256 _Tt_s_mp::_observe_Saved()
257 {
258         //
259         // tt_pattern_create(), tt_pattern_category_set(),
260         // tt_pattern_scope_add(), tt_pattern_op_add(), tt_pattern_arg_add()
261         //
262         _Tt_s_pattern_ptr pat = _Tt_self_procid::s_pattern_create();
263         Tt_status status = pat->set_category( TT_OBSERVE );
264         if (status != TT_OK) {
265                 return status;
266         }
267         status = pat->add_message_class( TT_NOTICE );
268         if (status != TT_OK) {
269                 return status;
270         }
271         status = pat->add_scope( TT_BOTH );
272         if (status != TT_OK) {
273                 return status;
274         }
275         status = pat->add_session( _tt_s_mp->initial_s_session->address_string() );
276         if (status != TT_OK) {
277                 return status;
278         }
279         _Tt_string_list_ptr ttpath = _Tt_typedb::tt_path();
280         if (ttpath.is_null()) {
281                 return TT_ERR_NOMEM;
282         }
283         _Tt_string_list_cursor pathC( ttpath );
284         while (pathC.next()) {
285                 status = pat->add_netfile( *pathC, 1 );
286                 if (status != TT_OK) {
287                         return status;
288                 }
289         }
290         status = pat->add_op( "Saved" );
291         if (status != TT_OK) {
292                 return status;
293         }
294         _Tt_arg_ptr arg = new _Tt_arg( TT_IN, "File" );
295         status = pat->add_arg( arg );
296         if (status != TT_OK) {
297                 return status;
298         }
299         pat->server_callback = _Tt_self_procid::observe_Saved;
300         //
301         // tt_pattern_register()
302         //
303         status = _self->add_pattern( pat );
304         if (status != TT_OK) {
305                 return status;
306         }
307         status = pat->join_files(_tt_s_mp->initial_s_session->process_tree_id());
308         if (status != TT_OK) {
309                 return status;
310         }
311         return TT_OK;
312 }
313
314
315 // 
316 // Installs a list of signatures. Each signature is installed in a table
317 // called sigs_table that maps op names to lists of signatures with that
318 // same opname. This cuts down on the number of signatures that need to
319 // be examined to dispatch a message.
320 // 
321 void _Tt_s_mp::
322 install_signatures(_Tt_signature_list_ptr &s)
323 {
324         _Tt_signature_list_cursor       sigC(s);
325         _Tt_sigs_by_op_ptr              sigs_byop;
326         
327         while (sigC.next()) {
328                 sigs_byop = (_Tt_sigs_by_op *)0;
329                 if (! sigs->lookup(sigC->op(),sigs_byop)) {
330                         sigs_byop = new _Tt_sigs_by_op(sigC->op());
331                         sigs_byop->sigs = new _Tt_signature_list();
332                         sigs->insert(sigs_byop);
333                 } 
334                 sigs_byop->sigs->push(*sigC);
335         }
336 }
337
338 void _Tt_s_mp::
339 remove_signatures(const _Tt_ptype &ptype)
340 {
341         _Tt_sigs_by_op_table_cursor     sigs_byopC(sigs);
342         
343         while (sigs_byopC.next()) {
344                 _Tt_signature_list_cursor sigC(sigs_byopC->sigs);
345                 while (sigC.next()) {
346                         if (sigC->ptid() == ptype.ptid()) {
347                                 sigC.remove();
348                         }
349                 }
350         }
351 }
352
353 void _Tt_s_mp::
354 remove_signatures(const _Tt_otype &otype)
355 {
356         _Tt_sigs_by_op_table_cursor     sigs_byopC(sigs);
357         
358         while (sigs_byopC.next()) {
359                 _Tt_signature_list_cursor sigC(sigs_byopC->sigs);
360                 while (sigC.next()) {
361                         if (sigC->otid() == otype.otid()) {
362                                 sigC.remove();
363                         }
364                 }
365         }
366 }
367
368 // 
369 // Iterates through the given table of ptypes and installs all the
370 // signatures contained in each ptype.
371 // 
372 void _Tt_s_mp::
373 install_ptable(_Tt_ptype_table_ptr &p)
374 {
375         _Tt_ptype_table_cursor  ptypes;
376         
377         ptable = p;
378         ptypes.reset(ptable);
379         while (ptypes.next()) {
380                 remove_signatures(**ptypes);
381                 install_signatures(ptypes->hsigs());
382                 install_signatures(ptypes->osigs());
383         }
384 }
385
386
387 // 
388 // Iterates through the given table of otypes and installs all the
389 // signatures contained in each otype.
390 // 
391 void _Tt_s_mp::
392 install_otable(_Tt_otype_table_ptr &o)
393 {
394         _Tt_otype_table_cursor  otypes;
395         
396         otable = o;
397         otypes.reset(otable);
398         while (otypes.next()) {
399                 remove_signatures(**otypes);
400                 install_signatures(otypes->hsigs());
401                 install_signatures(otypes->osigs());
402         }
403 }
404
405
406 // 
407 // It is important that the mp contain exactly one object for each
408 // procid that is registered because there is state that is contained
409 // in this instance of a procid that shouldn't be duplicated. This
410 // method takes a generic \(neither server nor client\) procid and
411 // returns either
412 // the _Tt_s_procid object that the mp has for the given id or else creates
413 // a new object if create_ifnot is equal to 1. When a procid is
414 // initialized, it's signalling fd is added to two parallel lists,
415 // _active_fds and _active_fds_procids.  _active_fds contains a list
416 // of fds whereas _active_fds_procids contains a list of procid ids.
417 // Each position in the two lists corresponds to each other. This is
418 // equivalent to having a single list of records where each record
419 // contains an fd and a procid id. These lists are used by the
420 // _Tt_s_mp::main_loop to poll all the fds in the list and match them
421 // with their respective procids. 
422 //
423 // The _active_fds_procids list can contain two "pseudo-procids" that
424 // are used to keep track of some special fds. These can be "U" for
425 // the special unix socket fd (used if OPT_UNIX_SOCKET_RPC is defined)
426 // that clients use to establish a unix socket rpc connection, and "X"
427 // for the fd that represents the connection to our desktop session. 
428 // See _Tt_s_mp::main_loop to see how they are used.
429 // 
430 //  XXX: Replacing the parallel list structure with a list of records
431 //  would increase readability.
432 // 
433 int
434 _Tt_s_mp::find_proc(
435                     const _Tt_procid_ptr   &procid,
436                     _Tt_s_procid_ptr &proc_returned,
437                     int               create_ifnot
438                     )
439 {
440         _Tt_s_procid_ptr        sp;
441         
442         if (! procid.is_null()) {
443                 if (! _last_proc_hit.is_null()) {
444                         if (_last_proc_hit->is_equal(procid)) {
445                                 proc_returned = _last_proc_hit;
446                                 return(1);
447                         }
448                 }
449                 active_procs->lookup(procid->id(),sp);
450         }
451         if (!sp.is_null()) {
452                 _last_proc_hit = sp;
453                 proc_returned = sp;
454                 return(1);
455         }
456         if (create_ifnot) {
457                 sp = new _Tt_s_procid(procid);
458                 
459                 /* procid not found, add it to list */
460                 if (sp->init() == TT_OK) {
461                         proc_returned = sp;
462                         add_procid( sp );
463                         return(1);
464                 } else {
465                         return(0);
466                 }
467         } else {
468                 return(0);
469         }
470 }
471
472
473 // 
474 // Called in order to generate a unique key for a new procid. This key is
475 // prepended to a procid's session in order to form an id that is unique
476 // among all procids joined to the server's session.
477 // 
478 _Tt_string _Tt_s_mp::
479 alloc_procid_key()
480 {
481         int             key = _next_procid_key++;
482         _Tt_string      result(_tt_base64_encode((unsigned long)key));
483         
484         result = result.cat(".").cat(_tt_base64_encode((unsigned long)_mp_start_time));
485         return(result);
486 }
487
488
489 // 
490 // This is the main loop of the message server. This method is
491 // responsible for servicing events such as rpc requests, disconnect
492 // signals from procids, connection requests for unix socket rpc
493 // requests, and events generated from our desktop connection (if
494 // applicable).  The main loop checks for the values of exit_main_loop
495 // and of fin and fout (see comments in bin/ttsession/mp_server.C)
496 // before invoking the _Tt_rpc_server::run method which will block on
497 // rpc requests and polling on the supplied list of file descriptors
498 // supplied in the _active_fds list.
499 // 
500 void _Tt_s_mp::
501 main_loop()
502 {
503         _Tt_int_rec_list_cursor         fds;
504         _Tt_string_list_cursor          fd_procid;
505         _Tt_s_procid_ptr                sp;
506
507         while (! exit_main_loop && (fin == fout)) {
508                 switch (initial_s_session->_rpc_server->run_until(&exit_main_loop,
509                                                                   _min_timeout,
510                                                                   _active_fds)) {
511                     case _TT_RPCSRV_ERR:
512                     case _TT_RPCSRV_OK:
513                         break;
514                       case _TT_RPCSRV_TMOUT:
515                         break;
516                     case _TT_RPCSRV_FDERR:
517                         // this error code is returned if any of the
518                         // file descriptors in _active_fds has input
519                         // pending. In the case of file descriptors
520                         // associated with procid signalling channels
521                         // this means that the connection to them was
522                         // lost, in the case of the file descriptor
523                         // being associated with the desktop
524                         // connection this means that there was an
525                         // event generated by the desktop server.
526                         // Otherwise, if the file descriptor was the
527                         // one associated with the unix socket rpc
528                         // socket then this is a new connection
529                         // request.
530                         //
531                         // Note that the _Tt_rpc_server::run method
532                         // will indicate which fd was active by
533                         // negating its value thus the test for being
534                         // less than zero indicates that was the fd we
535                         // were looking for.
536
537                         fds.reset(_active_fds);
538                         fd_procid.reset(_active_fds_procids);
539                         while (fds.next() && fd_procid.next()) {
540                                 if (fds->val < 0) {
541                                         if (*fd_procid == "X") {
542                                                 // X event came in
543                                                 fds->val *= -1;
544                                                 if (initial_session->desktop_event_callback()==-1) {
545                                                         exit_main_loop = 1;
546                                                         xfd = -2;
547                                                         break;
548                                                 }
549 #ifdef OPT_UNIX_SOCKET_RPC
550                                         } else if ((0 - fds->val)==unix_fd) {
551                                                 // connection request
552                                                 // for local rpc transport.
553
554                                                 fds->val = unix_fd;
555                                                 initial_s_session->u_rpc_init();
556 #endif                          // OPT_UNIX_SOCKET_RPC
557                                         } else {
558                                                 // signalling channel
559                                                 // became active for
560                                                 // some procid. This
561                                                 // means the
562                                                 // connection was lost
563                                                 // since the
564                                                 // signalling channel
565                                                 // is a write-only
566                                                 // connection.
567                                                 if (active_procs->lookup(*fd_procid,sp)) {
568                                                         // Before cleaning up,
569                                                         // send any on_exit
570                                                         // messages.
571                                                         sp->send_on_exit_messages();
572                                                         sp->set_active(-1);
573                                                         active_procs->remove(sp->id());
574                                                 }
575                                                 fds.remove();
576                                                 fd_procid.remove();
577                                         }
578                                 }
579                         }
580                         break;
581                     default:
582                         break;
583                 }
584         }
585 }
586
587
588 // 
589 // Returns 1 if there exist file-scope patterns for the given pathname.
590 // Uses the two parallel lists _file_scope_paths and
591 // _file_scope_refcounts which form a logical list of records of file
592 // pathnames to number of patterns registered.
593 // 
594 //  XXX: Use of parallel lists is confusing. Recoding using a single list
595 //  of records would improve readability.
596 // 
597 int _Tt_s_mp::
598 in_file_scope(const _Tt_string &f)
599 {
600         if (_file_scope_refcounts.is_null()) {
601                 return(0);
602         }
603         _Tt_int_rec_list_cursor         refcounts(_file_scope_refcounts);
604         _Tt_string_list_cursor          paths(_file_scope_paths);
605         
606         while (refcounts.next() && paths.next()) {
607                 if (*paths == f) {
608                         return(1);
609                 }
610         }
611         
612         return(0);
613 }
614
615
616 // 
617 // Adds (subtracts) number of file-scope patterns registered for the
618 // given pathname if add_scope is 1 (0). If the refcount of patterns goes
619 // to 0 then the pathname is removed from the _file_scope_refcounts and
620 // _file_scope_paths lists. See comment for _Tt_s_mp::in_file_scope for
621 // an explanation of these lists.
622 // 
623 void _Tt_s_mp::
624 mod_file_scope(const _Tt_string &f, int add_scope)
625 {
626         if (_file_scope_refcounts.is_null()) {
627                 _file_scope_refcounts = new _Tt_int_rec_list();
628                 _file_scope_paths = new _Tt_string_list();
629         }
630         
631         _Tt_int_rec_list_cursor         refcounts(_file_scope_refcounts);
632         _Tt_string_list_cursor          paths(_file_scope_paths);
633         
634         while (refcounts.next() && paths.next()) {
635                 if (*paths == f) {
636                         refcounts->val += (add_scope ? 1 : -1);
637                         if (refcounts->val == 0) {
638                                 refcounts.remove();
639                                 paths.remove();
640                         }
641                         return;
642                 }
643         }
644         if (add_scope) {
645                 _file_scope_refcounts->push(new _Tt_int_rec(1));
646                 _file_scope_paths->push(f);
647         }
648 }
649
650
651 Tt_status
652 _Tt_s_mp::add_procid(
653                      _Tt_s_procid_ptr &proc
654                      )
655 {
656         _active_fds->push(new _Tt_int_rec(-1));
657         _active_fds_procids->push(proc->id());
658         active_procs->insert(proc);
659         _last_proc_hit = proc;
660         return TT_OK;
661 }
662
663
664 // 
665 // Called in response to a client invoking the tt_close api call. This
666 // method will deallocate any resources that this procid holds and will
667 // remove the procid reference from any global data-structures. 
668 // 
669 Tt_status _Tt_s_mp::
670 s_remove_procid(_Tt_s_procid &proc)
671 {
672         proc.cancel_on_exit_messages();
673         // de-activate this procid
674         proc.set_active(0);
675         // remove from list of active procs
676         active_procs->remove(proc.id());
677         
678         return(TT_OK);
679 }