modules/pc/protocol_config.c
/* [<][>][^][v][top][bottom][index][help] */
FUNCTIONS
This source file includes following functions.
- find_command
- show_commands
- command_help
- command_quit
- show_const
- show_consts
- show_props
- show_thread
- show_whois
- show_access
- show_acl
- command_execute
- command_show
- command_repeat
- set_const
- set_consts
- set_props
- set_ban
- command_set
- command_sql
- process_input
- log_config
- authenticate_user
- PC_interact
1 /***************************************
2 $Revision: 1.19 $
3
4 Protocol config module (pc). This is the protocol that the admin uses to
5 talk to the server.
6
7 Status: NOT REVUED, NOT TESTED
8
9 ******************/ /******************
10 Filename : protocol_config.c
11 Authors : ottrey@ripe.net
12 marek@ripe.net
13 To Do : Add a facility to take callbacks instead of
14 hard-coding menu options.
15 Add in all the menu support provided by the GLib
16 libraries.
17 (Remove strtok if multiple threads are to be used.)
18 use gnu readline with expansion and history
19 ******************/ /******************
20 Copyright (c) 1999 RIPE NCC
21
22 All Rights Reserved
23
24 Permission to use, copy, modify, and distribute this software and its
25 documentation for any purpose and without fee is hereby granted,
26 provided that the above copyright notice appear in all copies and that
27 both that copyright notice and this permission notice appear in
28 supporting documentation, and that the name of the author not be
29 used in advertising or publicity pertaining to distribution of the
30 software without specific, written prior permission.
31
32 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
33 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
34 AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
35 DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
36 AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
37 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
38 ***************************************/
39 #include <stdio.h>
40 #include <stdlib.h>
41 /*** solaris' header file doesn't contain the crypt definition...
42 #include <unistd.h> */
43
44 extern char* crypt(const char *, const char *); /* crypt stuff */
45 #include <time.h> /* Time stuff */
46 #include <sys/ioctl.h> /* Terminal control stuff */
47 #include <termio.h> /* Terminal control stuff */
48
49 #include "mysql_driver.h"
50 #include "constants.h"
51 #include "properties.h"
52 #include "thread.h"
53 #include "protocol_config.h"
54 #include "access_control.h"
55 #include "socket.h"
56
57 /*+ Each command has a +*/
58 typedef struct _command {
59 const char *name; /*+ Name to be invoked. +*/
60 char *(*function)(char *, sk_conn_st *); /*+ Function to be invoked. +*/
61 const char *help; /*+ Command help. +*/
62 } Command;
63
64 /*
65 * Forward declarations
66 */
67 static char *command_help(char *input, sk_conn_st *condat);
68 static char *command_quit(char *input, sk_conn_st *condat);
69 static char *command_show(char *input, sk_conn_st *condat);
70 static char *command_repeat(char *input, sk_conn_st *condat);
71 static char *show_const(char *input, sk_conn_st *condat);
72 static char *show_consts(char *input, sk_conn_st *condat);
73 static char *show_props(char *input, sk_conn_st *condat);
74 static char *show_thread(char *input, sk_conn_st *condat);
75 static char *show_whois(char *input, sk_conn_st *condat);
76 static char *show_access(char *input, sk_conn_st *condat);
77 static char *show_acl(char *input, sk_conn_st *condat);
78 static char *command_set(char *input, sk_conn_st *condat);
79 static char *set_const(char *input, sk_conn_st *condat);
80 static char *set_consts(char *input, sk_conn_st *condat);
81 static char *set_props(char *input, sk_conn_st *condat);
82 static char *command_sql(char *input, sk_conn_st *condat);
83 static char *set_ban(char *input, sk_conn_st *condat);
84
85 /*+
86 * Contains the command definitions
87 +*/
88 static struct _command command[] = {
89 {"help" , command_help , HELP_HELP },
90 {"quit" , command_quit , HELP_QUIT },
91 {"show" , command_show , HELP_SHOW },
92 {"repeat" , command_repeat , HELP_REPEAT },
93 {"set" , command_set , HELP_SET },
94 {"sql" , command_sql , HELP_SQL },
95 {NULL , NULL , NULL }
96 };
97
98 /*+
99 * Contains the show commands
100 +*/
101 static struct _command show[] = {
102 {"const" , show_const , HELP_SHOW_CONST },
103 {"consts" , show_consts , HELP_SHOW_CONSTS },
104 {"props" , show_props , HELP_SHOW_PROPS },
105 {"thread" , show_thread , HELP_SHOW_THREAD },
106 {"whois" , show_whois , HELP_SHOW_WHOIS },
107 {"access" , show_access , HELP_SHOW_ACCESS },
108 {"acl" , show_acl , HELP_SHOW_ACL },
109 {NULL , NULL , NULL }
110 };
111
112 /*+
113 * Contains the set commands
114 +*/
115 static struct _command set[] = {
116 {"const" , set_const , HELP_SET_CONST },
117 {"consts" , set_consts , HELP_SET_CONSTS },
118 {"props" , set_props , HELP_SET_PROPS },
119 {"ban" , set_ban , HELP_SET_BAN },
120 {NULL , NULL , NULL }
121 };
122
123 static int find_command(char *comm_name, Command *comm) {
/* [<][>][^][v][top][bottom][index][help] */
124 int i, index;
125 char comm_buffer[STR_L];
126
127 if (comm_name != NULL) {
128 strcpy(comm_buffer, comm_name);
129 strtok(comm_buffer, " \t");
130 for (i=0, index=-1; comm[i].name != NULL; i++) {
131 if ( strcmp(comm_buffer, comm[i].name) == 0) {
132 index = i;
133 break;
134 }
135 }
136 }
137 else {
138 index = -2;
139 }
140
141 return index;
142 } /* find_command() */
143
144 static char *show_commands(Command *comm) {
/* [<][>][^][v][top][bottom][index][help] */
145 char *str;
146 char help_buffer[STR_XL];
147 char help_comm[STR_M];
148 int i;
149
150 sprintf(help_buffer, " commands are:\n\n");
151 i = 0;
152 while (comm[i].name != NULL) {
153 sprintf(help_comm, "%s\t%s\n", comm[i].name, comm[i].help);
154 strcat(help_buffer, help_comm);
155 i++;
156 }
157
158 /* str = (char *)calloc(1, strlen(help_buffer)+1); */
159 dieif( wr_malloc((void **)&str, strlen(help_buffer)+1) != UT_OK);
160 strcpy(str, help_buffer);
161
162 return str;
163 } /* show_commands() */
164
165
166 /*
167 * Command functions
168 */
169 static char *command_help(char *input, sk_conn_st *condat) {
/* [<][>][^][v][top][bottom][index][help] */
170 char *str;
171 char *str1;
172 char output_buffer[STR_XXL];
173 char *command_name;
174 int index;
175
176 strcpy(output_buffer, "");
177
178 strtok(input, " \t");
179 command_name = (char *)strtok(NULL, " \t");
180
181 index = find_command(command_name, command);
182
183 switch (index) {
184 case -2:
185 strcat(output_buffer, "Main");
186 str1 = show_commands(command);
187 strcat(output_buffer, str1);
188 wr_free(str1);
189 break;
190
191 case -1:
192 strcat(output_buffer, HELP_ERROR);
193 strcat(output_buffer, command_name);
194 break;
195
196 default:
197 strcat(output_buffer, command[index].help);
198 }
199
200 /*
201 str = (char *)CopyString(output_buffer);
202 */
203 /* str = (char *)calloc(1, strlen(output_buffer)+1); */
204 dieif( wr_malloc((void **)&str, strlen(output_buffer)+1) != UT_OK);
205 strcpy(str, output_buffer);
206
207 return str;
208 } /* command_help() */
209
210 static char *command_quit(char *input, sk_conn_st *condat) {
/* [<][>][^][v][top][bottom][index][help] */
211 /* Administrator wishes to quit. */
212 return NULL;
213 } /* command_quit() */
214
215 static char *show_const(char *input, sk_conn_st *condat) {
/* [<][>][^][v][top][bottom][index][help] */
216 /* Administrator wishes to show constants. */
217 char *result;
218 char *name;
219 char *tmp_input;
220
221 /* tmp_input = (char *)calloc(1, strlen(input)+1); */
222 dieif( wr_malloc((void **)&tmp_input, strlen(input)+1) != UT_OK);
223 strcpy(tmp_input, input);
224
225 /* The name will be the third token in stuff */
226 strtok(tmp_input, " ");
227 strtok(NULL, " ");
228 name = (char *)strtok(NULL, " ");
229
230 result = CO_const_to_string(name);
231
232 wr_free(tmp_input);
233 return result;
234
235 } /* show_const() */
236
237 static char *show_consts(char *input, sk_conn_st *condat) {
/* [<][>][^][v][top][bottom][index][help] */
238 /* Administrator wishes to show constants. */
239 return CO_to_string();
240
241 } /* show_consts() */
242
243 static char *show_props(char *input, sk_conn_st *condat) {
/* [<][>][^][v][top][bottom][index][help] */
244 /* Administrator wishes to show properties. */
245 return PR_to_string();
246
247 } /* show_props() */
248
249 static char *show_thread(char *input, sk_conn_st *condat) {
/* [<][>][^][v][top][bottom][index][help] */
250 /* Administrator wishes to show thread information. */
251 return TH_to_string();
252
253 } /* show_thread() */
254
255 static char *show_whois(char *input, sk_conn_st *condat) {
/* [<][>][^][v][top][bottom][index][help] */
256 /* Administrator wishes to show whois query information. */
257 return wr_string("WQ_to_string();");
258
259 } /* show_whois() */
260
261 static char *show_access(char *input, sk_conn_st *condat) {
/* [<][>][^][v][top][bottom][index][help] */
262 /* Administrator wishes to show whois query information. */
263
264 char line[128];
265 int cnt;
266 er_ret_t err;
267
268 if( act_runtime->top_ptr != NULL ) {
269 char *header = AC_to_string_header();
270
271 /* print header */
272 SK_cd_puts(condat,header);
273 wr_free(header);
274
275 cnt = rx_walk_tree(act_runtime->top_ptr, AC_rxwalkhook_print,
276 RX_WALK_SKPGLU, /* print no glue nodes */
277 255, 0, 0, condat, &err);
278 sprintf(line,"Found %d nodes\n", cnt);
279 SK_cd_puts(condat,line);
280 }
281
282 return wr_string("");
283
284 } /* show_access() */
285
286 static char *show_acl(char *input, sk_conn_st *condat) {
/* [<][>][^][v][top][bottom][index][help] */
287 /* Administrator wishes to show access control list. */
288
289 char line[128];
290 int cnt;
291 er_ret_t err;
292
293 if( act_acl->top_ptr != NULL ) {
294 char *header = AC_acl_to_string_header();
295
296 /* print header */
297 SK_cd_puts(condat,header);
298 wr_free(header);
299
300 cnt = rx_walk_tree(act_acl->top_ptr, AC_rxwalkhook_print_acl,
301 RX_WALK_SKPGLU, /* print no glue nodes */
302 255, 0, 0, condat, &err);
303 sprintf(line,"Found %d nodes\n", cnt);
304 SK_cd_puts(condat,line);
305 }
306
307 return wr_string("");
308
309 } /* show_acl() */
310
311 static char *command_execute(char *input, char *comm_name,
/* [<][>][^][v][top][bottom][index][help] */
312 Command *comm, sk_conn_st *condat) {
313 char *str;
314 char *str1;
315 char output_buffer[STR_XXL];
316 char *name;
317 int index;
318 char *tmp_input;
319
320 /* Make a copy of the input */
321 /* tmp_input = (char *)calloc(1, strlen(input)+1); */
322 dieif( wr_malloc((void **)&tmp_input, strlen(input)+1) != UT_OK);
323 strcpy(tmp_input, input);
324
325 strtok(tmp_input, " \t");
326 name = (char *)strtok(NULL, " \t");
327
328 index = find_command(name, comm);
329
330 switch (index) {
331 case -2:
332 str1 = show_commands(comm);
333 sprintf(output_buffer, "%s%s", comm_name, str1);
334 wr_free(str1);
335 break;
336
337 case -1:
338 sprintf(output_buffer, "%s invalid command: %s", comm_name, name);
339 break;
340
341 default:
342 sprintf(output_buffer, "%s", comm[index].function(input, condat));
343 }
344
345 /*
346 str = (char *)CopyString(output_buffer);
347 */
348 /* str = (char *)calloc(1, strlen(output_buffer)+1); */
349 dieif( wr_malloc((void **)&str, strlen(output_buffer)+1) != UT_OK);
350 strcpy(str, output_buffer);
351
352 wr_free(tmp_input);
353
354 return str;
355 } /* command_execute() */
356
357 static char *command_show(char *input, sk_conn_st *condat) {
/* [<][>][^][v][top][bottom][index][help] */
358 return command_execute(input, "Show", show, condat);
359 } /* command_show() */
360
361 static char *command_repeat(char *input, sk_conn_st *condat) {
/* [<][>][^][v][top][bottom][index][help] */
362 char *command_ptr;
363
364 /* Goto the bit after "repeat n " */
365 for (command_ptr=input+7; command_ptr[0] != ' ' || (command_ptr[0] >= '0' && command_ptr[0] <= '9'); command_ptr++);
366
367 return command_ptr+1;
368
369 } /* command_show() */
370
371 static char *set_const(char *input, sk_conn_st *condat) {
/* [<][>][^][v][top][bottom][index][help] */
372 /* Administrator wishes to set a constant. */
373 char *result;
374 char result_buf[STR_M];
375 char *tmp_input;
376 char *name;
377 char *value;
378 int value_len;
379 char *stuff;
380 char *str;
381
382 /* tmp_input = (char *)calloc(1, strlen(input)+1); */
383 dieif( wr_malloc((void **)&tmp_input, strlen(input)+1) != UT_OK);
384 strcpy(tmp_input, input);
385
386 stuff = (char *)strtok(tmp_input, "=");
387
388 /* The value will be after the '=' */
389 value = (char *)strtok(NULL, "=");
390
391 /* The name will be the third token in stuff */
392 strtok(stuff, " ");
393 strtok(NULL, " ");
394 name = (char *)strtok(NULL, " ");
395
396 /* Remove any quotes */
397 if (value[0] == '"') {
398 value++;
399 }
400 value_len=strlen(value);
401 if (value[value_len-1] == '"') {
402 value[value_len-1]='\0';
403 }
404
405 printf("set_const name=(%s), value=(%s)\n", name, value);
406 if (CO_set_const(name, value) == 0) {
407 strcpy(result_buf, "Constant successfully set\n");
408 }
409 else {
410 str = CO_const_to_string(name);
411 sprintf(result_buf, "Constant not successfully set\nReverting to: %s=%s\n", name, str);
412 wr_free(str);
413 }
414
415 /* result = (char *)calloc(1, strlen(result_buf)+1); */
416 dieif( wr_malloc((void **)&result, strlen(result_buf)+1) != UT_OK);
417 strcpy(result, result_buf);
418
419 wr_free(tmp_input);
420 return result;
421 } /* set_const() */
422
423 static char *set_consts(char *input, sk_conn_st *condat) {
/* [<][>][^][v][top][bottom][index][help] */
424 /* Administrator wishes to set constants. */
425 return CO_set();
426 } /* set_consts() */
427
428 static char *set_props(char *input, sk_conn_st *condat) {
/* [<][>][^][v][top][bottom][index][help] */
429 /* Administrator wishes to set properties. */
430 return PR_set();
431 } /* set_props() */
432
433 static char *set_ban(char *input, sk_conn_st *condat) {
/* [<][>][^][v][top][bottom][index][help] */
434 int flag;
435 char addrstr[128];
436
437 if( sscanf(input,"set ban %d %127s", &flag, addrstr) < 2) {
438 return wr_string("Invalid arguments");
439 }
440 else {
441 if( ! NOERR( AC_asc_ban_set( addrstr, "Manual", (flag!=0) ))) {
442 return wr_string("Error\n");
443 }
444 else {
445 return wr_string("OK");
446 }
447 }
448 }
449
450 static char *command_set(char *input, sk_conn_st *condat) {
/* [<][>][^][v][top][bottom][index][help] */
451 return command_execute(input, "Set", set, condat);
452 } /* command_set() */
453
454 static char *command_sql(char *input, sk_conn_st *condat) {
/* [<][>][^][v][top][bottom][index][help] */
455 char *str;
456 char output_buffer[STR_XXL];
457 char *sql_str;
458
459 char *res=NULL;
460
461 SQ_result_set_t *sql_result=NULL;
462 SQ_connection_t *sql_connection;
463
464 sql_connection = SQ_get_connection(CO_get_host(), CO_get_database_port(), CO_get_database(), CO_get_user(), CO_get_password() );
465
466 if (sql_connection == NULL) {
467 printf("/* Check for errors */\n");
468 }
469
470 /* skip over the "sql" */
471 sql_str = input+3;
472 /* skip over white space */
473 while (sql_str[0] == ' ') {
474 sql_str++;
475 }
476
477 strcpy(output_buffer, "");
478
479 if (sql_connection != NULL) {
480 if (strcmp(sql_str, "status") == 0) {
481 /* Get the status of the database */
482 res = SQ_info_to_string(sql_connection);
483 }
484 else {
485 if (strcmp(sql_str, "") == 0) {
486 /* Execute the default query (from the properties file) */
487 SQ_execute_query(sql_connection, CO_get_query(), &sql_result);
488 }
489 else {
490 /* Execute an sql query */
491 SQ_execute_query(sql_connection, sql_str, &sql_result);
492 }
493 if (sql_result != NULL) {
494 res = SQ_result_to_string(sql_result);
495 }
496 else {
497 printf("no results\n");
498 }
499 }
500 if (res != NULL) {
501 sprintf(output_buffer, "%s", res);
502 }
503 else {
504 printf("empty results\n");
505 }
506 }
507 else {
508 printf("Failed to make connection\n");
509 }
510
511 /*
512 strcat(output_buffer, mysql_info(sql_connection));
513 */
514
515 strcat(output_buffer, "XXX Results from mysql_info(sql_connection) is meant to go here. But it's not working!");
516
517 /*
518 str = (char *)CopyString(output_buffer);
519 */
520 /* str = (char *)calloc(1, strlen(output_buffer)+1); */
521 dieif( wr_malloc((void **)&str, strlen(output_buffer)+1) != UT_OK);
522 strcpy(str, output_buffer);
523
524 wr_free(res);
525 SQ_free_result(sql_result);
526
527 SQ_close_connection(sql_connection);
528
529 return str;
530
531 } /* command_sql() */
532
533
534 /* process_input() */
535 /*++++++++++++++++++++++++++++++++++++++
536
537 Process the input.
538
539 sk_conn_st *condat connection data
540
541 More:
542 +html+ <PRE>
543 Author:
544 ottrey
545 +html+ </PRE>
546 ++++++++++++++++++++++++++++++++++++++*/
547 static int process_input(char *input, sk_conn_st *condat) {
/* [<][>][^][v][top][bottom][index][help] */
548 int connected = 1;
549 char *input_ptr;
550 char *output;
551 int index;
552 int repeat=0;
553
554 input_ptr = input;
555
556 if (strncmp(input, "repeat", 6) == 0) {
557 /* XXX This is a really dodgy call, that hopefully converts
558 the string to the value of the first found integer. */
559 repeat = atoi(input+7);
560 input_ptr= command_repeat(input, condat);
561 }
562
563 index = find_command(input_ptr, command);
564
565 do {
566 switch (index) {
567 case -1:
568 /* Command not found */
569 output = command_help(NULL, condat);
570 break;
571
572 default:
573 output = command[index].function(input_ptr, condat);
574 }
575
576 if(output == NULL) {
577 connected = 0;
578 } else {
579 /*
580 printf("thread output=\n%s\n", output);
581 */
582 if ( CO_get_clear_screen() == 1 ) {
583 SK_cd_puts(condat, CLEAR_SCREEN);
584 }
585 SK_cd_puts(condat, output);
586 SK_cd_puts(condat, "\n");
587 SK_cd_puts(condat, CO_get_prompt());
588
589 wr_free(output);
590 }
591
592 if (repeat > 0) {
593 repeat--;
594 sleep(CO_get_sleep_time());
595 }
596
597 } while (repeat > 0);
598
599 return connected;
600
601 } /* process_input() */
602
603 static void log_config(const char *user, const char *status) {
/* [<][>][^][v][top][bottom][index][help] */
604 FILE *logf;
605 time_t now;
606 char timebuf[26];
607
608 time(&now);
609
610 if (CO_get_config_logging() == 1) {
611
612 if (strcmp(CO_get_config_logfile(), "stdout") == 0) {
613 printf(LOG_CONFIG, TH_get_id(), user, status, ctime_r(&now, timebuf));
614 }
615 else {
616 logf = fopen(CO_get_config_logfile(), "a");
617 fprintf(logf, LOG_CONFIG, TH_get_id(), user, status, ctime_r(&now, timebuf));
618 fclose(logf);
619 }
620 }
621
622 } /* log_config() */
623
624 /* XXX Doh! These only change the server's terminal. We need some
625 tricky escape sequence to send over the socket.
626 static void echo_off(int sock) {
627 struct termio state;
628
629 ioctl(0, TIOCGETP, &state);
630 state.c_lflag &= ~ECHO;
631 ioctl(0, TIOCSETP, &state);
632 } echo_off() */
633
634 /* XXX Doh! These only change the server's terminal. We need some
635 tricky escape sequence to send over the socket.
636 static void echo_on(int sock) {
637 struct termio state;
638
639 ioctl(0, TIOCGETP, &state);
640 state.c_lflag |= ECHO;
641 ioctl(0, TIOCSETP, &state);
642 } echo_on() */
643
644 static char *authenticate_user(sk_conn_st *condat) {
/* [<][>][^][v][top][bottom][index][help] */
645 char *user = NULL;
646 const char Salt[2] = "DB";
647 char input[MAX_INPUT_SIZE];
648 int read_result;
649 char *password=NULL;
650 char *user_password=NULL;
651 char user_buf[10];
652
653 SK_cd_puts(condat, LOGIN_PROMPT);
654 read_result = SK_cd_gets(condat, input, MAX_INPUT_SIZE);
655
656 strncpy(user_buf, input, 10);
657
658 SK_cd_puts(condat, PASSWD_PROMPT);
659 /* XXX These aren't working.
660 SK_puts(sock, ECHO_ON);
661 echo_off(sock);
662 */
663 read_result = SK_cd_gets(condat, input, MAX_INPUT_SIZE);
664 /* XXX These aren't working.
665 echo_on(sock);
666 SK_puts(sock, ECHO_OFF);
667 */
668
669 password = crypt(input, Salt);
670
671 user_password = PR_get_property(user_buf, DEFAULT_USER_NAME);
672
673 if (user_password != NULL) {
674 if (strcmp(password, user_password) == 0) {
675 /*user = (char *)calloc(1, strlen(user_buf)+1);*/
676 dieif( wr_malloc((void **)&user, strlen(user_buf)+1) != UT_OK);
677 strcpy(user, user_buf);
678 }
679 }
680
681 if (user == NULL) {
682 log_config(user_buf, "unsuccesful login attempt");
683 }
684
685 return user;
686
687 } /* authenticate_user() */
688
689 void PC_interact(int sock) {
/* [<][>][^][v][top][bottom][index][help] */
690 char input[MAX_INPUT_SIZE];
691 int connected = 1;
692 char *user=NULL;
693 sk_conn_st condat;
694
695 memset( &condat, 0, sizeof(condat));
696 condat.sock = sock;
697 SK_getpeerip(sock, &(condat.rIP));
698 condat.ip = SK_getpeername(sock); /* XXX *alloc involved */
699
700 /* Welcome the client */
701 SK_cd_puts(&condat, CO_get_welcome());
702
703 /* Authenticate the user */
704 if (CO_get_authenticate() == 1) {
705 user = authenticate_user(&condat);
706 }
707 else {
708 user="nobody";
709 }
710
711 if (user != NULL) {
712
713
714 /* Log admin logging on */
715 log_config(user, "logged on");
716
717
718 {
719 char timestring[26];
720 extern time_t SV_starttime;
721
722 ctime_r(&SV_starttime, timestring);
723 SK_cd_printf(&condat,
724 "System running since %sUptime in seconds: %ld \n\n",
725 timestring,
726 time(NULL) - SV_starttime);
727 }
728
729 SK_cd_puts(&condat, CO_get_prompt());
730
731 while (condat.rtc==0 && connected) {
732 /* Read input */
733 SK_cd_gets(&condat, input, MAX_INPUT_SIZE);
734 connected = process_input(input, &condat);
735 }
736
737 /* Log admin logging off */
738 log_config(user, "logged off");
739 }
740
741 /* Close the socket */
742 SK_close(sock);
743
744 wr_free(condat.ip);
745 } /* PC_interact() */
746