├── .gitignore ├── AUTHORS ├── ChangeLog ├── INSTALL ├── Makefile ├── README.md └── sysrqd.c /.gitignore: -------------------------------------------------------------------------------- 1 | sysrqd.o 2 | sysrqd 3 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Julien Danjou 2 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | Version 17 -- 8 Apr 2019 2 | * Fix check of inet_aton() returned value 3 | 4 | Version 16 -- Thu, 24 Mar 2016 14:43:33 CET 5 | * Update README 6 | * Add missing changelog entry 7 | 8 | Version 15 -- Thu, 24 Mar 2016 14:36:33 CET 9 | * Allow to store password encrypted using crypt(3) 10 | 11 | Version 14 -- Tue, 16 Mar 2010 16:54:22 +0100 12 | * Fix authentification with clients not sending \r\n 13 | 14 | Version 13 -- Mon, 30 Nov 2009 09:51:24 +0100 15 | * No code change, but version 12 was badly released with no changelog 16 | and bad tarball. 17 | 18 | Version 12 -- Mon, 19 Oct 2009 14:03:01 +0200 19 | * Ignore SIGPIPE 20 | 21 | Version 11 -- Mon, 19 Oct 2009 11:30:52 +0200 22 | * Do not try to write anything if read() has failed while reading 23 | password. Fix a crash using e.g. nmap. 24 | 25 | Version 10 -- Thu, 12 Feb 2009 12:05:54 +0100 26 | * memset() our pwd/bindip 27 | * Merge read password and bindfile functions 28 | * Add more things to syslog 29 | * Fix authentification 30 | 31 | Version 9 -- Fri, 19 Jan 2007 15:32:00 +0100 32 | * Add a warning about TERM and KILL in README 33 | * Merge a patch from Neil McGovern: 34 | - don't go in swap, always stay in memory 35 | - if /etc/sysrqd.bind exists, bind to a specific ip 36 | instead of any. 37 | 38 | Version 8 -- Tue, 11 Jul 2006 11:41:05 +0200 39 | * Use official ports registered by IANA: 40 | sysrqd 4094/tcp sysrq daemon 41 | sysrqd 4094/udp sysrq daemon 42 | 43 | Version 7 -- Tue, 16 May 2006 15:55:55 +0200 44 | * Call daemon() instead of simply forking 45 | 46 | Version 6 -- Tue, 16 May 2006 15:00:44 +0200 47 | * Fix bug with pid file 48 | (Thanks to Norbert Kiesel ) 49 | * Change default port to 880 50 | 51 | Version 5 -- Mon, 1 May 2006 11:58:21 +0200 52 | * Fix little bug with open() 53 | 54 | Version 4 -- Sun, 30 Apr 2006 21:50:51 +0200 55 | * Add support of syslog 56 | * Fork and run in background 57 | * Write a pid file 58 | 59 | Version 3 -- Sat, 11 Mar 2006 11:48:00 +0100 60 | * Fix little bug if password file does not have \n 61 | 62 | Version 2 -- Mon, 29 Aug 2005 17:06:00 +0200 63 | * Remove init.d script 64 | 65 | Version 1 -- Mon, 29 Aug 2005 11:02:26 +0200 66 | * Initial release 67 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Symple type: 2 | 3 | % make 4 | % make install 5 | 6 | - Read the README file. 7 | 8 | - Create a password file: 9 | echo "mypassword" > /etc/sysrqd.secret 10 | chmod 0600 /etc/sysrqd.secret 11 | 12 | - Optionnaly, create a bind file to bind to a specific IP 13 | echo 192.168.2.13 > /etc/sysrqd.bind 14 | 15 | - Add it to your local daemons startup. 16 | 17 | 18 | If you need to tweak some paramaters, take a look on define at the beginning 19 | of sysrqd.c 20 | 21 | You can add this lines to /etc/services: 22 | sysrqd 4094/tcp # sysrq daemon 23 | sysrqd 4094/udp # sysrq daemon 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VERSION=$(shell grep '^Version' ChangeLog | head -n 1 | cut -d' ' -f2 | tr -d ' ') 2 | BIN=sysrqd 3 | O=sysrqd.o 4 | CFLAGS+=-W -Wall -Wextra \ 5 | -Wundef -Wshadow -Wcast-align -Wwrite-strings -Wsign-compare \ 6 | -Wunused -Winit-self -Wpointer-arith -Wredundant-decls \ 7 | -Wmissing-prototypes -Wmissing-format-attribute -Wmissing-noreturn \ 8 | -std=gnu99 -pipe -DSYSRQD_VERSION="\"$(VERSION)\"" -O3 9 | LDFLAGS+=-lcrypt 10 | 11 | SBINDIR=$(DESTDIR)/usr/sbin 12 | #MANDIR=$(DESTDIR)/usr/share/man/man1 13 | INSTALL = install 14 | #MAN=sysrqd.1 15 | 16 | $(BIN): $(O) 17 | $(CC) -o $(BIN) $(O) $(LDFLAGS) 18 | 19 | install: $(BIN) 20 | $(INSTALL) -d -m 755 $(SBINDIR) 21 | $(INSTALL) -m 755 $(BIN) $(SBINDIR) 22 | 23 | #$(INSTALL) -d -m 755 $(MANDIR) 24 | #$(INSTALL) -m 644 $(MAN) $(MANDIR) 25 | 26 | clean: 27 | rm -f *~ $(O) $(BIN) 28 | 29 | release: clean 30 | mkdir ../$(BIN)-$(VERSION) 31 | cp -a * ../$(BIN)-$(VERSION) 32 | cd .. && tar czf $(BIN)-$(VERSION).tar.gz $(BIN)-$(VERSION) 33 | rm -rf ../$(BIN)-$(VERSION) 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | _ 2 | ___ _ _ ___ _ __ __ _ __| | 3 | / __| | | / __| '__/ _` |/ _` | 4 | \__ \ |_| \__ \ | | (_| | (_| | 5 | |___/\__, |___/_| \__, |\__,_| 6 | |___/ |_| 7 | 8 | « Don't let me down. » -- The Beatles 9 | 10 | 11 | sysrqd is a small daemon intended to manage 12 | [Linux SysRq](https://en.wikipedia.org/wiki/Magic_SysRq_key) over the network. 13 | Its philosophy is to be very responsive under heavy load and to try to be 14 | somehow reliable. Authentication is made by clear password. 15 | 16 | Connection should be made to port 4094. 17 | 18 | # Demo 19 | 20 | % telnet localhost 4094 21 | Trying 127.0.0.1... 22 | Connected to localhost.localdomain. 23 | Escape character is '^]'. 24 | sysrqd password: hello 25 | sysrq> s 26 | sysrq> u 27 | sysrq> q 28 | 29 | # Warning 30 | 31 | Please, be careful if you use 'e' (tErm) and 'i' (kIll). This will kill all 32 | processes, including sysrqd! 33 | -------------------------------------------------------------------------------- /sysrqd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * sysrqd.c - Daemon to control sysrq over network 3 | * 4 | * © 2005-2009 Julien Danjou 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; version 2 dated June, 1991. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #define PASS_MAX_LEN 256 41 | #define BIND_MAX_LEN 16 42 | #define PROMPT "sysrq> " 43 | #define SYSRQ_TRIGGER_PATH "/proc/sysrq-trigger" 44 | #define AUTH_FILE "/etc/sysrqd.secret" 45 | #define BINDIP_FILE "/etc/sysrqd.bind" 46 | #define PID_FILE "/var/run/sysrqd.pid" 47 | #define SYSRQD_PRIO -19 48 | #define SYSRQD_LISTEN_PORT 4094 49 | 50 | char pwd[PASS_MAX_LEN]; 51 | int sock_serv; 52 | 53 | /* Macro used to write a string to the client */ 54 | #define write_cli(s) \ 55 | write(sock_client, s, sizeof(s) - 1); 56 | 57 | /* Macro used to write the prompt */ 58 | #define write_prompt(s) \ 59 | write_cli(PROMPT); 60 | 61 | #define errmsg(s) \ 62 | fprintf(stderr, "%s (%s:%d)\n", s, __FILE__, __LINE__); 63 | 64 | /* Authenticate the connection */ 65 | static int 66 | auth (int sock_client) 67 | { 68 | char buf[PASS_MAX_LEN]; 69 | size_t len; 70 | char *crypt_result; 71 | 72 | write_cli("sysrqd password: "); 73 | memset(buf, 0, sizeof(buf)); 74 | 75 | /* Read password */ 76 | len = read(sock_client, buf, sizeof(buf) - 1); 77 | 78 | /* remove \r and \n at end */ 79 | while(len > 0 80 | && (buf[len - 1] == '\n' || buf[len - 1] == '\r')) 81 | len--; 82 | 83 | /* If password has been sent */ 84 | if(len > 0) 85 | { 86 | buf[len] = '\0'; 87 | /* Check if our stored password is crypted, i.e. starts with $[0-9]$ */ 88 | if(strlen(pwd) >= 34 && pwd[0] == '$' && pwd[2] == '$' && pwd[1] >= '0' && pwd[1] <= '9') 89 | { 90 | crypt_result = crypt(buf, pwd); 91 | if(!strcmp(crypt_result, pwd)) 92 | return 1; 93 | else 94 | write_cli("Go away!\r\n"); 95 | } else { 96 | if(!strcmp(buf, pwd)) 97 | return 1; 98 | else 99 | write_cli("Go away!\r\n"); 100 | } 101 | } 102 | 103 | return 0; 104 | } 105 | 106 | /* Read a configuration file */ 107 | static int 108 | read_conffile (const char *file, char* buf, size_t buflen) 109 | { 110 | int fd; 111 | 112 | if((fd = open (file, O_RDONLY)) == -1) 113 | { 114 | syslog(LOG_ERR, "Unable to open file %s: %s", 115 | file, 116 | strerror(errno)); 117 | return 1; 118 | } 119 | 120 | memset(buf, 0, buflen); 121 | read (fd, buf, buflen - 1); 122 | close (fd); 123 | 124 | buf[buflen - 1] = '\0'; 125 | 126 | /* Strip last \n */ 127 | char *tmp; 128 | if((tmp = strchr(buf, '\n'))) 129 | *tmp = '\0'; 130 | 131 | return 0; 132 | } 133 | 134 | /* Read commands */ 135 | static void 136 | read_cmd (int sock_client, int fd_sysrq) 137 | { 138 | char buf = 0; 139 | 140 | write_prompt(); 141 | do 142 | { 143 | if(buf == '\n') 144 | write_prompt(); 145 | 146 | if((buf >= 48 && buf <= 57) || 147 | (buf >= 97 && buf <= 122 )) 148 | write(fd_sysrq, &buf, 1); 149 | } 150 | while(read (sock_client, &buf, 1) == 1 && buf != 'q'); 151 | } 152 | 153 | 154 | /* 155 | * Listen to a port, 156 | * authenticate connection 157 | * and execute commands 158 | */ 159 | static int 160 | start_listen (int fd_sysrq) 161 | { 162 | int sock_client; 163 | socklen_t size_addr; 164 | struct sockaddr_in addr; 165 | struct sockaddr_in addr_client; 166 | int opt; 167 | char bindip[BIND_MAX_LEN]; 168 | 169 | addr.sin_family = AF_INET; 170 | addr.sin_port = htons(SYSRQD_LISTEN_PORT); 171 | 172 | if(read_conffile(BINDIP_FILE, bindip, sizeof(bindip))) 173 | addr.sin_addr.s_addr = INADDR_ANY; 174 | else if(!inet_aton(bindip, &addr.sin_addr)) 175 | { 176 | syslog(LOG_ERR, "Unable to convert IP: %s, using INADDR_ANY", 177 | strerror(errno)); 178 | addr.sin_addr.s_addr = INADDR_ANY; 179 | } 180 | 181 | if(!(sock_serv = socket (PF_INET, SOCK_STREAM, 0))) 182 | { 183 | syslog(LOG_ERR, "Error while creating server socket: %s", 184 | strerror(errno)); 185 | return 1; 186 | } 187 | 188 | /* We tries to avoid "socket already in use" errors */ 189 | opt = 1; 190 | setsockopt(sock_serv, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)); 191 | 192 | if(bind (sock_serv, (struct sockaddr *) &addr, sizeof (addr))) 193 | { 194 | syslog(LOG_ERR, "Unable to bind(): %s", 195 | strerror(errno)); 196 | return 1; 197 | } 198 | 199 | if(listen(sock_serv, 2)) 200 | { 201 | syslog(LOG_ERR, "Unable to listen(): %s", 202 | strerror(errno)); 203 | return 1; 204 | } 205 | 206 | size_addr = sizeof (addr_client); 207 | 208 | syslog(LOG_INFO, "Listening on port tcp/%d", SYSRQD_LISTEN_PORT); 209 | 210 | while((sock_client = accept (sock_serv, (struct sockaddr *) &addr_client, &size_addr))) 211 | { 212 | if(auth (sock_client)) 213 | read_cmd (sock_client, fd_sysrq); 214 | close(sock_client); 215 | } 216 | 217 | close (sock_serv); 218 | 219 | return 0; 220 | } 221 | 222 | static void __attribute__ ((noreturn)) 223 | signal_handler (void) 224 | { 225 | close (sock_serv); 226 | exit (EXIT_FAILURE); 227 | } 228 | 229 | static int 230 | catch_signals (void) 231 | { 232 | struct sigaction sa; 233 | sigset_t mask; 234 | 235 | sigfillset (&mask); 236 | 237 | sa.sa_handler = (void *) &signal_handler; 238 | sa.sa_mask = mask; 239 | 240 | sigaction (SIGINT, &sa, NULL); 241 | sigaction (SIGTERM, &sa, NULL); 242 | 243 | /* ignore sigpipe */ 244 | sigemptyset(&mask); 245 | sigaddset(&mask, SIGPIPE); 246 | sigprocmask(SIG_BLOCK, &mask, NULL); 247 | 248 | return 0; 249 | } 250 | 251 | 252 | static int 253 | write_pidfile(pid_t pid) 254 | { 255 | FILE *pidf = fopen(PID_FILE, "w"); 256 | if (pidf == NULL) 257 | return 1; 258 | 259 | fprintf(pidf, "%d\n", pid); 260 | fclose(pidf); 261 | 262 | return 0; 263 | } 264 | 265 | static void 266 | do_on_exit(void) 267 | { 268 | syslog(LOG_NOTICE, "Exiting"); 269 | } 270 | 271 | int 272 | main (void) 273 | { 274 | int fd_sysrq; 275 | 276 | atexit(do_on_exit); 277 | 278 | /* We test if it is worth the pain to fork if setpriority would fail */ 279 | if(getuid() != 0) 280 | { 281 | errmsg ("Only root can run this program."); 282 | return 1; 283 | } 284 | 285 | /* We read our password */ 286 | if(read_conffile (AUTH_FILE, pwd, sizeof(pwd))) 287 | return EXIT_FAILURE; 288 | 289 | /* mlock, we want this to always run */ 290 | mlockall(MCL_CURRENT | MCL_FUTURE); 291 | 292 | /* We daemonize */ 293 | daemon(0, 0); 294 | 295 | openlog ("sysrqd", LOG_PID, LOG_DAEMON); 296 | syslog(LOG_PID | LOG_DAEMON, "Starting"); 297 | 298 | if(write_pidfile(getpid())) 299 | syslog (LOG_PID | LOG_DAEMON, "Unable to write pidfile"); 300 | 301 | /* We set our priority */ 302 | if(setpriority (PRIO_PROCESS, 0, SYSRQD_PRIO)) 303 | { 304 | syslog (LOG_PID | LOG_DAEMON, "Unable to set priority: %s", 305 | strerror(errno)); 306 | return EXIT_FAILURE; 307 | } 308 | 309 | /* Catch some signals */ 310 | catch_signals (); 311 | 312 | /* We keep the sysrq-trigger file opened */ 313 | fd_sysrq = open(SYSRQ_TRIGGER_PATH, O_SYNC|O_WRONLY); 314 | if(fd_sysrq == -1) 315 | { 316 | syslog(LOG_ERR, "Error while opening sysrq trigger: %s", 317 | strerror(errno)); 318 | return EXIT_FAILURE; 319 | } 320 | 321 | /* Now listen */ 322 | if(!start_listen (fd_sysrq)) 323 | return EXIT_FAILURE; 324 | 325 | /* If we quit, close the trigger */ 326 | close (fd_sysrq); 327 | 328 | closelog(); 329 | 330 | return EXIT_SUCCESS; 331 | } 332 | --------------------------------------------------------------------------------