cdesktopenv/cde/lib/tt/slib/mp_s_session.C

582 lines
16 KiB
C

/*
* 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 librararies 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 <stdio.h>
#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 <unistd.h>
#include "util/tt_port.h"
#include <X11/Xlib.h>
#ifdef OPT_UNIX_SOCKET_RPC
# include <sys/socket.h>
# include <sys/un.h>
# 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(ultrix) || defined(_AIX) || defined(hpux) || defined(__osf__)
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(-1,
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);
}
#if defined(ultrix)
/* strdup - make duplicate of string s */
#include <string.h>
#include <malloc.h>
static char *
strdup(char *s)
{
char *p;
p = (char *)malloc(strlen(s) + 1); /* + 1 for '\0' character */
if (p != NULL)
strcpy(p,s);
return p;
}
#endif
//
// 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<<TT_SESSION) ||
scopes&(1<<TT_FILE_IN_SESSION) ||
scopes&(1<<TT_BOTH)) {
if (add) {
pcursor->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);
}