diff options
-rw-r--r-- | include/libbb.h | 6 | ||||
-rw-r--r-- | init/halt.c | 15 | ||||
-rw-r--r-- | libbb/utmp.c | 123 | ||||
-rw-r--r-- | loginutils/getty.c | 8 | ||||
-rw-r--r-- | loginutils/login.c | 2 | ||||
-rw-r--r-- | miscutils/last.c | 3 | ||||
-rw-r--r-- | networking/telnetd.c | 33 |
7 files changed, 134 insertions, 56 deletions
diff --git a/include/libbb.h b/include/libbb.h index ea61701b7..f3121eb47 100644 --- a/include/libbb.h +++ b/include/libbb.h | |||
@@ -797,9 +797,11 @@ void die_if_bad_username(const char* name) FAST_FUNC; | |||
797 | #endif | 797 | #endif |
798 | 798 | ||
799 | #if ENABLE_FEATURE_UTMP | 799 | #if ENABLE_FEATURE_UTMP |
800 | void FAST_FUNC update_utmp(int new_type, const char *short_tty, const char *username, const char *opt_host); | 800 | void FAST_FUNC write_new_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname); |
801 | void FAST_FUNC update_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname); | ||
801 | #else | 802 | #else |
802 | # define update_utmp(new_type, short_tty, username, opt_host) ((void)0) | 803 | # define write_new_utmp(pid, new_type, tty_name, username, hostname) ((void)0) |
804 | # define update_utmp(pid, new_type, tty_name, username, hostname) ((void)0) | ||
803 | #endif | 805 | #endif |
804 | 806 | ||
805 | int execable_file(const char *name) FAST_FUNC; | 807 | int execable_file(const char *name) FAST_FUNC; |
diff --git a/init/halt.c b/init/halt.c index a3459ee48..f1bb2c4a8 100644 --- a/init/halt.c +++ b/init/halt.c | |||
@@ -18,17 +18,18 @@ static void write_wtmp(void) | |||
18 | { | 18 | { |
19 | struct utmp utmp; | 19 | struct utmp utmp; |
20 | struct utsname uts; | 20 | struct utsname uts; |
21 | if (access(bb_path_wtmp_file, R_OK|W_OK) == -1) { | 21 | /* "man utmp" says wtmp file should *not* be created automagically */ |
22 | /*if (access(bb_path_wtmp_file, R_OK|W_OK) == -1) { | ||
22 | close(creat(bb_path_wtmp_file, 0664)); | 23 | close(creat(bb_path_wtmp_file, 0664)); |
23 | } | 24 | }*/ |
24 | memset(&utmp, 0, sizeof(utmp)); | 25 | memset(&utmp, 0, sizeof(utmp)); |
25 | utmp.ut_tv.tv_sec = time(NULL); | 26 | utmp.ut_tv.tv_sec = time(NULL); |
26 | safe_strncpy(utmp.ut_user, "shutdown", UT_NAMESIZE); | 27 | strcpy(utmp.ut_user, "shutdown"); /* it is wide enough */ |
27 | utmp.ut_type = RUN_LVL; | 28 | utmp.ut_type = RUN_LVL; |
28 | safe_strncpy(utmp.ut_id, "~~", sizeof(utmp.ut_id)); | 29 | utmp.ut_id[0] = '~'; utmp.ut_id[1] = '~'; /* = strcpy(utmp.ut_id, "~~"); */ |
29 | safe_strncpy(utmp.ut_line, "~~", UT_LINESIZE); | 30 | utmp.ut_line[0] = '~'; utmp.ut_line[1] = '~'; /* = strcpy(utmp.ut_line, "~~"); */ |
30 | if (uname(&uts) == 0) | 31 | uname(&uts); |
31 | safe_strncpy(utmp.ut_host, uts.release, sizeof(utmp.ut_host)); | 32 | safe_strncpy(utmp.ut_host, uts.release, sizeof(utmp.ut_host)); |
32 | updwtmp(bb_path_wtmp_file, &utmp); | 33 | updwtmp(bb_path_wtmp_file, &utmp); |
33 | } | 34 | } |
34 | #else | 35 | #else |
diff --git a/libbb/utmp.c b/libbb/utmp.c index 06b939b9d..d6ba336e3 100644 --- a/libbb/utmp.c +++ b/libbb/utmp.c | |||
@@ -15,37 +15,87 @@ static void touch(const char *filename) | |||
15 | close(open(filename, O_WRONLY | O_CREAT, 0664)); | 15 | close(open(filename, O_WRONLY | O_CREAT, 0664)); |
16 | } | 16 | } |
17 | 17 | ||
18 | // TODO: move to libbb | ||
19 | static const char* skip_dev_pfx(const char *tty_name) | ||
20 | { | ||
21 | if (strncmp(tty_name, "/dev/", 5) == 0) | ||
22 | tty_name += 5; | ||
23 | return tty_name; | ||
24 | } | ||
25 | |||
26 | void FAST_FUNC write_new_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname) | ||
27 | { | ||
28 | struct utmp utent; | ||
29 | char *id; | ||
30 | unsigned width; | ||
31 | |||
32 | memset(&utent, 0, sizeof(utent)); | ||
33 | utent.ut_pid = pid; | ||
34 | utent.ut_type = new_type; | ||
35 | tty_name = skip_dev_pfx(tty_name); | ||
36 | safe_strncpy(utent.ut_line, tty_name, sizeof(utent.ut_line)); | ||
37 | if (username) | ||
38 | safe_strncpy(utent.ut_user, username, sizeof(utent.ut_user)); | ||
39 | if (hostname) | ||
40 | safe_strncpy(utent.ut_host, hostname, sizeof(utent.ut_host)); | ||
41 | utent.ut_tv.tv_sec = time(NULL); | ||
42 | |||
43 | /* Invent our own ut_id. ut_id is only 4 chars wide. | ||
44 | * Try to fit something remotely meaningful... */ | ||
45 | id = utent.ut_id; | ||
46 | width = sizeof(utent.ut_id); | ||
47 | if (tty_name[0] == 'p') { | ||
48 | /* if "ptyXXX", map to "pXXX" */ | ||
49 | /* if "pts/XX", map to "p/XX" */ | ||
50 | *id++ = 'p'; | ||
51 | width--; | ||
52 | } /* else: usually it's "ttyXXXX", map to "XXXX" */ | ||
53 | if (strlen(tty_name) > 3) | ||
54 | tty_name += 3; | ||
55 | strncpy(id, tty_name, width); | ||
56 | |||
57 | touch(_PATH_UTMP); | ||
58 | //utmpname(_PATH_UTMP); | ||
59 | setutent(); | ||
60 | /* Append new one (hopefully, unless we collide on ut_id) */ | ||
61 | pututline(&utent); | ||
62 | endutent(); | ||
63 | |||
64 | #if ENABLE_FEATURE_WTMP | ||
65 | /* "man utmp" says wtmp file should *not* be created automagically */ | ||
66 | /*touch(bb_path_wtmp_file);*/ | ||
67 | updwtmp(bb_path_wtmp_file, &utent); | ||
68 | #endif | ||
69 | } | ||
70 | |||
18 | /* | 71 | /* |
19 | * Read "man utmp" to make sense out of it. | 72 | * Read "man utmp" to make sense out of it. |
20 | */ | 73 | */ |
21 | void FAST_FUNC update_utmp(int new_type, const char *short_tty, const char *username, const char *opt_host) | 74 | void FAST_FUNC update_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname) |
22 | { | 75 | { |
23 | struct utmp utent; | 76 | struct utmp utent; |
24 | struct utmp *ut; | 77 | struct utmp *utp; |
25 | pid_t pid; | ||
26 | 78 | ||
27 | touch(_PATH_UTMP); | 79 | touch(_PATH_UTMP); |
28 | utmpname(_PATH_UTMP); | 80 | //utmpname(_PATH_UTMP); |
29 | setutent(); | 81 | setutent(); |
30 | 82 | ||
31 | pid = getpid(); | ||
32 | /* Did init/getty/telnetd/sshd/... create an entry for us? | 83 | /* Did init/getty/telnetd/sshd/... create an entry for us? |
33 | * It should be (new_type-1), but we'd also reuse | 84 | * It should be (new_type-1), but we'd also reuse |
34 | * any other potentially stale xxx_PROCESS entry */ | 85 | * any other potentially stale xxx_PROCESS entry */ |
35 | while ((ut = getutent()) != NULL) { | 86 | while ((utp = getutent()) != NULL) { |
36 | if (ut->ut_pid == pid | 87 | if (utp->ut_pid == pid |
37 | // && ut->ut_line[0] | 88 | // && ut->ut_line[0] |
38 | && ut->ut_id[0] /* must have nonzero id */ | 89 | && utp->ut_id[0] /* must have nonzero id */ |
39 | && ( ut->ut_type == INIT_PROCESS | 90 | && ( utp->ut_type == INIT_PROCESS |
40 | || ut->ut_type == LOGIN_PROCESS | 91 | || utp->ut_type == LOGIN_PROCESS |
41 | || ut->ut_type == USER_PROCESS | 92 | || utp->ut_type == USER_PROCESS |
42 | || ut->ut_type == DEAD_PROCESS | 93 | || utp->ut_type == DEAD_PROCESS |
43 | ) | 94 | ) |
44 | ) { | 95 | ) { |
45 | utent = *ut; /* struct copy */ | 96 | if (utp->ut_type >= new_type) { |
46 | if (ut->ut_type >= new_type) { | ||
47 | /* Stale record. Nuke hostname */ | 97 | /* Stale record. Nuke hostname */ |
48 | memset(utent.ut_host, 0, sizeof(utent.ut_host)); | 98 | memset(utp->ut_host, 0, sizeof(utp->ut_host)); |
49 | } | 99 | } |
50 | /* NB: pututline (see later) searches for matching utent | 100 | /* NB: pututline (see later) searches for matching utent |
51 | * using getutid(utent) - we must not change ut_id | 101 | * using getutid(utent) - we must not change ut_id |
@@ -54,39 +104,38 @@ void FAST_FUNC update_utmp(int new_type, const char *short_tty, const char *user | |||
54 | break; | 104 | break; |
55 | } | 105 | } |
56 | } | 106 | } |
57 | endutent(); | 107 | //endutent(); - no need, pututline can deal with (and actually likes) |
108 | //the situation when utmp file is positioned on found record | ||
58 | 109 | ||
59 | if (!ut) { | 110 | if (!utp) { |
60 | /* Didn't find anything, create new one */ | 111 | if (new_type != DEAD_PROCESS) |
61 | memset(&utent, 0, sizeof(utent)); | 112 | write_new_utmp(pid, new_type, tty_name, username, hostname); |
62 | utent.ut_pid = pid; | 113 | else |
63 | /* Invent our own ut_id. ut_id is only 4 chars wide. | 114 | endutent(); |
64 | * Try to fit something remotely meaningful... */ | 115 | return; |
65 | if (short_tty[0] == 'p') { | ||
66 | /* if "ptyXXX", map to "pXXX" */ | ||
67 | /* if "pts/XX", map to "p/XX" */ | ||
68 | utent.ut_id[0] = 'p'; | ||
69 | strncpy(utent.ut_id + 1, short_tty + 3, sizeof(utent.ut_id)-1); | ||
70 | } else { | ||
71 | /* assuming it's "ttyXXXX", map to "XXXX" */ | ||
72 | strncpy(utent.ut_id, short_tty + 3, sizeof(utent.ut_id)); | ||
73 | } | ||
74 | } | 116 | } |
75 | 117 | ||
118 | /* Make a copy. We can't use *utp, pututline's internal getutid | ||
119 | * will overwrite it before it is used! */ | ||
120 | utent = *utp; | ||
121 | |||
76 | utent.ut_type = new_type; | 122 | utent.ut_type = new_type; |
77 | safe_strncpy(utent.ut_line, short_tty, sizeof(utent.ut_line)); | 123 | if (tty_name) |
78 | safe_strncpy(utent.ut_user, username, sizeof(utent.ut_user)); | 124 | safe_strncpy(utent.ut_line, skip_dev_pfx(tty_name), sizeof(utent.ut_line)); |
79 | if (opt_host) | 125 | if (username) |
80 | safe_strncpy(utent.ut_host, opt_host, sizeof(utent.ut_host)); | 126 | safe_strncpy(utent.ut_user, username, sizeof(utent.ut_user)); |
127 | if (hostname) | ||
128 | safe_strncpy(utent.ut_host, hostname, sizeof(utent.ut_host)); | ||
81 | utent.ut_tv.tv_sec = time(NULL); | 129 | utent.ut_tv.tv_sec = time(NULL); |
82 | 130 | ||
83 | /* Update, or append new one */ | 131 | /* Update, or append new one */ |
84 | setutent(); | 132 | //setutent(); |
85 | pututline(&utent); | 133 | pututline(&utent); |
86 | endutent(); | 134 | endutent(); |
87 | 135 | ||
88 | #if ENABLE_FEATURE_WTMP | 136 | #if ENABLE_FEATURE_WTMP |
89 | touch(bb_path_wtmp_file); | 137 | /* "man utmp" says wtmp file should *not* be created automagically */ |
138 | /*touch(bb_path_wtmp_file);*/ | ||
90 | updwtmp(bb_path_wtmp_file, &utent); | 139 | updwtmp(bb_path_wtmp_file, &utent); |
91 | #endif | 140 | #endif |
92 | } | 141 | } |
diff --git a/loginutils/getty.c b/loginutils/getty.c index 8d1d5254e..d032357e2 100644 --- a/loginutils/getty.c +++ b/loginutils/getty.c | |||
@@ -19,7 +19,7 @@ | |||
19 | #include <syslog.h> | 19 | #include <syslog.h> |
20 | 20 | ||
21 | #if ENABLE_FEATURE_UTMP | 21 | #if ENABLE_FEATURE_UTMP |
22 | #include <utmp.h> /* LOGIN_PROCESS */ | 22 | # include <utmp.h> /* LOGIN_PROCESS */ |
23 | #endif | 23 | #endif |
24 | 24 | ||
25 | #ifndef IUCLC | 25 | #ifndef IUCLC |
@@ -579,6 +579,7 @@ int getty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | |||
579 | int getty_main(int argc UNUSED_PARAM, char **argv) | 579 | int getty_main(int argc UNUSED_PARAM, char **argv) |
580 | { | 580 | { |
581 | int n; | 581 | int n; |
582 | pid_t pid; | ||
582 | char *fakehost = NULL; /* Fake hostname for ut_host */ | 583 | char *fakehost = NULL; /* Fake hostname for ut_host */ |
583 | char *logname; /* login name, given to /bin/login */ | 584 | char *logname; /* login name, given to /bin/login */ |
584 | /* Merging these into "struct local" may _seem_ to reduce | 585 | /* Merging these into "struct local" may _seem_ to reduce |
@@ -651,17 +652,18 @@ int getty_main(int argc UNUSED_PARAM, char **argv) | |||
651 | /* tcgetattr() + error check */ | 652 | /* tcgetattr() + error check */ |
652 | ioctl_or_perror_and_die(0, TCGETS, &termios, "%s: TCGETS", options.tty); | 653 | ioctl_or_perror_and_die(0, TCGETS, &termios, "%s: TCGETS", options.tty); |
653 | 654 | ||
655 | pid = getpid(); | ||
654 | #ifdef __linux__ | 656 | #ifdef __linux__ |
655 | // FIXME: do we need this? Otherwise "-" case seems to be broken... | 657 | // FIXME: do we need this? Otherwise "-" case seems to be broken... |
656 | // /* Forcibly make fd 0 our controlling tty, even if another session | 658 | // /* Forcibly make fd 0 our controlling tty, even if another session |
657 | // * has it as a ctty. (Another session loses ctty). */ | 659 | // * has it as a ctty. (Another session loses ctty). */ |
658 | // ioctl(0, TIOCSCTTY, (void*)1); | 660 | // ioctl(0, TIOCSCTTY, (void*)1); |
659 | /* Make ourself a foreground process group within our session */ | 661 | /* Make ourself a foreground process group within our session */ |
660 | tcsetpgrp(0, getpid()); | 662 | tcsetpgrp(0, pid); |
661 | #endif | 663 | #endif |
662 | 664 | ||
663 | /* Update the utmp file. This tty is ours now! */ | 665 | /* Update the utmp file. This tty is ours now! */ |
664 | update_utmp(LOGIN_PROCESS, options.tty, "LOGIN", fakehost); | 666 | update_utmp(pid, LOGIN_PROCESS, options.tty, "LOGIN", fakehost); |
665 | 667 | ||
666 | /* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */ | 668 | /* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */ |
667 | debug("calling termios_init\n"); | 669 | debug("calling termios_init\n"); |
diff --git a/loginutils/login.c b/loginutils/login.c index 4a820379d..bf21d6121 100644 --- a/loginutils/login.c +++ b/loginutils/login.c | |||
@@ -384,7 +384,7 @@ int login_main(int argc UNUSED_PARAM, char **argv) | |||
384 | fchown(0, pw->pw_uid, pw->pw_gid); | 384 | fchown(0, pw->pw_uid, pw->pw_gid); |
385 | fchmod(0, 0600); | 385 | fchmod(0, 0600); |
386 | 386 | ||
387 | update_utmp(USER_PROCESS, short_tty, username, run_by_root ? opt_host : NULL); | 387 | update_utmp(getpid(), USER_PROCESS, short_tty, username, run_by_root ? opt_host : NULL); |
388 | 388 | ||
389 | /* We trust environment only if we run by root */ | 389 | /* We trust environment only if we run by root */ |
390 | if (ENABLE_LOGIN_SCRIPTS && run_by_root) | 390 | if (ENABLE_LOGIN_SCRIPTS && run_by_root) |
diff --git a/miscutils/last.c b/miscutils/last.c index 6e3ed9093..55c03ae41 100644 --- a/miscutils/last.c +++ b/miscutils/last.c | |||
@@ -92,7 +92,8 @@ int last_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
92 | goto next; | 92 | goto next; |
93 | } | 93 | } |
94 | if (ut.ut_type != DEAD_PROCESS | 94 | if (ut.ut_type != DEAD_PROCESS |
95 | && ut.ut_user[0] && ut.ut_line[0] | 95 | && ut.ut_user[0] |
96 | && ut.ut_line[0] | ||
96 | ) { | 97 | ) { |
97 | ut.ut_type = USER_PROCESS; | 98 | ut.ut_type = USER_PROCESS; |
98 | } | 99 | } |
diff --git a/networking/telnetd.c b/networking/telnetd.c index cab94f0f7..b3e66eb4b 100644 --- a/networking/telnetd.c +++ b/networking/telnetd.c | |||
@@ -20,18 +20,22 @@ | |||
20 | * Vladimir Oleynik <dzo@simtreas.ru> 2001 | 20 | * Vladimir Oleynik <dzo@simtreas.ru> 2001 |
21 | * Set process group corrections, initial busybox port | 21 | * Set process group corrections, initial busybox port |
22 | */ | 22 | */ |
23 | |||
24 | #define DEBUG 0 | 23 | #define DEBUG 0 |
25 | 24 | ||
26 | #include "libbb.h" | 25 | #include "libbb.h" |
27 | #include <syslog.h> | 26 | #include <syslog.h> |
28 | 27 | ||
29 | #if DEBUG | 28 | #if DEBUG |
30 | #define TELCMDS | 29 | # define TELCMDS |
31 | #define TELOPTS | 30 | # define TELOPTS |
32 | #endif | 31 | #endif |
33 | #include <arpa/telnet.h> | 32 | #include <arpa/telnet.h> |
34 | 33 | ||
34 | #if ENABLE_FEATURE_UTMP | ||
35 | # include <utmp.h> /* LOGIN_PROCESS */ | ||
36 | #endif | ||
37 | |||
38 | |||
35 | struct tsession { | 39 | struct tsession { |
36 | struct tsession *next; | 40 | struct tsession *next; |
37 | pid_t shell_pid; | 41 | pid_t shell_pid; |
@@ -319,7 +323,11 @@ make_new_session( | |||
319 | xopen(tty_name, O_RDWR); /* becomes our ctty */ | 323 | xopen(tty_name, O_RDWR); /* becomes our ctty */ |
320 | xdup2(0, 1); | 324 | xdup2(0, 1); |
321 | xdup2(0, 2); | 325 | xdup2(0, 2); |
322 | tcsetpgrp(0, getpid()); /* switch this tty's process group to us */ | 326 | pid = getpid(); |
327 | tcsetpgrp(0, pid); /* switch this tty's process group to us */ | ||
328 | |||
329 | //TODO: fetch remote addr via getpeername (see ftpd.c) | ||
330 | write_new_utmp(pid, LOGIN_PROCESS, tty_name, /*username:*/ "LOGIN", /*hostname:*/ NULL); | ||
323 | 331 | ||
324 | /* The pseudo-terminal allocated to the client is configured to operate | 332 | /* The pseudo-terminal allocated to the client is configured to operate |
325 | * in cooked mode, and with XTABS CRMOD enabled (see tty(4)) */ | 333 | * in cooked mode, and with XTABS CRMOD enabled (see tty(4)) */ |
@@ -358,12 +366,13 @@ make_new_session( | |||
358 | static void | 366 | static void |
359 | free_session(struct tsession *ts) | 367 | free_session(struct tsession *ts) |
360 | { | 368 | { |
361 | struct tsession *t = G.sessions; | 369 | struct tsession *t; |
362 | 370 | ||
363 | if (option_mask32 & OPT_INETD) | 371 | if (option_mask32 & OPT_INETD) |
364 | exit(EXIT_SUCCESS); | 372 | exit(EXIT_SUCCESS); |
365 | 373 | ||
366 | /* Unlink this telnet session from the session list */ | 374 | /* Unlink this telnet session from the session list */ |
375 | t = G.sessions; | ||
367 | if (t == ts) | 376 | if (t == ts) |
368 | G.sessions = ts->next; | 377 | G.sessions = ts->next; |
369 | else { | 378 | else { |
@@ -414,6 +423,7 @@ static void handle_sigchld(int sig UNUSED_PARAM) | |||
414 | { | 423 | { |
415 | pid_t pid; | 424 | pid_t pid; |
416 | struct tsession *ts; | 425 | struct tsession *ts; |
426 | int save_errno = errno; | ||
417 | 427 | ||
418 | /* Looping: more than one child may have exited */ | 428 | /* Looping: more than one child may have exited */ |
419 | while (1) { | 429 | while (1) { |
@@ -424,11 +434,22 @@ static void handle_sigchld(int sig UNUSED_PARAM) | |||
424 | while (ts) { | 434 | while (ts) { |
425 | if (ts->shell_pid == pid) { | 435 | if (ts->shell_pid == pid) { |
426 | ts->shell_pid = -1; | 436 | ts->shell_pid = -1; |
437 | // man utmp: | ||
438 | // When init(8) finds that a process has exited, it locates its utmp entry | ||
439 | // by ut_pid, sets ut_type to DEAD_PROCESS, and clears ut_user, ut_host | ||
440 | // and ut_time with null bytes. | ||
441 | // [same applies to other processes which maintain utmp entries, like telnetd] | ||
442 | // | ||
443 | // We do not bother actually clearing fields: | ||
444 | // it might be interesting to know who was logged in and from where | ||
445 | update_utmp(pid, DEAD_PROCESS, /*tty_name:*/ NULL, /*username:*/ NULL, /*hostname:*/ NULL); | ||
427 | break; | 446 | break; |
428 | } | 447 | } |
429 | ts = ts->next; | 448 | ts = ts->next; |
430 | } | 449 | } |
431 | } | 450 | } |
451 | |||
452 | errno = save_errno; | ||
432 | } | 453 | } |
433 | 454 | ||
434 | int telnetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 455 | int telnetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
@@ -689,6 +710,8 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv) | |||
689 | ts = next; | 710 | ts = next; |
690 | continue; | 711 | continue; |
691 | kill_session: | 712 | kill_session: |
713 | if (ts->shell_pid > 0) | ||
714 | update_utmp(ts->shell_pid, DEAD_PROCESS, /*tty_name:*/ NULL, /*username:*/ NULL, /*hostname:*/ NULL); | ||
692 | free_session(ts); | 715 | free_session(ts); |
693 | ts = next; | 716 | ts = next; |
694 | } | 717 | } |