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