/* KDM remote control application Copyright (C) 2004 Oswald Buddenhagen <ossi@kde.org> 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include <config.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <ctype.h> #include <sys/socket.h> #include <sys/un.h> static int openctl( int fd, int err, const char *ctl, const char *dpy ) { struct sockaddr_un sa; sa.sun_family = AF_UNIX; if (dpy) snprintf( sa.sun_path, sizeof(sa.sun_path), "%s/dmctl-%s/socket", ctl, dpy ); else snprintf( sa.sun_path, sizeof(sa.sun_path), "%s/dmctl/socket", ctl ); if (!connect( fd, (struct sockaddr *)&sa, sizeof(sa) )) return 1; if (err) fprintf( stderr, "Cannot connect socket '%s'.\n", sa.sun_path ); return 0; } static const char * readcfg( const char *cfg ) { FILE *fp; const char *ctl; char *ptr, *ptr2; char buf[1024]; if (!(fp = fopen( cfg, "r" ))) { fprintf( stderr, "Cannot open kdm config file '%s'.\n", cfg ); return 0; } ctl = "/var/run/xdmctl"; while (fgets( buf, sizeof(buf), fp )) if (!strncmp( buf, "FifoDir", 7 )) { ptr = buf + 7; while (*ptr && isspace( *ptr )) ptr++; if (*ptr++ != '=') continue; while (*ptr && isspace( *ptr )) ptr++; for (ptr2 = buf + strlen( buf ); ptr2 > ptr && isspace( *(ptr2 - 1) ); ptr2--); *ptr2 = 0; ctl = strdup( ptr ); break; } fclose( fp ); return ctl; } static int exe( int fd, const char *in, int len ) { char buf[4096]; if (write( fd, in, len ) != len) { fprintf( stderr, "Cannot send command\n" ); return 1; } if ((len = read( fd, buf, sizeof(buf) )) <= 0) { fprintf( stderr, "Cannot receive reply\n" ); return 1; } if (len == sizeof(buf) && buf[sizeof(buf) - 1] != '\n') fprintf( stderr, "Warning: reply is too long\n" ); fwrite( buf, 1, len, stdout ); if (len == sizeof(buf) && buf[sizeof(buf) - 1] != '\n') puts( "[...]" ); return 0; } static int run( int fd, char **argv ) { unsigned len, l; char buf[1024]; if (!*argv) return exe( fd, "caps\n", 5 ); if (!strcmp( *argv, "-" )) { for (;;) { if (isatty( 0 )) { fputs( "> ", stdout ); fflush( stdout ); } if (!fgets( buf, sizeof(buf), stdin )) return 0; if (exe( fd, buf, strlen( buf ) )) return 1; } } else { len = strlen( *argv ); if (len >= sizeof(buf)) goto bad; memcpy( buf, *argv, len ); while (*++argv) { l = strlen( *argv ); if (len + l + 1 >= sizeof(buf)) goto bad; buf[len++] = '\t'; memcpy( buf + len, *argv, l ); len += l; } buf[len++] = '\n'; return exe( fd, buf, len ); bad: fprintf( stderr, "Command too long\n" ); return 1; } } int main( int argc, char **argv ) { char *dpy = getenv( "DISPLAY" ); const char *ctl = getenv( "DM_CONTROL" ); const char *cfg = KDE_CONFDIR "/kdm/kdmrc"; char *ptr; int fd; (void)argc; while (*++argv) { ptr = *argv; if (*ptr != '-' || !*(ptr + 1)) break; ptr++; if (*ptr == '-') ptr++; if (!strcmp( ptr, "h" ) || !strcmp( ptr, "help" )) { puts( "Usage: kdmctl [options] [command [command arguments]]\n" "\n" "Options are:\n" " -h -help This help message.\n" " -g -global Use global control socket even if $DISPLAY is set\n" " -d -display Override $DISPLAY\n" " -s -sockets Override $DM_CONTROL\n" " -c -config Use alternative kdm config file\n" "\n" "The directory in which the sockets are located is determined this way:\n" "- the -s option is examined\n" "- the $DM_CONTROL variable is examined\n" "- the kdm config file is searched for the FifoDir key\n" "- /var/run/xdmctl and /var/run are tried\n" "\n" "If $DISPLAY is set (or -d was specified) and -g was not specified, the\n" "display-specific control socket will be used, otherwise the global one.\n" "\n" "Tokens in the command and the reply are tab-separated.\n" "Command arguments can be specified as separate command line parameters,\n" "in which case they are simply concatenated with tabs in between.\n" "\n" "If the command is '-', kdmctl reads commands from stdin.\n" "The default command is 'caps'.\n" ); return 0; } else if (!strcmp( ptr, "g" ) || !strcmp( ptr, "global" )) dpy = 0; else if (!strcmp( ptr, "d" ) || !strcmp( ptr, "display" )) { if (!argv[1]) goto needarg; dpy = *++argv; } else if (!strcmp( ptr, "s" ) || !strcmp( ptr, "sockets" )) { if (!argv[1]) goto needarg; ctl = *++argv; } else if (!strcmp( ptr, "c" ) || !strcmp( ptr, "config" )) { if (!argv[1]) { needarg: fprintf( stderr, "Option '%s' needs argument.\n", ptr ); return 1; } cfg = *++argv; } else { fprintf( stderr, "Unknown option '%s'.\n", ptr ); return 1; } } if ((!ctl || !*ctl) && *cfg) ctl = readcfg( cfg ); if ((fd = socket( PF_UNIX, SOCK_STREAM, 0 )) < 0) { fprintf( stderr, "Cannot create UNIX socket\n" ); return 1; } if (dpy && (ptr = (char*)strchr( dpy, ':' )) && (ptr = (char*)strchr( ptr, '.' ))) *ptr = 0; if (ctl && *ctl) { if (!openctl( fd, 1, ctl, dpy )) return 1; } else { if (!openctl( fd, 0, "/var/run/xdmctl", dpy ) && !openctl( fd, 0, "/var/run", dpy )) { fprintf( stderr, "No command socket found.\n" ); return 1; } } return run( fd, argv ); }