Logo Search packages:      
Sourcecode: pbbuttonsd version File versions  Download package

pbb_ipc.c

/* ----------------------------------------------------------------------------
 * pbbuttons_ipc.c
 * functions for client/server inter process communication
 *
 * Copyright 2002 Matthias Grimm
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 * ----------------------------------------------------------------------------*/
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <syslog.h>

#include "pbb.h"

extern struct libbase libdata;

/* Init function called by the program init procedure. Function is known by
   the prginitab. It does module data initialisation */

int
ipc_init (char *name, int mode, int flags)
{
      if (mode == LIBMODE_SERVER)
            return ipc_serverinit ();
      else
            return ipc_clientinit (name, flags);
}

int
ipc_serverinit ()
{
      struct libbase *base = &libdata;
      struct pbbmessage pbbmsg; /* includes empty taglist */
      int sPort, i;

      base->mode = LIBMODE_SERVER;

      if ((sPort = ipc_findport(SERVERPORTKEY)) < 0) {
            if ((base->msgport = ipc_createport(SERVERPORTKEY)) < 0) {
                  return E_MSGPORT;
            }
      } else {
            if ((base->msgport = ipc_createport( (key_t) getpid() )) >= 0) {
                  ipc_send(sPort, READVALUE, NULL); /* send test message */
                  for (i=10; i > 0; i--) { 
                        usleep(100);      /* timeout loop for the answer */
                        if (ipc_getmessage (base->msgport, 0, &pbbmsg, sizeof(struct pbbmessage)) >= 0)
                              break;
                  }
                  ipc_removeport(base->msgport);
                  if (i == 0) {
                        ipc_removeport(sPort);  /* remove stall pbbuttonsd msgport */
                        ipc_serverinit();       /* try again */
                        return E_OLDPORT;       /* tell calling function that an old
                                           * port has been found and removed 
                                           */
                  }
            }
            return E_TWICE;
      }
      base->serverport = base->msgport;
      return 0;
}

int
ipc_clientinit (char *name, int flags)
{
      struct libbase *base = &libdata;
      struct tagitem taglist[4];

      base->mode = LIBMODE_CLIENT;
      
      taglist_init (taglist);
      taglist_add (taglist, TAG_CLIENTFLAGS, (long) flags);
      if (name)
            taglist_add (taglist, TAG_CLIENTNAME, (long) name);
      taglist_add (taglist, TAG_CLIENTPID, (long) getpid());

      if ((base->serverport = ipc_findport(SERVERPORTKEY)) < 0) {
            return E_NOSERVER;
      } else if ((base->msgport = ipc_createport( (key_t) getpid() )) < 0) {
            return E_MSGPORT;
      } else if (flags & CLIENT_REGISTER)
            if ((ipc_send (0, REGISTERCLIENT, taglist)) < 0) {
                  return E_REGISTER;
      }
      return 0;
}

/* Exit function called by the program exit procedure. Function is known by
   the prginitab. It does module data cleanup. */

int
ipc_exit ()
{
      struct libbase *base = &libdata;

      if (base->mode == LIBMODE_SERVER)
            if (base->daemon == PBBDS_PARENT)
                  base->msgport = -1; /* let messageport to child process */
            else
                  distribute_to_clients (CLIENTEXIT, NULL); /* send all clients 'good bye' */
      else
            ipc_send (0, UNREGISTERCLIENT, NULL);

      if (base->msgport >= 0)
            ipc_removeport(base->msgport);
      return 0;
}

/* Function to send a ipc message to a given messageport.
   if the library was in client mode the message would be sent
   to the server and the parameter 'mp' will be ignored. if the
   pointer 'taglist' was NULL, an empty taglist would be created
   and sent to the receiver. */

int
ipc_send (int mp, int action, struct tagitem *taglist)
{
      struct libbase *base = &libdata;
      char msgbuffer[MSGMAX], *strptr, *strdata, *nullstr = "";
      struct pbbmessage *pbbmsg;
      int n = 0;

      if (base->mode == LIBMODE_CLIENT)
            mp = base->serverport;

      pbbmsg = (struct pbbmessage *) msgbuffer;
      pbbmsg->returnport = base->msgport;
      pbbmsg->action = action;

      if (taglist ==  NULL) {
            pbbmsg->taglist[n].tag = TAG_END;
            pbbmsg->taglist[n].data = 0;
      } else do {
            pbbmsg->taglist[n].tag = taglist[n].tag;
            pbbmsg->taglist[n].data = taglist[n].data;
      } while (taglist[n++].tag != TAG_END);

      strptr = (char *) &pbbmsg->taglist[n];
      n = 0;
      while (pbbmsg->taglist[n].tag) {
            /* copy and reloc stringdata in tags */
            if ( !(pbbmsg->taglist[n].tag & FLG_ERROR) &&
                   (pbbmsg->taglist[n].tag & FLG_STRING)) {
                  strdata = (char*)  pbbmsg->taglist[n].data;
                  if (strdata == NULL)
                        strdata = nullstr;
                  if (strptr + strlen (strdata) + 1 >= msgbuffer + sizeof (msgbuffer))
                        return -1;
                  strcpy (strptr, strdata);
                  pbbmsg->taglist[n].data = (tag_t) (strptr - msgbuffer);
                  strptr += strlen (strdata) + 1 ;
            }
            n++;
      }

      if ((ipc_putmessage (mp, pbbmsg, (size_t) (strptr - msgbuffer))) == 0)
            return 0;   /* message sent */
      return -1;  /* message not sent */
}

/* This funcion will check it there are messages pending at the messageport.
    If so the message would be received and returned. In this case all string
    pointers in the buffer are adjusted and zero is returned.
    If it was something wrong with the message or no message was pending,
    -1 would be returned and the contents of the buffer is invalid.
    If REGISTERCLIENT or UNREGISTERCLIENT were received, the
    messages would be processed here directly. the returncode in this case
    is also -1. */

int
ipc_receive (void *buffer, size_t bufferlen)
{
      struct libbase *base = &libdata;
      struct pbbmessage *pbbmsg = (struct pbbmessage *) buffer;
      int n;
      uid_t owner;

      if (ipc_getmessage (base->msgport, 0, pbbmsg, bufferlen) < 0) {
            if (errno == -E2BIG)
                  ipc_getmessagepart (base->msgport, 0, pbbmsg, bufferlen);
      } else if (pbbmsg->messagetype == MESSAGETYPE) {
            owner = ipc_getportowner (pbbmsg->returnport);
            n = 0;
            while (pbbmsg->taglist[n].tag) {
                  if ((pbbmsg->action == CHANGEVALUE) && (base->filtermode == 1))
                        tagerror (&pbbmsg->taglist[n], E_PERM);
                  else if ((pbbmsg->action == CHANGEVALUE) && (base->filtermode == 2) && (owner != base->uid))
                        tagerror (&pbbmsg->taglist[n], E_PERM);
                  else if ((pbbmsg->action == CHANGEVALUE) && (base->filtermode == 0) &&
                          (tagfind (base->ptags, pbbmsg->taglist[n].tag, 0)) &&
                          (owner != geteuid()))                /* protected tags are only changable by */
                        tagerror (&pbbmsg->taglist[n], E_PERM);   /* the processowner of pbbuttonsd */
                  else if ((pbbmsg->taglist[n].tag & FLG_PRIVATE))     /* filter private tags */
                        tagerror (&pbbmsg->taglist[n], E_PRIVATE);
                  else if ( !(pbbmsg->taglist[n].tag & FLG_ERROR) &&
                                 (pbbmsg->taglist[n].tag & FLG_STRING))     /* reloc stringdata in tags */
                        pbbmsg->taglist[n].data += (long) buffer;
                  n++;
            }
            if (base->mode == LIBMODE_SERVER) {
                  if (pbbmsg->action == REGISTERCLIENT) {
                        if (register_client (pbbmsg->returnport, pbbmsg->taglist))
                              ipc_send (pbbmsg->returnport, REGFAILED, NULL);
                        return -1;  /* message evaluated */
                  } else if (pbbmsg->action ==  UNREGISTERCLIENT) {
                        unregister_client (pbbmsg->returnport);
                        return -1;  /* message evaluated */
                  } else if (pbbmsg->action == GETCLIENTLIST) {
                        ipc_sendclientlist(pbbmsg->returnport);
                        return -1;  /* message evaluated */
                  }
            }     
            return 0;   /* message received */
      }
      return -1;  /* no message pending or ignore it */
}

/* This function adds a client message port to the list. This client will get event messages
 * from the server in the future. If the client was successfull added, the return value
 * would be 0, otherwise -1
 */
#ifdef DEBUG
void
printClients ()
{
      struct libbase *base = &libdata;
      struct listnode *it = base->clients.first;
      struct pbbclient *client;

      printf ("\nClients:\n\n");
      printf ("  PID   | Name                 | Port    | Flags\n");
      printf ("  ------+----------------------+---------+-----------\n");
      while ((it = it->next)) {
            client = (struct pbbclient *) getNodeData (it->pred);
            printf ("  %5d | %-20s | %7d | %08x\n", client->pid,
                        (client->name) ? client->name : "<unknown>",
                        client->port, client->flags);
      }
      printf ("\n");
}
#endif

void
ipc_sendclientlist (int port)
{
      struct libbase *base = &libdata;
      struct listnode *it = base->clients.first;
      struct pbbclient *client;
      struct tagitem *taglist;
      int cnt;

      cnt = getNodeCount(&base->clients);
      if ((taglist = (struct tagitem *) malloc (sizeof(struct tagitem) * (cnt*4 + 1)))) {
            taglist_init (taglist);
      
            while ((it = it->next)) {
                  client = (struct pbbclient *) getNodeData (it->pred);
                  taglist_add (taglist, TAG_CLIENTPID,   client->pid);
                  taglist_add (taglist, TAG_CLIENTNAME,  (tag_t) client->name);
                  taglist_add (taglist, TAG_CLIENTPORT,  client->port);
                  taglist_add (taglist, TAG_CLIENTFLAGS, client->flags);
            }
            
            ipc_send (port, CHANGEVALUE, taglist);
            free (taglist);
      }
}

struct listnode *
getPBBClientName (struct listhead *lh, char *name)
{
      struct listnode *it = lh->first;
      struct pbbclient *client;

      if (name) {
            while ((it = it->next)) {
                  client = (struct pbbclient *) getNodeData (it->pred);
                  if (client->name)
                        if (strcmp (name, client->name) == 0)
                              return it->pred;
            }           
      }
      return NULL;
}

int
register_client (int cmsgport, struct tagitem *taglist)
{
      struct libbase *base = &libdata;
      struct listnode *oldnode, *newnode;
      struct pbbclient *client;
      long flags;
      char *name;
      int len, pid;

      name = (char *) tagfind (taglist, TAG_CLIENTNAME, (long) NULL);
      flags = tagfind (taglist, TAG_CLIENTFLAGS, 0);
      pid = (int) tagfind (taglist, TAG_CLIENTPID, 0);

      oldnode = getPBBClientName (&base->clients, name);
      if ((oldnode) && (flags & CLIENT_EXCLUSIVE)) { 
            if (flags & CLIENT_REPLACEMENT) {
                  /* new client replaces old one. So wave old existing
                   * client good bye
                   */
                  client = (struct pbbclient *) getNodeData (oldnode);
                  ipc_send (client->port, CLIENTEXIT, NULL);
                  freeListNode (oldnode);
            } else
                  /* client already exists, registration denied */
                  return -1;
      }
      
      len = (name) ? strlen (name) + 1 : 0;
      if ((newnode = newListNode (sizeof (struct pbbclient) + len))) {
            client = (struct pbbclient *) getNodeData (newnode);
            client->port = cmsgport;
            client->flags = flags;
            client->pid = pid;
            if (name) {
                  client->name = (char*) client + sizeof (struct pbbclient);
                  strcpy (client->name, name);
            } else
                  client->name = NULL;
            appendListNode (&base->clients, newnode);
#ifdef DEBUG
            printClients();
#endif
            return 0;
      }
      return -1;
}

/* This function removes a client message port from the list and reorganizes the
 * list so that gaps would be eliminated. This client won't get further messages from
 * the server. If the client was successfull removed, the return value would be 0,
 * otherwise -1
 */
int
unregister_client (int cmsgport)
{
      struct libbase *base = &libdata;
      struct listnode *it = base->clients.first;
      struct pbbclient *client;

      while ((it = it->next)) {
            client = (struct pbbclient *) getNodeData (it->pred);
            if (client->port == cmsgport) {
                  freeListNode (it->pred);
#ifdef DEBUG      
                  printClients();
#endif
                  return 0;
            }
      }
      return -1;
}

/* This function distributes a single tag to all clients. Most of the
    distribution tasks are single tag transmissions. */

void
singletag_to_clients (int action, tag_t tag, tag_t data)
{
      struct tagitem taglist[2];

      taglist_init (taglist);
      taglist_add (taglist, tag, data);
      distribute_to_clients (action, taglist);
}

/* This function distributes a single tag to the server. */

int
singletag_to_server (int action, tag_t tag, tag_t data)
{
      struct tagitem taglist[2];

      taglist_init (taglist);
      taglist_add (taglist, tag, data);
      return ipc_send (0, action, taglist);
}

/* This function sends a message with given code and value to every registered
 * client. If an error with a client occured, that client would be unregistered
 */
void
distribute_to_clients (int action, struct tagitem *taglist)
{
      struct libbase *base = &libdata;
      struct listnode *it = base->clients.first;
      struct pbbclient *client;

#ifdef DEBUG
      peep_ipc (taglist);
#endif

      while ((it = it->next)) {
            client = (struct pbbclient *) getNodeData (it->pred);
            if ((ipc_send (client->port, action, taglist)) != 0)
                  unregister_client (client->port);
      }
}

int
ipc_protect_tag (tag_t tag)
{
      struct libbase *base = &libdata;

      if (base->ptagcount < (MAXPROTECTEDTAGS - 1)) {
            taglist_add (base->ptags, tag, 1);
            return 0;
      }
      return -1;
}

void
ipc_filteruser (uid_t uid)
{
      struct libbase *base = &libdata;
      base->filtermode = 2;
      base->uid = uid;
}

void
ipc_filterall ()
{
      struct libbase *base = &libdata;
      base->filtermode = 1;
}

void
ipc_filterclear ()
{
      struct libbase *base = &libdata;
      base->filtermode = 0;
}

/* ---------------------- LOW LEVEL MESSAGE FUNCTIONS ----------------------- */

/* This function create an new message port with world read/write permissions.
   The result is an errorcode or an IPC identifier for the messageport */

int
ipc_createport (key_t keyval)
{
      int mp;
      int perm = 0622;  /* permissions for the messageport */

      if ((mp = msgget(keyval, IPC_CREAT | IPC_EXCL | perm)) == -1) {
            return -errno;
      }
      return mp;
}

/* This function look up a message port. If it was found the IPC identifier would
    be returned. Otherwise an errorcode will be returned. */

int
ipc_findport (key_t keyval)
{
      int mp;

      if ((mp = msgget(keyval, 0)) == -1)
            return -errno;
      return mp;
}

/* This function removed a messageport from the kernel list. */

int
ipc_removeport (int mp)
{
      if( msgctl( mp, IPC_RMID, 0) == -1)
            return -errno;
      return 0;
}

/* This function puts a message to a given IPC messagequeue */

int
ipc_putmessage (int mp, struct pbbmessage *msg, size_t len)
{
      msg->messagetype = MESSAGETYPE;
      if (msgsnd (mp, msg, len - sizeof(long), 0) == -1)
            return -errno;
      return 0;
}

/* This function polls the next message from the given IPC message queue.
   if there is no new message pending it returns immediately with a
   corresponding return code. */

int
ipc_getmessage (int mp, long type, struct pbbmessage *msg, size_t len)
{
      if (msgrcv (mp, msg, len, type, IPC_NOWAIT) == -1)
            return -errno;
      return 0;
}

/* This function polls the next message from the given IPC message queue.
   if there is no new message pending it returns immediately with a
   corresponding return code. This function gets also messages that are to
   big for the buffer. In this case the buffer is filled to its end and the rest of
   the message is lost. */

int
ipc_getmessagepart (int mp, long type, struct pbbmessage *msg, size_t len)
{
      if (msgrcv (mp, msg, len, type, IPC_NOWAIT | MSG_NOERROR) == -1)
            return -errno;
      return 0;
}

/* This function reads the creator-id of an given port. This is needed to verify
   permissions. The creator-id is used because the owner-id of a messageport
   could be changed by the owner, not so the creator-id. */

uid_t
ipc_getportowner (int mp)
{
      struct msqid_ds qbuf;

      if (msgctl (mp, IPC_STAT, &qbuf) != -1)
            return qbuf.msg_perm.cuid;
      return -1;
}

Generated by  Doxygen 1.6.0   Back to index