00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include <ctype.h>
00024 #include <stdio.h>
00025 #include <stdlib.h>
00026
00027 #include <qcolor.h>
00028 #include <qdir.h>
00029 #include <qfile.h>
00030 #include <qfileinfo.h>
00031 #include <qmap.h>
00032 #include <qstringlist.h>
00033 #include <qtextstream.h>
00034 #include <qvariant.h>
00035
00036
00037
00038 #include <config.h>
00039
00040 #include "../dcopclient.h"
00041 #include "../dcopref.h"
00042 #include "../kdatastream.h"
00043
00044 #include "marshall.cpp"
00045
00046 typedef QMap<QString, QString> UserList;
00047
00048 static DCOPClient* dcop = 0;
00049
00050 static QTextStream cin_ ( stdin, IO_ReadOnly );
00051 static QTextStream cout_( stdout, IO_WriteOnly );
00052 static QTextStream cerr_( stderr, IO_WriteOnly );
00053
00063 enum Session { DefaultSession = 0, AllSessions, QuerySessions, CustomSession };
00064
00065 bool startsWith(const QCString &id, const char *str, int n)
00066 {
00067 return !n || (strncmp(id.data(), str, n) == 0);
00068 }
00069
00070 bool endsWith(QCString &id, char c)
00071 {
00072 if (id.length() && (id[id.length()-1] == c))
00073 {
00074 id.truncate(id.length()-1);
00075 return true;
00076 }
00077 return false;
00078 }
00079
00080 void queryApplications(const QCString &filter)
00081 {
00082 int filterLen = filter.length();
00083 QCStringList apps = dcop->registeredApplications();
00084 for ( QCStringList::Iterator it = apps.begin(); it != apps.end(); ++it )
00085 {
00086 QCString &clientId = *it;
00087 if ( (clientId != dcop->appId()) &&
00088 !startsWith(clientId, "anonymous",9) &&
00089 startsWith(clientId, filter, filterLen)
00090 )
00091 printf( "%s\n", clientId.data() );
00092 }
00093
00094 if ( !dcop->isAttached() )
00095 {
00096 qWarning( "server not accessible" );
00097 exit(1);
00098 }
00099 }
00100
00101 void queryObjects( const QCString &app, const QCString &filter )
00102 {
00103 int filterLen = filter.length();
00104 bool ok = false;
00105 bool isDefault = false;
00106 QCStringList objs = dcop->remoteObjects( app, &ok );
00107 for ( QCStringList::Iterator it = objs.begin(); it != objs.end(); ++it )
00108 {
00109 QCString &objId = *it;
00110
00111 if (objId == "default")
00112 {
00113 isDefault = true;
00114 continue;
00115 }
00116
00117 if (startsWith(objId, filter, filterLen))
00118 {
00119 if (isDefault)
00120 printf( "%s (default)\n", objId.data() );
00121 else
00122 printf( "%s\n", objId.data() );
00123 }
00124 isDefault = false;
00125 }
00126 if ( !ok )
00127 {
00128 if (!dcop->isApplicationRegistered(app))
00129 qWarning( "No such application: '%s'", app.data());
00130 else
00131 qWarning( "Application '%s' not accessible", app.data() );
00132 exit(1);
00133 }
00134 }
00135
00136 void queryFunctions( const char* app, const char* obj )
00137 {
00138 bool ok = false;
00139 QCStringList funcs = dcop->remoteFunctions( app, obj, &ok );
00140 for ( QCStringList::Iterator it = funcs.begin(); it != funcs.end(); ++it ) {
00141 printf( "%s\n", (*it).data() );
00142 }
00143 if ( !ok )
00144 {
00145 qWarning( "object '%s' in application '%s' not accessible", obj, app );
00146 exit( 1 );
00147 }
00148 }
00149
00150 int callFunction( const char* app, const char* obj, const char* func, const QCStringList args )
00151 {
00152 QString f = func;
00153 int left = f.find( '(' );
00154 int right = f.find( ')' );
00155
00156 if ( right < left )
00157 {
00158 qWarning( "parentheses do not match" );
00159 return( 1 );
00160 }
00161
00162 if ( left < 0 ) {
00163
00164 bool ok = false;
00165 QCStringList funcs = dcop->remoteFunctions( app, obj, &ok );
00166 QCString realfunc;
00167 if ( !ok && args.isEmpty() )
00168 goto doit;
00169 if ( !ok )
00170 {
00171 qWarning( "object not accessible" );
00172 return( 1 );
00173 }
00174 for ( QCStringList::Iterator it = funcs.begin(); it != funcs.end(); ++it ) {
00175 int l = (*it).find( '(' );
00176 int s = (*it).find( ' ');
00177 if ( s < 0 )
00178 s = 0;
00179 else
00180 s++;
00181
00182 if ( l > 0 && (*it).mid( s, l - s ) == func ) {
00183 realfunc = (*it).mid( s );
00184 uint a = (*it).contains(',');
00185 if ( ( a == 0 && args.isEmpty() ) || ( a > 0 && a + 1 == args.count() ) )
00186 break;
00187 }
00188 }
00189 if ( realfunc.isEmpty() )
00190 {
00191 qWarning("no such function");
00192 return( 1 );
00193 }
00194 f = realfunc;
00195 left = f.find( '(' );
00196 right = f.find( ')' );
00197 }
00198
00199 doit:
00200 if ( left < 0 )
00201 f += "()";
00202
00203
00204
00205
00206
00207 QStringList intTypes;
00208 intTypes << "int" << "unsigned" << "long" << "bool" ;
00209
00210 QStringList types;
00211 if ( left >0 && left + 1 < right - 1) {
00212 types = QStringList::split( ',', f.mid( left + 1, right - left - 1) );
00213 for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) {
00214 QString lt = (*it).simplifyWhiteSpace();
00215
00216 int s = lt.find(' ');
00217
00218
00219
00220
00221
00222
00223
00224 if ( s > 0 )
00225 {
00226 QStringList partl = QStringList::split(' ' , lt);
00227
00228
00229
00230
00231
00232
00233
00234 s=1;
00235
00236 while (s < static_cast<int>(partl.count()) && intTypes.contains(partl[s]))
00237 {
00238 s++;
00239 }
00240
00241 if ( s < static_cast<int>(partl.count())-1)
00242 {
00243 qWarning("The argument `%s' seems syntactically wrong.",
00244 lt.latin1());
00245 }
00246 if ( s == static_cast<int>(partl.count())-1)
00247 {
00248 partl.remove(partl.at(s));
00249 }
00250
00251 lt = partl.join(" ");
00252 lt = lt.simplifyWhiteSpace();
00253 }
00254
00255 (*it) = lt;
00256 }
00257 QString fc = f.left( left );
00258 fc += '(';
00259 bool first = TRUE;
00260 for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) {
00261 if ( !first )
00262 fc +=",";
00263 first = FALSE;
00264 fc += *it;
00265 }
00266 fc += ')';
00267 f = fc;
00268 }
00269
00270 QByteArray data, replyData;
00271 QCString replyType;
00272 QDataStream arg(data, IO_WriteOnly);
00273
00274 uint i = 0;
00275 for( QStringList::Iterator it = types.begin(); it != types.end(); ++it )
00276 marshall( arg, args, i, *it );
00277
00278 if ( i != args.count() )
00279 {
00280 qWarning( "arguments do not match" );
00281 return( 1 );
00282 }
00283
00284 if ( !dcop->call( app, obj, f.latin1(), data, replyType, replyData) ) {
00285 qWarning( "call failed");
00286 return( 1 );
00287 } else {
00288 QDataStream reply(replyData, IO_ReadOnly);
00289
00290 if ( replyType != "void" && replyType != "ASYNC" )
00291 {
00292 QCString replyString = demarshal( reply, replyType );
00293 printf( "%s\n", replyString.data() );
00294 }
00295 }
00296 return 0;
00297 }
00298
00302 void showHelp( int exitCode = 0 )
00303 {
00304 cout_ << "Usage: dcop [options] [application [object [function [arg1] [arg2] ... ] ] ]" << endl
00305 << "" << endl
00306 << "Console DCOP client" << endl
00307 << "" << endl
00308 << "Generic options:" << endl
00309 << " --help Show help about options" << endl
00310 << "" << endl
00311 << "Options:" << endl
00312 << " --pipe Call DCOP for each line read from stdin" << endl
00313 << " This is roughly equivalent to calling" << endl
00314 << " 'while read line ; do dcop $line ; done'" << endl
00315 << " but because no new dcop instance has to be started for" << endl
00316 << " each line this is generally much faster, especially" << endl
00317 << " for the slower GNU dynamic linkers." << endl
00318 << " --user <user> Connect to the given user's DCOP server. This option will" << endl
00319 << " ignore the values of the environment vars $DCOPSERVER and" << endl
00320 << " $ICEAUTHORITY, even if they are set." << endl
00321 << " If the user has more than one open session, you must also" << endl
00322 << " use one of the --list-sessions, --session or --all-sessions" << endl
00323 << " command-line options." << endl
00324 << " --all-users Send the same DCOP call to all users with a running DCOP" << endl
00325 << " server. Only failed calls to existing DCOP servers will" << endl
00326 << " generate an error message. If no DCOP server is available" << endl
00327 << " at all, no error will be generated." << endl
00328 << " --session <ses> Send to the given KDE session. This option can only be" << endl
00329 << " used in combination with the --user option." << endl
00330 << " --all-sessions Send to all sessions found. Only works with the --user" << endl
00331 << " and --all-users options." << endl
00332 << " --list-sessions List all active KDE session for a user or all users." << endl
00333 << endl;
00334
00335 exit( exitCode );
00336 }
00337
00342 static UserList userList()
00343 {
00344 UserList result;
00345
00346 QFile f( "/etc/passwd" );
00347
00348 if( !f.open( IO_ReadOnly ) )
00349 {
00350 cerr_ << "Can't open /etc/passwd for reading!" << endl;
00351 return result;
00352 }
00353
00354 QStringList l( QStringList::split( '\n', f.readAll() ) );
00355
00356 for( QStringList::ConstIterator it( l.begin() ); it != l.end(); ++it )
00357 {
00358 QStringList userInfo( QStringList::split( ':', *it, true ) );
00359 result[ userInfo[ 0 ] ] = userInfo[ 5 ];
00360 }
00361
00362 return result;
00363 }
00364
00369 QStringList dcopSessionList( const QString &user, const QString &home )
00370 {
00371 if( home.isEmpty() )
00372 {
00373 cerr_ << "WARNING: Cannot determine home directory for user "
00374 << user << "!" << endl
00375 << "Please check permissions or set the $DCOPSERVER variable manually before" << endl
00376 << "calling dcop." << endl;
00377 return QStringList();
00378 }
00379
00380 QStringList result;
00381 QFileInfo dirInfo( home );
00382 if( !dirInfo.exists() || !dirInfo.isReadable() )
00383 return result;
00384
00385 QDir d( home );
00386 d.setFilter( QDir::Files | QDir::Hidden | QDir::NoSymLinks );
00387 d.setNameFilter( ".DCOPserver*" );
00388
00389 const QFileInfoList *list = d.entryInfoList();
00390 if( !list )
00391 return result;
00392
00393 QFileInfoListIterator it( *list );
00394 QFileInfo *fi;
00395
00396 while ( ( fi = it.current() ) != 0 )
00397 {
00398 if( fi->isReadable() )
00399 result.append( fi->fileName() );
00400 ++it;
00401 }
00402 return result;
00403 }
00404
00408 int runDCOP( QCStringList args, UserList users, Session session,
00409 const QString sessionName, bool readStdin )
00410 {
00411 bool DCOPrefmode=false;
00412 QCString app;
00413 QCString objid;
00414 QCString function;
00415 QCStringList params;
00416 DCOPClient *client = 0L;
00417 int retval = 0;
00418 if ( !args.isEmpty() && args[ 0 ].find( "DCOPRef(" ) == 0 )
00419 {
00420 int delimPos = args[ 0 ].findRev( ',' );
00421 if( delimPos == -1 )
00422 {
00423 cerr_ << "Error: '" << args[ 0 ]
00424 << "' is not a valid DCOP reference." << endl;
00425 exit( -1 );
00426 }
00427 app = args[ 0 ].mid( 8, delimPos-8 );
00428 delimPos++;
00429 objid = args[ 0 ].mid( delimPos, args[ 0 ].length()-delimPos-1 );
00430 if( args.count() > 1 )
00431 function = args[ 1 ];
00432 if( args.count() > 2 )
00433 {
00434 params = args;
00435 params.remove( params.begin() );
00436 params.remove( params.begin() );
00437 }
00438 DCOPrefmode=true;
00439 }
00440 else
00441 {
00442 if( !args.isEmpty() )
00443 app = args[ 0 ];
00444 if( args.count() > 1 )
00445 objid = args[ 1 ];
00446 if( args.count() > 2 )
00447 function = args[ 2 ];
00448 if( args.count() > 3)
00449 {
00450 params = args;
00451 params.remove( params.begin() );
00452 params.remove( params.begin() );
00453 params.remove( params.begin() );
00454 }
00455 }
00456
00457 bool firstRun = true;
00458 UserList::Iterator it;
00459 QStringList sessions;
00460 bool presetDCOPServer = false;
00461
00462 QString dcopServer;
00463
00464 for( it = users.begin(); it != users.end() || firstRun; it++ )
00465 {
00466 firstRun = false;
00467
00468
00469
00470 if( session == QuerySessions )
00471 {
00472 QStringList sessions = dcopSessionList( it.key(), it.data() );
00473 if( sessions.isEmpty() )
00474 {
00475 if( users.count() <= 1 )
00476 {
00477 cout_ << "No active sessions";
00478 if( !( *it ).isEmpty() )
00479 cout_ << " for user " << *it;
00480 cout_ << endl;
00481 }
00482 }
00483 else
00484 {
00485 cout_ << "Active sessions ";
00486 if( !( *it ).isEmpty() )
00487 cout_ << "for user " << *it << " ";
00488 cout_ << ":" << endl;
00489
00490 QStringList::Iterator sIt;
00491 for( sIt = sessions.begin(); sIt != sessions.end(); sIt++ )
00492 cout_ << " " << *sIt << endl;
00493
00494 cout_ << endl;
00495 }
00496 continue;
00497 }
00498
00499 if( getenv( "DCOPSERVER" ) )
00500 {
00501 sessions.append( getenv( "DCOPSERVER" ) );
00502 presetDCOPServer = true;
00503 }
00504
00505 if( users.count() > 1 || ( users.count() == 1 &&
00506 ( getenv( "DCOPSERVER" ) == 0 ) ) )
00507 {
00508 sessions = dcopSessionList( it.key(), it.data() );
00509 if( sessions.isEmpty() )
00510 {
00511 if( users.count() > 1 )
00512 continue;
00513 else
00514 {
00515 cerr_ << "ERROR: No active KDE sessions!" << endl
00516 << "If you are sure there is one, please set the $DCOPSERVER variable manually" << endl
00517 << "before calling dcop." << endl;
00518 exit( -1 );
00519 }
00520 }
00521 else if( !sessionName.isEmpty() )
00522 {
00523 if( sessions.contains( sessionName ) )
00524 {
00525 sessions.clear();
00526 sessions.append( sessionName );
00527 }
00528 else
00529 {
00530 cerr_ << "ERROR: The specified session doesn't exist!" << endl;
00531 exit( -1 );
00532 }
00533 }
00534 else if( sessions.count() > 1 && session != AllSessions )
00535 {
00536 cerr_ << "ERROR: Multiple available KDE sessions!" << endl
00537 << "Please specify the correct session to use with --session or use the" << endl
00538 << "--all-sessions option to broadcast to all sessions." << endl;
00539 exit( -1 );
00540 }
00541 }
00542
00543 if( users.count() > 1 || ( users.count() == 1 &&
00544 ( getenv( "ICEAUTHORITY" ) == 0 || getenv( "DISPLAY" ) == 0 ) ) )
00545 {
00546
00547 QString home = it.data();
00548 QString iceFile = it.data() + "/.ICEauthority";
00549 QFileInfo fi( iceFile );
00550 if( iceFile.isEmpty() )
00551 {
00552 cerr_ << "WARNING: Cannot determine home directory for user "
00553 << it.key() << "!" << endl
00554 << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl
00555 << "calling dcop." << endl;
00556 }
00557 else if( fi.exists() )
00558 {
00559 if( fi.isReadable() )
00560 {
00561 char *envStr = strdup( ( "ICEAUTHORITY=" + iceFile ).ascii() );
00562 putenv( envStr );
00563
00564 }
00565 else
00566 {
00567 cerr_ << "WARNING: ICE authority file " << iceFile
00568 << "is not readable by you!" << endl
00569 << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl
00570 << "calling dcop." << endl;
00571 }
00572 }
00573 else
00574 {
00575 if( users.count() > 1 )
00576 continue;
00577 else
00578 {
00579 cerr_ << "WARNING: Cannot find ICE authority file "
00580 << iceFile << "!" << endl
00581 << "Please check permissions or set the $ICEAUTHORITY"
00582 << " variable manually before" << endl
00583 << "calling dcop." << endl;
00584 }
00585 }
00586 }
00587
00588
00589
00590
00591
00592 QStringList::Iterator sIt = sessions.begin();
00593 for( ; sIt != sessions.end() || users.isEmpty(); sIt++ )
00594 {
00595 if( !presetDCOPServer && !users.isEmpty() )
00596 {
00597 QString dcopFile = it.data() + "/" + *sIt;
00598 QFile f( dcopFile );
00599 if( !f.open( IO_ReadOnly ) )
00600 {
00601 cerr_ << "Can't open " << dcopFile << " for reading!" << endl;
00602 exit( -1 );
00603 }
00604
00605 QStringList l( QStringList::split( '\n', f.readAll() ) );
00606 dcopServer = l.first();
00607
00608 if( dcopServer.isEmpty() )
00609 {
00610 cerr_ << "WARNING: Unable to determine DCOP server for session "
00611 << *sIt << "!" << endl
00612 << "Please check permissions or set the $DCOPSERVER variable manually before" << endl
00613 << "calling dcop." << endl;
00614 exit( -1 );
00615 }
00616 }
00617
00618 delete client;
00619 client = new DCOPClient;
00620 if( !dcopServer.isEmpty() )
00621 client->setServerAddress( dcopServer.ascii() );
00622 bool success = client->attach();
00623 if( !success )
00624 {
00625 cerr_ << "ERROR: Couldn't attach to DCOP server!" << endl;
00626 if( users.isEmpty() )
00627 break;
00628 else
00629 continue;
00630 }
00631 dcop = client;
00632
00633 int argscount = args.count();
00634 if ( DCOPrefmode )
00635 argscount++;
00636 switch ( argscount )
00637 {
00638 case 0:
00639 queryApplications("");
00640 break;
00641 case 1:
00642 if (endsWith(app, '*'))
00643 queryApplications(app);
00644 else
00645 queryObjects( app, "" );
00646 break;
00647 case 2:
00648 if (endsWith(objid, '*'))
00649 queryObjects(app, objid);
00650 else
00651 queryFunctions( app, objid );
00652 break;
00653 case 3:
00654 default:
00655 if( readStdin )
00656 {
00657 QCStringList::Iterator replaceArg = params.end();
00658
00659 QCStringList::Iterator it;
00660 for( it = params.begin(); it != params.end(); it++ )
00661 if( *it == "%1" )
00662 replaceArg = it;
00663
00664
00665
00666 while ( !cin_.atEnd() )
00667 {
00668 QString buf = cin_.readLine();
00669
00670 if( replaceArg != params.end() )
00671 *replaceArg = buf.local8Bit();
00672
00673 if( !buf.isNull() )
00674 {
00675 int res = callFunction( app, objid, function, params );
00676 retval = QMAX( retval, res );
00677 }
00678 }
00679 }
00680 else
00681 {
00682
00683
00684 int res = callFunction( app, objid, function, params );
00685 retval = QMAX( retval, res );
00686 }
00687 break;
00688 }
00689
00690 if( users.isEmpty() )
00691 break;
00692 }
00693
00694
00695 if( it == users.end() )
00696 break;
00697 }
00698
00699 return retval;
00700 }
00701
00702
00703 int main( int argc, char** argv )
00704 {
00705 bool readStdin = false;
00706 int numOptions = 0;
00707 QString user;
00708 Session session = DefaultSession;
00709 QString sessionName;
00710
00711 cin_.setEncoding( QTextStream::Locale );
00712
00713
00714 for( int pos = 1 ; pos <= argc - 1 ; pos++ )
00715 {
00716 if( strcmp( argv[ pos ], "--help" ) == 0 )
00717 showHelp( 0 );
00718 else if( strcmp( argv[ pos ], "--pipe" ) == 0 )
00719 {
00720 readStdin = true;
00721 numOptions++;
00722 }
00723 else if( strcmp( argv[ pos ], "--user" ) == 0 )
00724 {
00725 if( pos <= argc - 2 )
00726 {
00727 user = QString::fromLocal8Bit( argv[ pos + 1] );
00728 numOptions +=2;
00729 pos++;
00730 }
00731 else
00732 {
00733 cerr_ << "Missing username for '--user' option!" << endl << endl;
00734 showHelp( -1 );
00735 }
00736 }
00737 else if( strcmp( argv[ pos ], "--session" ) == 0 )
00738 {
00739 if( session == AllSessions )
00740 {
00741 cerr_ << "ERROR: --session cannot be mixed with --all-sessions!" << endl << endl;
00742 showHelp( -1 );
00743 }
00744 else if( pos <= argc - 2 )
00745 {
00746 sessionName = QString::fromLocal8Bit( argv[ pos + 1] );
00747 numOptions +=2;
00748 pos++;
00749 }
00750 else
00751 {
00752 cerr_ << "Missing session name for '--session' option!" << endl << endl;
00753 showHelp( -1 );
00754 }
00755 }
00756 else if( strcmp( argv[ pos ], "--all-users" ) == 0 )
00757 {
00758 user = "*";
00759 numOptions ++;
00760 }
00761 else if( strcmp( argv[ pos ], "--list-sessions" ) == 0 )
00762 {
00763 session = QuerySessions;
00764 numOptions ++;
00765 }
00766 else if( strcmp( argv[ pos ], "--all-sessions" ) == 0 )
00767 {
00768 if( !sessionName.isEmpty() )
00769 {
00770 cerr_ << "ERROR: --session cannot be mixed with --all-sessions!" << endl << endl;
00771 showHelp( -1 );
00772 }
00773 session = AllSessions;
00774 numOptions ++;
00775 }
00776 else if( argv[ pos ][ 0 ] == '-' )
00777 {
00778 cerr_ << "Unknown command-line option '" << argv[ pos ]
00779 << "'." << endl << endl;
00780 showHelp( -1 );
00781 }
00782 else
00783 break;
00784 }
00785
00786 argc -= numOptions;
00787
00788 QCStringList args;
00789 for( int i = numOptions; i < argc + numOptions - 1; i++ )
00790 args.append( argv[ i + 1 ] );
00791
00792 if( readStdin && args.count() < 3 )
00793 {
00794 cerr_ << "--pipe option only supported for function calls!" << endl << endl;
00795 showHelp( -1 );
00796 }
00797
00798 if( user == "*" && args.count() < 3 && session != QuerySessions )
00799 {
00800 cerr_ << "ERROR: The --all-users option is only supported for function calls!" << endl << endl;
00801 showHelp( -1 );
00802 }
00803
00804 if( session == QuerySessions && !args.isEmpty() )
00805 {
00806 cerr_ << "ERROR: The --list-sessions option cannot be used for actual DCOP calls!" << endl << endl;
00807 showHelp( -1 );
00808 }
00809
00810 if( session == QuerySessions && user.isEmpty() )
00811 {
00812 cerr_ << "ERROR: The --list-sessions option can only be used with the --user or" << endl
00813 << "--all-users options!" << endl << endl;
00814 showHelp( -1 );
00815 }
00816
00817 if( session != DefaultSession && session != QuerySessions &&
00818 args.count() < 3 )
00819 {
00820 cerr_ << "ERROR: The --session and --all-sessions options are only supported for function" << endl
00821 << "calls!" << endl << endl;
00822 showHelp( -1 );
00823 }
00824
00825 UserList users;
00826 if( user == "*" )
00827 users = userList();
00828 else if( !user.isEmpty() )
00829 users[ user ] = userList()[ user ];
00830
00831 int retval = runDCOP( args, users, session, sessionName, readStdin );
00832
00833 return retval;
00834 }
00835
00836
00837