From bba0335bbea81519beafb7fec1979a0abbd8a7ea Mon Sep 17 00:00:00 2001 From: zecke Date: Tue, 01 Oct 2002 14:27:17 +0000 Subject: 5th try initial checkin of X11 Opie stuff it features an IPC Server the client is todo this will become the OPIE X11 department including alternative QPEApplication, QCOP replacement --- diff --git a/x11/ipc/DESIGN b/x11/ipc/DESIGN new file mode 100644 index 0000000..fbe121b --- a/dev/null +++ b/x11/ipc/DESIGN @@ -0,0 +1,42 @@ +This is the design of our Opie PDA Communication +Protocol +It's main purpose is to be src compatible with QCOP +from TT and Qtopia. It's main target is to be used +as a QCOP replacement for the X11 Windowing System. + +We heavily use unix domain sockets for the internal +communication. +We've one socket in $HOME/.opie.cop which is the +server socket. We will communicate with the use +of QDataStream and QByteArray. +Internally QByteArries will be copied from the client +to the server + +A package is looking similiar to this one + +int packet_type; // Register, Unregister, Call, Method, Reply, RegisterChannel, UnregisterChannel +QString channel; +QString header +QByteArray packet + +Server PART: +We keep the fds together with a list of Channels +QMap> int is the fd and QString is the channel +name +this makes the isRegistered stuff more easy to manage + + +Client: +RPC is currently not enabled but will be available in future versions +I do not want to add it otherwise I'll stick too tight +to this implementation. +We currently only support send and forget +isRegistered will be taken care of the client side. We can not +use the X WindowSystem for that + + +I hope this helps... be sure to look at the code +I try to comment it +and now go to the hacking ;) + +-zecke 30.09.02 \ No newline at end of file diff --git a/x11/ipc/common/ocoppacket.cpp b/x11/ipc/common/ocoppacket.cpp new file mode 100644 index 0000000..0d8ae84 --- a/dev/null +++ b/x11/ipc/common/ocoppacket.cpp @@ -0,0 +1,59 @@ + +#include + +#include "ocoppacket.h" + +OCOPPacket::OCOPPacket( int type, + const QCString& channel, + const QCString& header, + const QByteArray& array ) + : m_type( type ), m_channel( channel ), + m_header( header ), m_content( array ) +{ +} +OCOPHead OCOPPacket::head()const { + OCOPHead head; + memset(&head, 0, sizeof(head) ); + + head.magic = 47; + head.type = m_type; + head.chlen = m_channel.size(); + head.funclen = m_header.size(); + head.datalen = m_content.size(); + + return head; +}; +QByteArray OCOPPacket::toByteArray()const { + QByteArray array; + QDataStream stream(array, IO_WriteOnly ); + stream << m_type; + stream << m_channel; + stream << m_header; + stream << m_content; + + return array; +} +int OCOPPacket::type()const { + return m_type; +} +QCString OCOPPacket::channel()const { + return m_channel; +} +QCString OCOPPacket::header()const { + return m_header; +} +QByteArray OCOPPacket::content()const { + return m_content; +} +void OCOPPacket::setType( int type ) { + m_type = type; +} +void OCOPPacket::setChannel( const QCString& chan ) { + m_channel = chan; +} +void OCOPPacket::setHeader( const QCString& head ) { + m_header = head; +} +void OCOPPacket::setContent( const QByteArray& arra ) { + m_content = arra; +} diff --git a/x11/ipc/common/ocoppacket.h b/x11/ipc/common/ocoppacket.h new file mode 100644 index 0000000..490ff03 --- a/dev/null +++ b/x11/ipc/common/ocoppacket.h @@ -0,0 +1,64 @@ +/* GPL!*/ + +#ifndef OPIE_OCOP_PACKET_H +#define OPIE_OCOP_PACKET_H + +#include +#include + +/** + * This is the head which will be sent + * in advance to every packet + */ +struct OCOPHead { + int magic; + int type; + int chlen; + int funclen; + int datalen; +}; + +/** + * This is the basic packet we will + * use for the communication between server + * and client + */ +class OCOPPacket { +public: + enum Type { + Register = 0, Unregister, Call, + Method, Reply, RegisterChannel, + UnregisterChannel, Return, Signal, + IsRegistered + }; + /** + * the c'tor + * type the Type of this packet + * the Channel + * the header of the function + * the data inside a QByteArray + */ + OCOPPacket( int type, const QCString& channel = QCString(), + const QCString& header = QCString(), + const QByteArray& array = QByteArray() ); + + QByteArray toByteArray()const; + int type()const; + QCString channel()const; + QCString header()const; + QByteArray content()const; + OCOPHead head()const; + + void setType( int type ); + void setChannel( const QCString& ); + void setHeader(const QCString& ); + void setContent( const QByteArray& ); + +private: + int m_type; + QCString m_channel; + QCString m_header; + QByteArray m_content; +}; + +#endif diff --git a/x11/ipc/server/main.cpp b/x11/ipc/server/main.cpp new file mode 100644 index 0000000..bc359c9 --- a/dev/null +++ b/x11/ipc/server/main.cpp @@ -0,0 +1,13 @@ +#include +#include "ocopserver.h" + + +int main( int argc, char* argv[] ) { + QApplication app(argc, argv ); + + /* get the server started */ + OCopServer server; + + /* enter the event loop */ + return app.exec(); +}; diff --git a/x11/ipc/server/ocopclient.h b/x11/ipc/server/ocopclient.h new file mode 100644 index 0000000..056a058 --- a/dev/null +++ b/x11/ipc/server/ocopclient.h @@ -0,0 +1,12 @@ +#ifndef OPIE_OCOP_CLIENT_H +#define OPIE_OCOP_CLIENT_H + +#include +#include + +struct OCOPClient { + int fd; + QSocketNotifier* notify; +}; + +#endif diff --git a/x11/ipc/server/ocopserver.cpp b/x11/ipc/server/ocopserver.cpp new file mode 100644 index 0000000..3df574b --- a/dev/null +++ b/x11/ipc/server/ocopserver.cpp @@ -0,0 +1,383 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ocopserver.h" + +OCopServer::OCopServer() + : QObject() +{ + setName( "ocopserver"); + + /* + * init the server + */ + init(); + initSocket(); +} +OCopServer::~OCopServer() { +// socket notifiers should be deleted + close(m_serverfd ); +} +void OCopServer::init() { + /* + * we set SIGPIPE to SIG_IGN + * to get EPIPE on reads ;) + */ + qWarning("SIGPIPE to be ignored"); + signal(SIGPIPE, SIG_IGN ); + + /* + * initialize some variables + */ + m_server = 0l; + m_serverError = 0l; +} + +/** + * here we will init our server + * socket and bind and do the listen + */ +void OCopServer::initSocket() { + /* get the home dir */ + QCString home( getenv("HOME") ); + QCString path( home + "/.opie.cop"); + + if ( ( m_serverfd = socket( PF_UNIX, SOCK_STREAM, 0 ) ) == -1 ) { + qWarning("failed to create server socket"); + /* try again later */ + QTimer::singleShot( 400, this, SLOT(initSocket() ) ); + return; + } + qWarning( "unlinking file %s", path.data() ); + + /* unlink previous sockets */ + unlink( path.data() ); + + struct sockaddr_un m_address; + memset(&m_address, 0, sizeof(m_address ) ); + m_address.sun_family = AF_UNIX; /* unix domain socket */ + strcpy(m_address.sun_path, path.data() ); + m_adrlaenge = sizeof(m_address.sun_family) + strlen(m_address.sun_path ); + + /* cast to make it a (sockadr*) */ + if (bind(m_serverfd, (struct sockaddr*)&m_address, m_adrlaenge ) == -1 ) { + qWarning("Server could not bind try again"); + close(m_serverfd); + QTimer::singleShot(400, this, SLOT(initSocket() ) ); + return; + } + + /* tell the kernel that we're listening and accepting + * 5 pending connections */ + if (listen(m_serverfd, 5) == -1 ) { + qWarning("could not listen"); + close(m_serverfd ); + QTimer::singleShot(400, this, SLOT(initSocket() ) ); + return; + } + + /* + * now we will create two QSocketNotifier + * which will us notify on reads + * and errors + * we do this because they integrate + * nicely into the QApplication eventloop + */ + m_server = new QSocketNotifier(m_serverfd, QSocketNotifier::Read, this ); + connect( m_server, SIGNAL(activated(int) ), + this, SLOT(newOnServer() ) ); + + m_serverError = new QSocketNotifier( m_serverfd, QSocketNotifier::Exception, this); + connect(m_serverError, SIGNAL(activated(int) ), + this, SLOT(errorOnServer() ) ); + + qWarning("done with registering"); +} +/** + * we got the possibility to read + * on the server + * this is mostly due a connect + * on a client side + * we will accept it + * add it to our list + */ +void OCopServer::newOnServer() { + int fd = accept(); + if ( fd < 0 ) + return; + + /* + * we got a successfull new connection + * be happy + * set SocketNotifier + * connect it + * and a OCOPClient + */ + qWarning("Heureka new connection %d", fd ); + + + registerClient( fd ); +} +int OCopServer::accept() { + /* + * accept it + * the socket is currently blocking IIRC + */ + return ::accept( m_serverfd, (struct sockaddr*)&m_address, &m_adrlaenge ); +} +void OCopServer::newOnClient( int fd ) { + int bug[4096]; + //qWarning("new stuff for client on fd %d", fd ); + errno = 0; + OCOPHead head; + memset(&head, 0, sizeof(head) ); + int rea = ::read(fd, &head, sizeof(head) ); + //qWarning("read %d %d", rea, errno); + /* + * I should get EPIPE but nothing like this happens + * so if rea == 0 and we were signaled by the notifier + * we close it and drop the clients... + */ + if ( rea <= 0 ) { + deregisterClient( fd ); + return; + } + /* + * OCOPHead + */ + qWarning("data %s %d", bug, rea ); + + /* + * Check the magic + * if chcked read till EOF if magic does not match + * otherwise do read + * channel + * func + * data into mem + * and then send the OCOPPacket + * + */ + if (head.magic == 47 ) { + qWarning("magic match"); + QCString channel( head.chlen+1 ); + QCString func( head.funclen+1 ); + QByteArray data ( head.datalen ); + + /* + * we do not check for errors + */ + int s = read(fd, channel.data(), head.chlen ); + s = read(fd, func.data(), head.funclen ); + s = read(fd, data.data(), head.datalen ); + + /* debug output */ + qWarning("channel %s %d", channel.data(), head.chlen ); + qWarning("func %s %d", func.data(), head.funclen ); + /* debug end */ + + /* + * now that we got the complete body + * we need to make a package + * and then we need to send it to clients + * making a package is done here + * dispatching it not + */ + OCOPPacket packet( head.type, channel, func, data ); + dispatch( packet, fd ); + + }else{ + qWarning("magic does not match"); + qWarning("magic %d", head.magic ); + } +} +void OCopServer::registerClient( int fd ) { + if (m_clients.contains(fd) ) + return; + + QSocketNotifier* notify = new QSocketNotifier(fd, QSocketNotifier::Read, this ); + connect(notify, SIGNAL(activated(int) ), + this, SLOT(newOnClient(int) ) ); + OCOPClient client; + client.fd = fd; + client.notify = notify; + m_clients.insert( client.fd, client ); + qWarning("clients are up to %d", m_clients.count() ); +}; +void OCopServer::deregisterClient(int fd ) { + QMap::Iterator it = m_clients.find( fd ); + if (it != m_clients.end() ) { + OCOPClient client = (*it); + delete client.notify; + m_clients.remove(fd ); + close(fd ); + /* + * TIME_ME + * + * now delete from all channels + * go through all channels + * remove the fd from the list + * if count becomes 0 remove the channel + * otherwise replace QArray + */ + QMap >::Iterator it; + for ( it = m_channels.begin(); it != m_channels.end(); ++it ) { + /* + * The channel contains this fd + */ + if ( it.data().contains( fd ) ) { + QValueList array = it.data(); + + /* + * remove channel or just replace + */ + if ( array.count() == 1 ) { + /* is the list now invalidatet? */ + m_channels.remove( it ); + }else{ + array.remove( fd ); + it = m_channels.replace( it.key(), array ); + } + } + } // off all channels + } + qWarning("clients are now at %d", m_clients.count() ); +}; +/** + * this function will evaluate + * the package and then do the appropriate thins + */ +void OCopServer::dispatch( const OCOPPacket& packet, int sourceFD ) { + qWarning("packet.type() == %d", packet.type() ); + switch( packet.type() ) { + case OCOPPacket::Register: + registerClient(sourceFD ); + break; + case OCOPPacket::Unregister: + deregisterClient(sourceFD ); + break; + case OCOPPacket::Call: + call( packet, sourceFD ); + break; + /* not implemented */ + case OCOPPacket::Method: + break; + /* nit implemented */ + case OCOPPacket::Reply: + break; + case OCOPPacket::RegisterChannel: + addChannel( packet.channel() , sourceFD ); + break; + case OCOPPacket::UnregisterChannel: + delChannel( packet.channel(), sourceFD ); + break; + /* not implemented */ + case OCOPPacket::Return: + break; + /* not implemented :( */ + case OCOPPacket::Signal: + break; + case OCOPPacket::IsRegistered: + isRegistered( packet.channel(), sourceFD ); + break; + }; +} +void OCopServer::errorOnServer() { + /* + * something is wrong on the server socket? + * what should we do? + * FIXME + */ +} +QStringList OCopServer::channels() { + QStringList list; + { + QMap >::Iterator it; + for (it = m_channels.begin(); it != m_channels.end(); ++it ) { + list << it.key(); + }; + } + return list; +} +bool OCopServer::isChannelRegistered( const QCString& chan ) const{ + return m_channels.contains( chan ); +} +void OCopServer::addChannel( const QCString& channel, + int fd ) { + QMap >::Iterator it; + it = m_channels.find( channel ); + + /* could be empty */ + QValueList list = it.data(); + list.append( fd ); + it = m_channels.replace( channel, list ); +}; +void OCopServer::delChannel( const QCString& channel, + int fd ) { + if (!m_channels.contains( channel ) ) + return; + + QMap >::Iterator it; + it = m_channels.find( channel ); + + if ( it.data().contains(fd) ) { + + QValueList ints = it.data(); + if ( ints.count() == 1 ) + m_channels.remove( it ); + else{ + QValueList ints = it.data(); + ints.remove( fd ); + m_channels.replace( it.key(), ints ); + } + } +} +void OCopServer::isRegistered( const QCString& channel, int fd) { + OCOPHead head; + QCString func(2); + + memset(&head, 0, sizeof(head ) ); + head.magic = 47; + head.type = OCOPPacket::IsRegistered; + head.chlen = channel.size(); + head.funclen = func.size(); + head.datalen = 0; + + if ( isChannelRegistered( channel ) ) { + //is registered + func[0] = 1; + }else{ + func[0] = 0; + } + + /** + * write the head + * and then channel + * success/failure inside func + */ + write(fd, &head, sizeof(head) ); + write(fd, channel.data(), channel.size() ); + write(fd, func.data(), func.size() ); +} +QValueList OCopServer::clients( const QCString& channel ) { + return m_channels[channel]; +} +void OCopServer::call( const OCOPPacket& p, int fd ) { + QValueList cli = clients( p.channel() ); + QValueList::Iterator it; + + OCOPHead head = p.head(); + for (it = cli.begin(); it != cli.end(); ++it ) { + write( (*it), &head, sizeof(head ) ); + write( (*it), p.channel().data(), p.channel().size() ); + write( (*it), p.header().data(), p.header().size() ); + write( (*it), p.content().data(), p.content().size() ); + }; +} diff --git a/x11/ipc/server/ocopserver.h b/x11/ipc/server/ocopserver.h new file mode 100644 index 0000000..3e08d5b --- a/dev/null +++ b/x11/ipc/server/ocopserver.h @@ -0,0 +1,95 @@ + +#ifndef OPIE_OCOP_SERVER_H +#define OPIE_OCOP_SERVER_H + +#include + +#include +#include +#include +#include +#include + +#include "../common/ocoppacket.h" +#include "ocopclient.h" +/** + * This is the main server + * It starts the socket + * takes care of the receiving and sending + */ +class OCopServer : public QObject { + Q_OBJECT +public: + OCopServer(); + ~OCopServer(); + + QStringList channels(); + bool isChannelRegistered(const QCString& )const; + +private slots: + void errorOnServer();// error on Server + void newOnServer();// accept to be taken + void newOnClient(int fd ); // new package received + +private: + /* replace fd with a special class in future + * to even work on Windoze aye aye + */ + /** + * add a channel with a fd + * if the channel is not present + * then it'll be created + * if it's present we will ad the fd + */ + void addChannel( const QCString& channel, + int fd ); + void delChannel( const QCString& channel, + int fd ); + + /** + * fd was closed + */ + void deregisterClient( int fd ); + + /** + * fd popped up + */ + void registerClient( int fd ); + +private: + void init(); +private slots: + void initSocket(); +private: + int accept(); + void isRegistered( const QCString& channel, int ); + void dispatch( const OCOPPacket&, int sourceFD ); + void call( const OCOPPacket&, int sourceFD ); + QValueList clients(const QCString& channel ); + /* + * All clients + * They include a fd and a QSocketNotifier + */ + QMap m_clients; + + /* + * The channels avilable + */ + QMap > m_channels; + + /* + * a notifier for our server + * if new stuff is arriving + */ + QSocketNotifier* m_server; + + /* + * error + */ + QSocketNotifier* m_serverError; + int m_serverfd; + struct sockaddr_un m_address; + unsigned int m_adrlaenge; +}; + +#endif diff --git a/x11/ipc/server/ocopserver.pro b/x11/ipc/server/ocopserver.pro new file mode 100644 index 0000000..1776063 --- a/dev/null +++ b/x11/ipc/server/ocopserver.pro @@ -0,0 +1,9 @@ +TEMPLATE = app +CONFIG = qt warn_on debug +#CONFIG = qt warn_on release +HEADERS = ../common/ocoppacket.h ocopclient.h ocopserver.h +SOURCES = ../common/ocoppacket.cpp main.cpp ocopserver.cpp +INCLUDEPATH += $(OPIEDIR)/include +DEPENDPATH += $(OPIEDIR)/include +TARGET = ocopserver + diff --git a/x11/ipc/test/testcon.c b/x11/ipc/test/testcon.c new file mode 100644 index 0000000..22382b1 --- a/dev/null +++ b/x11/ipc/test/testcon.c @@ -0,0 +1,58 @@ +#include +#include +#include + +#include +#include + +struct head { + int magic; + int flags; + int chlen; + int funclen; + int datalen; +}; + +int main( int argc, char* argv[] ) { + int fd, groesse; + struct sockaddr_un unix_adr; + struct head he; + char* ch = "QPE/System"; + char* fun = "call()"; + int test; + char te; + + + if( ( fd = socket(PF_UNIX, SOCK_STREAM, 0 ) ) < 0 ){ + printf("Could not socket\n" ); return 0; + } + + memset(&unix_adr, 0, sizeof(unix_adr ) ); + unix_adr.sun_family = AF_UNIX; + strcpy(unix_adr.sun_path, "/home/ich/.opie.cop"); + groesse = sizeof(unix_adr.sun_family) + strlen(unix_adr.sun_path); + + connect(fd, (struct sockaddr*)&unix_adr, groesse ); + + test= 260; + //te = (char)test; + //write(fd,&test,sizeof(test)); + + + he.magic = 47; + he.flags = 5; + he.chlen = strlen(ch); + he.funclen = strlen(fun); + he.datalen = 0; + + for(;;){ + write(fd,&he,sizeof(he) ); + //write(fd,(char*)&he.magic,1); + write(fd,ch,strlen(ch) ); + write(fd,fun,strlen(fun) ); + sleep(5); + }; + + + +} -- cgit v0.9.0.2