home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume22
/
queuer
/
part03
/
qmaster.c
< prev
Wrap
C/C++ Source or Header
|
1990-06-07
|
19KB
|
864 lines
/* Copyright 1990 The President and Fellows of Harvard University
Permission to use, copy, modify, and distribute this program for any
purpose and without fee is hereby granted, provided that this
copyright and permission notice appear on all copies and supporting
documentation, the name of Harvard University not be used in advertising
or publicity pertaining to distribution of the program, or to results
derived from its use, without specific prior written permission, and notice
be given in supporting documentation that copying and distribution is by
permission of Harvard University. Harvard University makes no
representations about the suitability of this software for any purpose.
It is provided "as is" without express or implied warranty. */
/* qmaster.c - Dan Lanciani '89 */
/*
refresh hosts less
pick up strays
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include <signal.h>
#include <sgtty.h>
#include <setjmp.h>
#include "queue.h"
#define GARBAGE (1*60)
#define HOLDDOWN (2*60)
#define HOSTPOLLTIME (5*60)
#define DEADMAN (1*60)
struct hostinfo {
struct hostinfo *hi_next;
char *hi_name;
struct sockaddr_in hi_addr;
int hi_equiv;
int hi_load;
time_t hi_timestamp;
int hi_dead;
int hi_queued;
} *hostinfo;
struct userinfo {
struct userinfo *ui_next;
long ui_unid;
struct sockaddr_in ui_addr;
char *ui_name;
struct proginfo *ui_prog;
time_t ui_timestamp;
int ui_mark;
};
struct queueinfo {
struct queueinfo *qi_next;
char *qi_name;
int qi_maxrun;
int qi_minload;
int qi_maxperu;
int qi_hcnt;
struct hostinfo *qi_hosts[MAXHCNT];
struct userinfo *qi_heads[MAXHCNT];
struct userinfo *qi_head;
} *queueinfo;
struct proginfo {
struct proginfo *pi_next;
char *pi_name;
struct queueinfo *pi_queue;
} *proginfo;
struct sockaddr_in sin = { AF_INET };
time_t time();
extern int errno;
int reapchild();
long atol();
long
newunid()
{
static int fd = -1;
long unid;
char buf[100];
if(fd < 0) {
if((fd = open(UNID, 2)) < 0) {
if((fd = creat(UNID, 0644)) < 0) {
perror("creat");
exit(1);
}
sprintf(buf, "%ld", FIRSTUNID);
write(fd, buf, strlen(buf) + 1);
close(fd);
if((fd = open(UNID, 2)) < 0) {
perror("open");
exit(1);
}
}
}
lseek(fd, 0L, 0);
read(fd, buf, sizeof(buf));
unid = atol(buf) + 1;
if(!isunid(unid))
unid = FIRSTUNID;
sprintf(buf, "%ld", unid);
lseek(fd, 0L, 0);
write(fd, buf, strlen(buf) + 1);
return(unid);
}
#define refresh(hi) \
if(time((time_t)0) - (hi)->hi_timestamp > HOSTPOLLTIME) { \
(hi)->hi_load = getrload((hi)->hi_name); \
(hi)->hi_timestamp = time((time_t)0); \
} \
else
struct hostinfo *
hibyname(name)
register char *name;
{
register struct hostinfo *hi;
register struct hostent *hp;
for(hi = hostinfo; hi; hi = hi->hi_next)
if(!strcmp(hi->hi_name, name)) {
refresh(hi);
return(hi);
}
if(!(hp = gethostbyname(name)))
return(0);
hi = (struct hostinfo *)malloc(sizeof(struct hostinfo));
hi->hi_name = newstring(name);
if(infile(EQUIVFILE, hi->hi_name))
hi->hi_equiv = 1;
else
hi->hi_equiv = 0;
hi->hi_addr.sin_family = hp->h_addrtype;
bcopy(hp->h_addr, &hi->hi_addr.sin_addr, hp->h_length);
hi->hi_load = getrload(name);
hi->hi_timestamp = time((time_t)0);
hi->hi_dead = 0;
hi->hi_queued = 0;
hi->hi_next = hostinfo;
hostinfo = hi;
return(hi);
}
struct hostinfo *
hibyaddr(addr)
register struct sockaddr_in *addr;
{
register struct hostinfo *hi;
register struct hostent *hp;
for(hi = hostinfo; hi; hi = hi->hi_next)
if(addr->sin_addr.s_addr == hi->hi_addr.sin_addr.s_addr) {
refresh(hi);
return(hi);
}
if(!(hp=gethostbyaddr(&addr->sin_addr, sizeof(struct in_addr),AF_INET)))
return(0);
hi = (struct hostinfo *)malloc(sizeof(struct hostinfo));
hi->hi_name = newstring(hp->h_name);
if(index(hi->hi_name, '.'))
*index(hi->hi_name, '.') = '\0';
if(infile(EQUIVFILE, hi->hi_name))
hi->hi_equiv = 1;
else
hi->hi_equiv = 0;
hi->hi_addr.sin_family = hp->h_addrtype;
bcopy(hp->h_addr, &hi->hi_addr.sin_addr, hp->h_length);
hi->hi_load = getrload(hi->hi_name);
hi->hi_timestamp = time((time_t)0);
hi->hi_dead = 0;
hi->hi_queued = 0;
hi->hi_next = hostinfo;
hostinfo = hi;
return(hi);
}
struct userinfo *
uibyunid(unid, unlnk)
register long unid;
{
register struct queueinfo *qi;
register struct userinfo *ui, *pu;
register int i;
for(qi = queueinfo; qi; qi = qi->qi_next) {
for(i = 0; i < qi->qi_hcnt; i++)
for(pu = 0, ui =qi->qi_heads[i];ui;pu=ui,ui=ui->ui_next)
if(unid == ui->ui_unid) {
if(unlnk)
if(pu)
pu->ui_next = ui->ui_next;
else
qi->qi_heads[i] = ui->ui_next;
return(ui);
}
for(pu = 0, ui = qi->qi_head; ui; pu = ui, ui = ui->ui_next)
if(unid == ui->ui_unid) {
if(unlnk)
if(pu)
pu->ui_next = ui->ui_next;
else
qi->qi_head = ui->ui_next;
return(ui);
}
}
return(0);
}
struct queueinfo *
qibyname(name)
register char *name;
{
register struct queueinfo *qi;
for(qi = queueinfo; qi; qi = qi->qi_next)
if(!strcmp(qi->qi_name, name))
return(qi);
return(0);
}
struct proginfo *
pibyname(name)
register char *name;
{
register struct proginfo *pi;
register struct queueinfo *qi;
register int i;
for(pi = proginfo; pi; pi = pi->pi_next)
if(!strcmp(pi->pi_name, name))
return(pi);
if(readconf(name))
return(0);
if(!(qi = qibyname(queue))) {
qi = (struct queueinfo *)malloc(sizeof(struct queueinfo));
qi->qi_name = newstring(queue);
qi->qi_maxrun = maxrun;
qi->qi_minload = minload;
qi->qi_maxperu = maxperu;
for(i = 0; i < hcnt; i++) {
if(!(qi->qi_hosts[i] = hibyname(hosts[i]))) {
i--;
hcnt--;
continue;
}
qi->qi_hosts[i]->hi_queued = 1;
qi->qi_heads[i] = 0;
}
if(hcnt <= 0) {
free(qi);
return(0);
}
qi->qi_hcnt = hcnt;
qi->qi_head = 0;
qi->qi_next = queueinfo;
queueinfo = qi;
}
pi = (struct proginfo *)malloc(sizeof(struct proginfo));
pi->pi_name = newstring(name);
pi->pi_queue = qi;
pi->pi_next = proginfo;
proginfo = pi;
return(pi);
}
char *
getln(n, buf)
char *buf;
{
register char *p = buf;
do
if(read(n, p, 1) != 1)
return(0);
while(*p++ != '\n');
p[-1] = 0;
return(buf);
}
char *
getstr(n, buf)
char *buf;
{
register char *p = buf;
do
if(read(n, p, 1) != 1)
return(0);
while(*p++);
return(buf);
}
struct timeval timeout = { 60, 0 };
char myname[100];
int s, u, on = 1;
int strays, dropouts, upstarts;
int deadmans, enqueues, dequeues, showqueues;
jmp_buf jb;
catch()
{
deadmans++;
longjmp(jb, 1);
}
main(argc, argv, envp)
char **argv, **envp;
{
register struct servent *sp;
register struct queueinfo *qi;
register struct userinfo *ui, *pu;
register struct hostinfo *hi, *garbagehi = 0;
register int i, j, qport;
long probe, mask, unid;
char buf[BUFSIZ];
time_t garbage = 0;
gethostname(myname, sizeof(myname));
if(argc > 1) {
for(i = 1; i < argc; i++)
if(!strcmp(argv[i], myname))
goto iammaster;
fprintf(stderr, "I am not master %s\n", myname);
exit(0);
}
iammaster:
#ifndef DEBUG
for(s = 0; s < 30; s++)
close(s);
if((s = open("/dev/tty", 2)) >= 0) {
ioctl(s, TIOCNOTTY, 0);
close(s);
}
s = open("/", 0);
dup(s);
dup(s);
signal(SIGALRM, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
if(fork())
exit(0);
#endif
while(fork()) {
wait((int *)0);
sleep(15);
}
if(!(sp = getservbyname("queue", "tcp"))) {
fprintf(stderr, "queue/tcp: Bad service?!?\n");
exit(1);
}
qport = sp->s_port;
if(!(sp = getservbyname("qmaster", "tcp"))) {
fprintf(stderr, "qmaster/tcp: Bad service?!?\n");
exit(1);
}
if((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
exit(1);
}
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on));
sin.sin_port = sp->s_port;
if(bind(s, &sin, sizeof(sin))) {
perror("bind");
exit(1);
}
listen(s, 10);
mask = (1L << s);
if(!(sp = getservbyname("qmaster", "udp"))) {
fprintf(stderr, "qmaster/udp: Bad service?!?\n");
exit(1);
}
if((u = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket");
exit(1);
}
setsockopt(u, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
sin.sin_port = sp->s_port;
if(bind(u, &sin, sizeof(sin))) {
perror("bind");
exit(1);
}
mask |= (1L << u);
while(1) {
struct sockaddr_in from;
int s0, fromlen;
signal(SIGCHLD, reapchild);
probe = mask;
if(select(32, &probe, 0, 0, &timeout) < 0) {
sleep(1);
continue;
}
if(probe & (1L << s)) {
fromlen = sizeof(from);
if((s0 = accept(s, &from, &fromlen)) < 0) {
if(errno = EINTR)
continue;
perror("accept");
sleep(1);
continue;
}
if(from.sin_family != AF_INET ||
htons((u_short)from.sin_port) >= IPPORT_RESERVED
|| htons((u_short)from.sin_port)
< IPPORT_RESERVED / 2) {
close(s0);
continue;
}
dostream(s0, &from);
}
if(probe & (1L << u))
dodgram(u);
for(hi = hostinfo; hi; hi = hi->hi_next)
if(hi->hi_queued)
refresh(hi);
for(qi = queueinfo; qi; qi = qi->qi_next) {
if(!qi->qi_head)
continue;
for(i = 0; i < qi->qi_hcnt; i++)
for(j = i + 1; j < qi->qi_hcnt; j++)
if(qi->qi_hosts[j]->hi_load <
qi->qi_hosts[i]->hi_load) {
ui = qi->qi_heads[i];
hi = qi->qi_hosts[i];
qi->qi_heads[i]=qi->qi_heads[j];
qi->qi_hosts[i]=qi->qi_hosts[j];
qi->qi_heads[j] = ui;
qi->qi_hosts[j] = hi;
}
for(i = 0; i < qi->qi_hcnt && qi->qi_head; i++) {
if(qi->qi_hosts[i]->hi_dead)
continue;
if(qi->qi_minload &&
qi->qi_hosts[i]->hi_load>qi->qi_minload)
continue;
j = 0;
for(ui = qi->qi_heads[i]; ui; ui = ui->ui_next)
j++;
while(j < qi->qi_maxrun && (ui = qi->qi_head)) {
qi->qi_head = ui->ui_next;
ui->ui_next = qi->qi_heads[i];
qi->qi_heads[i] = ui;
sprintf(buf, "\1%s",
qi->qi_hosts[i]->hi_name);
sendto(u, buf, strlen(buf) + 1, 0,
&ui->ui_addr,
sizeof(ui->ui_addr));
ui->ui_timestamp = time((time_t)0);
j++;
}
}
*buf = 0;
for(ui = qi->qi_head; ui; ui = ui->ui_next)
sendto(u, buf, 1, 0, &ui->ui_addr,
sizeof(ui->ui_addr));
}
if(time((time_t)0) - garbage > GARBAGE) {
int rp = IPPORT_RESERVED - 1;
register char *p;
if(!garbagehi)
garbagehi = hostinfo;
if(!(hi = garbagehi) || !hi->hi_queued)
goto out;
hi->hi_addr.sin_port = qport;
if((i = rresvport(&rp)) < 0)
goto out;
if(setjmp(jb))
goto out2;
#ifdef DEBUG
fprintf(stderr, "gc host %s\n", hi->hi_name);
#endif
signal(SIGALRM, catch);
alarm(DEADMAN);
if(connect(i, &hi->hi_addr, sizeof(hi->hi_addr))) {
hi->hi_dead = 1;
goto out2;
}
hi->hi_dead = 0;
alarm(DEADMAN);
write(i, "\1", 2);
alarm(DEADMAN);
while(getln(i, buf)) {
alarm(0);
if(strncmp(buf, "Queue: ", 7))
goto out2;
if(!(p = index(buf + 7, ',')))
goto out2;
*p = 0;
#ifdef DEBUG
fprintf(stderr, "queue = %s\n", buf);
#endif
if(!(qi = qibyname(buf + 7))) {
skip:
alarm(DEADMAN);
while(getln(i, buf) && *buf)
alarm(DEADMAN);
continue;
}
for(j = 0; j < qi->qi_hcnt; j++)
if(hi == qi->qi_hosts[j])
break;
if(j >= qi->qi_hcnt)
goto skip;
for(ui = qi->qi_heads[j]; ui; ui = ui->ui_next)
ui->ui_mark = 0;
alarm(DEADMAN);
if(!getln(i, buf) || strncmp(buf, "Pid", 3))
goto out2;
#ifdef DEBUG
fprintf(stderr, "header = %s\n", buf);
#endif
while(getln(i, buf) && *buf) {
alarm(0);
#ifdef DEBUG
fprintf(stderr, "entry = %s\n", buf);
#endif
unid = atol(buf);
for(ui = qi->qi_heads[j]; ui;
ui = ui->ui_next)
if(ui->ui_unid == unid) {
/* XXX check mark */
ui->ui_mark = 1;
break;
}
if(ui) {
alarm(DEADMAN);
continue;
}
if(ui = uibyunid(unid, 1)) {
ui->ui_mark = 1;
ui->ui_next = qi->qi_heads[j];
qi->qi_heads[j] = ui;
upstarts++;
alarm(DEADMAN);
continue;
}
/* XXX add it */
strays++;
alarm(DEADMAN);
}
alarm(0);
again:
for(pu = 0, ui = qi->qi_heads[j]; ui;
pu = ui, ui = ui->ui_next)
if(!ui->ui_mark && time((time_t)0) -
ui->ui_timestamp > HOLDDOWN) {
if(pu)
pu->ui_next = ui->ui_next;
else
qi->qi_heads[j] = ui->ui_next;
free(ui->ui_name);
free(ui);
dropouts++;
goto again;
}
alarm(DEADMAN);
}
out2:
close(i);
out:
alarm(0);
signal(SIGALRM, SIG_DFL);
garbagehi = hi->hi_next;
garbage = time((time_t)0);
}
}
}
reapchild()
{
union wait status;
while(wait3(&status, WNOHANG, 0) > 0);
}
dostream(s, sin)
register int s;
struct sockaddr_in *sin;
{
register struct hostinfo *hi;
if(!(hi = hibyaddr(sin)))
goto bad;
if(setjmp(jb))
goto bad;
signal(SIGALRM, catch);
alarm(DEADMAN);
if(!strcmp(hi->hi_name, "localhost") || !strcmp(hi->hi_name, myname))
qservice(s, myname, sin, 1);
else if(hi->hi_equiv)
qservice(s, hi->hi_name, sin, 0);
bad:
alarm(0);
signal(SIGALRM, SIG_IGN);
close(s);
}
dodgram(s)
register int s;
{
char buf[BUFSIZ];
struct sockaddr_in sin;
int len, fromlen = sizeof(sin);
register struct userinfo *ui;
if(setjmp(jb))
goto bad;
signal(SIGALRM, catch);
alarm(10);
if((len = recvfrom(s, buf, sizeof(buf), 0, &sin, sizeof(sin))) <= 0)
goto bad;
alarm(0);
switch(*buf&0377) {
case 0:
if(ui = uibyunid(atol(buf + 1), 1)) {
free(ui->ui_name);
free(ui);
}
break;
}
bad:
alarm(0);
signal(SIGALRM, SIG_IGN);
}
qservice(s, host, sin, local)
register int s;
register char *host;
register struct sockaddr_in *sin;
register int local;
{
char request;
if(read(s, &request, 1) != 1)
return;
switch(request&0377) {
case 0:
menqueue(s, host, sin, local);
enqueues++;
break;
case 1:
mshowqueue(s, host, sin, local);
showqueues++;
break;
case 2:
mdequeue(s, host, sin, local);
dequeues++;
break;
}
}
menqueue(s, host, sin, local)
register int s;
register char *host;
register struct sockaddr_in *sin;
register int local;
{
char what[BUFSIZ], prog[BUFSIZ], user[BUFSIZ], buf[BUFSIZ];
register struct proginfo *pi;
register struct queueinfo *qi;
register struct userinfo *ui, *u2;
register int i, j;
if(!getstr(s, what) || !getstr(s, prog) || !getstr(s, user))
return;
alarm(0);
if(!(pi = pibyname(prog)))
return;
qi = pi->pi_queue;
if(qi->qi_maxperu) {
for(i = j = 0; i < qi->qi_hcnt; i++)
for(ui = qi->qi_heads[i]; ui; ui = ui->ui_next)
if(!strcmp(user, ui->ui_name))
j++;
for(ui = qi->qi_head; ui; ui = ui->ui_next)
if(!strcmp(user, ui->ui_name))
j++;
if(j >= qi->qi_maxperu) {
*buf = 1;
sprintf(buf + 1, "You already have %d jobs queued", j);
alarm(DEADMAN);
write(s, buf, strlen(buf + 1) + 2);
return;
}
}
ui = (struct userinfo *)malloc(sizeof(struct userinfo));
*buf = 0;
sprintf(buf + 1, "%ld", ui->ui_unid = newunid());
alarm(DEADMAN);
write(s, buf, strlen(buf + 1) + 2);
alarm(0);
ui->ui_addr = *sin;
ui->ui_addr.sin_port = atoi(what);
ui->ui_name = newstring(user);
ui->ui_prog = pi;
ui->ui_next = 0;
if(u2 = qi->qi_head) {
while(u2->ui_next)
u2 = u2->ui_next;
u2->ui_next = ui;
}
else
qi->qi_head = ui;
}
mshowqueue(s, host, sin, local)
register int s;
register char *host;
register struct sockaddr_in *sin;
register int local;
{
register int i;
register struct queueinfo *qi;
register struct userinfo *ui;
register struct hostinfo *hi;
char buf[BUFSIZ];
alarm(0);
if(fork())
return;
for(i = 0; i < 3; i++)
dup2(s, i);
if(!getstr(s, buf))
exit(1);
if(s > 2)
close(s);
if(!strcmp(buf, "test")) {
printf("Debugging information:\n");
printf("Strays: %d, Dropouts: %d, Upstarts: %d\n",
strays, dropouts, upstarts);
printf("Deadmans: %d\n", deadmans);
printf("Enqueues: %d, Dequeues: %d, Showqueues: %d\n",
enqueues, dequeues, showqueues);
printf("Host\t\tAddr\t\tEquiv\tLoad\tDead\tQueued\n");
for(hi = hostinfo; hi; hi = hi->hi_next)
printf("%-16s%s\t%d\t%d\t%d\t%d\n",
hi->hi_name, inet_ntoa(hi->hi_addr.sin_addr),
hi->hi_equiv, hi->hi_load,
hi->hi_dead, hi->hi_queued);
printf("\n");
}
printf("Queue: %s\n", buf);
printf("Pid\t\tState\tUser\tHost\tCommand\n");
fflush(stdout);
if(!(qi = qibyname(buf)))
exit(1);
for(i = 0; i < qi->qi_hcnt; i++)
for(ui = qi->qi_heads[i]; ui; ui = ui->ui_next) {
printf("%-16ld", ui->ui_unid);
printf("RUN\t");
printf("%s\t", ui->ui_name);
if(hi = hibyaddr(&ui->ui_addr))
printf("%s\t", hi->hi_name);
else
printf("%s\t", inet_ntoa(ui->ui_addr.sin_addr.s_addr));
printf("%s\n", ui->ui_prog->pi_name);
}
for(ui = qi->qi_head; ui; ui = ui->ui_next) {
printf("%-16ld", ui->ui_unid);
printf("WAIT\t");
printf("%s\t", ui->ui_name);
if(hi = hibyaddr(&ui->ui_addr))
printf("%s\t", hi->hi_name);
else
printf("%s\t", inet_ntoa(ui->ui_addr.sin_addr.s_addr));
printf("%s\n", ui->ui_prog->pi_name);
}
fflush(stdout);
exit(0);
}
mdequeue(s, host, sin, local)
register int s;
register char *host;
register struct sockaddr_in *sin;
register int local;
{
char what[BUFSIZ], prog[BUFSIZ], user[BUFSIZ], buf[BUFSIZ];
long unid;
register struct proginfo *pi;
register struct queueinfo *qi;
register struct userinfo *ui, *pu;
register int i;
if(!getstr(s, user) || !getstr(s, prog) || !getstr(s, what))
return;
if(!(pi = pibyname(prog)))
return;
qi = pi->pi_queue;
if(strcmp(what, "all"))
unid = atol(what);
else
unid = -1;
alarm(DEADMAN);
for(i = 0; i < qi->qi_hcnt; i++)
top: for(pu = 0, ui = qi->qi_heads[i]; ui; pu = ui, ui = ui->ui_next)
if(ui->ui_unid == unid ||
(!unid && !strcmp(ui->ui_name, what)) ||
unid == -1) {
if(strcmp(ui->ui_name, user) &&
strcmp(user, "root")) {
/*sprintf(buf, "Not owner (%s, %d)\n",
ui->ui_name, ui->ui_unid);
write(s, buf, strlen(buf));*/
continue;
}
alarm(0);
if(pu)
pu->ui_next = ui->ui_next;
else
qi->qi_heads[i] = ui->ui_next;
free(ui->ui_name);
free(ui);
alarm(DEADMAN);
/*sprintf(buf, "Killed %d of %s for %s\n",
ui->ui_unid, ui->ui_name, user);
write(s, buf, strlen(buf));*/
goto top;
}
alarm(DEADMAN);
top2: for(pu = 0, ui = qi->qi_head; ui; pu = ui, ui = ui->ui_next)
if(ui->ui_unid == unid ||
(!unid && !strcmp(ui->ui_name, what)) || unid == -1) {
if(strcmp(ui->ui_name, user) &&
strcmp(user, "root")) {
sprintf(buf, "Not owner (%s, %d)\n",
ui->ui_name, ui->ui_unid);
write(s, buf, strlen(buf));
continue;
}
alarm(0);
if(pu)
pu->ui_next = ui->ui_next;
else
qi->qi_head = ui->ui_next;
*buf = 2;
sendto(u, buf, 1, 0, &ui->ui_addr, sizeof(ui->ui_addr));
free(ui->ui_name);
free(ui);
alarm(DEADMAN);
sprintf(buf, "Killed %d of %s for %s\n",
ui->ui_unid, ui->ui_name, user);
write(s, buf, strlen(buf));
goto top2;
}
}