diff options
author | delphij <delphij@FreeBSD.org> | 2006-01-18 07:47:46 +0000 |
---|---|---|
committer | delphij <delphij@FreeBSD.org> | 2006-01-18 07:47:46 +0000 |
commit | 55eb0335a6ca658635fcbceceb48c7173143e1cd (patch) | |
tree | 31d4251b33d21cb2fb315ba888e9965790a8a3cf /usr.sbin/inetd | |
parent | f99d64bc8026953fb7714cec81dd7bd7dcf66136 (diff) | |
download | FreeBSD-src-55eb0335a6ca658635fcbceceb48c7173143e1cd.zip FreeBSD-src-55eb0335a6ca658635fcbceceb48c7173143e1cd.tar.gz |
Improves and cleanups over inetd(8):
- Teach inetd(8) about kqueue, originally implemented by jmg@[1].
- Use new C99 style function prototypes instead of K&Rs.
- Raise WARNS from 2 to 6
Glanced at by: ru
MFC After: 2 weeks
[1] http://people.freebsd.org/~jmg/inetd.kq.patch,
http://people.freebsd.org/~jmg/inetd.kq.html
Diffstat (limited to 'usr.sbin/inetd')
-rw-r--r-- | usr.sbin/inetd/Makefile | 2 | ||||
-rw-r--r-- | usr.sbin/inetd/inetd.c | 361 | ||||
-rw-r--r-- | usr.sbin/inetd/inetd.h | 14 |
3 files changed, 150 insertions, 227 deletions
diff --git a/usr.sbin/inetd/Makefile b/usr.sbin/inetd/Makefile index afe4c51..c5d86b7 100644 --- a/usr.sbin/inetd/Makefile +++ b/usr.sbin/inetd/Makefile @@ -6,7 +6,7 @@ MAN= inetd.8 MLINKS= inetd.8 inetd.conf.5 SRCS= inetd.c builtins.c -WARNS?= 2 +WARNS?= 6 CFLAGS+= -DLOGIN_CAP #CFLAGS+= -DSANITY_CHECK diff --git a/usr.sbin/inetd/inetd.c b/usr.sbin/inetd/inetd.c index e5f7365..d434616 100644 --- a/usr.sbin/inetd/inetd.c +++ b/usr.sbin/inetd/inetd.c @@ -113,6 +113,8 @@ __FBSDID("$FreeBSD$"); #include <sys/wait.h> #include <sys/time.h> #include <sys/resource.h> +#include <sys/types.h> +#include <sys/event.h> #include <sys/stat.h> #include <sys/un.h> @@ -197,6 +199,7 @@ __FBSDID("$FreeBSD$"); #ifndef TOOMANY #define TOOMANY 256 /* don't start more than TOOMANY */ #endif + #define CNT_INTVL 60 /* servers in CNT_INTVL sec. */ #define RETRYTIME (60*10) /* retry after bind or server fail */ #define MAX_MAXCHLD 32767 /* max allowable max children */ @@ -204,8 +207,6 @@ __FBSDID("$FreeBSD$"); #define SIGBLOCK (sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM)) void close_sep(struct servtab *); -void flag_signal(int); -void flag_config(int); void config(void); int cpmip(const struct servtab *, int); void endconfig(void); @@ -215,11 +216,8 @@ struct servtab *getconfigent(void); int matchservent(const char *, const char *, const char *); char *nextline(FILE *); void addchild(struct servtab *, int); -void flag_reapchild(int); -void reapchild(void); void enable(struct servtab *); void disable(struct servtab *); -void flag_retry(int); void retry(void); int setconfig(void); void setup(struct servtab *); @@ -230,7 +228,6 @@ void unregisterrpc(register struct servtab *sep); static struct conninfo *search_conn(struct servtab *sep, int ctrl); static int room_conn(struct servtab *sep, struct conninfo *conn); static void addchild_conn(struct conninfo *conn, pid_t pid); -static void reapchild_conn(pid_t pid); static void free_conn(struct conninfo *conn); static void resize_conn(struct servtab *sep, int maxperip); static void free_connlist(struct servtab *sep); @@ -245,7 +242,7 @@ int wrap_bi = 0; int debug = 0; int dolog = 0; int maxsock; /* highest-numbered descriptor */ -fd_set allsock; +int kqsock; int options; int timingout; int toomany = TOOMANY; @@ -261,7 +258,6 @@ int v4bind_ok = 0; struct sockaddr_in6 *bind_sa6; int v6bind_ok = 0; #endif -int signalpipe[2]; #ifdef SANITY_CHECK int nsock; #endif @@ -313,6 +309,7 @@ whichaf(struct request_info *req) int main(int argc, char **argv) { + struct kevent kqevlist[16]; struct servtab *sep; struct passwd *pwd; struct group *grp; @@ -336,7 +333,11 @@ main(int argc, char **argv) #define peer4 p_un.peer_un4 #define peer6 p_un.peer_un6 #define peermax p_un.peer_max - int i; + int i, j; +#ifdef SANITY_CHECK + int k; +#endif + int status; struct addrinfo hints, *res; const char *servname; int error; @@ -519,19 +520,21 @@ main(int argc, char **argv) } #endif + kqsock = kqueue(); + if (kqsock < 0) + err(EX_OSERR, "ERROR: Cannot allocate kqueue"); + sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask, SIGALRM); sigaddset(&sa.sa_mask, SIGCHLD); sigaddset(&sa.sa_mask, SIGHUP); - sa.sa_handler = flag_retry; + sa.sa_handler = SIG_IGN; sigaction(SIGALRM, &sa, &saalrm); + WATCH_SIG(SIGALRM, retry); config(); - sa.sa_handler = flag_config; sigaction(SIGHUP, &sa, &sahup); - sa.sa_handler = flag_reapchild; - sigaction(SIGCHLD, &sa, &sachld); - sa.sa_handler = SIG_IGN; + WATCH_SIG(SIGHUP, config); sigaction(SIGPIPE, &sa, &sapipe); { @@ -544,27 +547,8 @@ main(int argc, char **argv) (void)setenv("inetd_dummy", dummy, 1); } - if (pipe(signalpipe) != 0) { - syslog(LOG_ERR, "pipe: %m"); - exit(EX_OSERR); - } - if (fcntl(signalpipe[0], F_SETFD, FD_CLOEXEC) < 0 || - fcntl(signalpipe[1], F_SETFD, FD_CLOEXEC) < 0) { - syslog(LOG_ERR, "signalpipe: fcntl (F_SETFD, FD_CLOEXEC): %m"); - exit(EX_OSERR); - } - FD_SET(signalpipe[0], &allsock); -#ifdef SANITY_CHECK - nsock++; -#endif - if (signalpipe[0] > maxsock) - maxsock = signalpipe[0]; - if (signalpipe[1] > maxsock) - maxsock = signalpipe[1]; - for (;;) { int n, ctrl; - fd_set readable; #ifdef SANITY_CHECK if (nsock == 0) { @@ -572,46 +556,60 @@ main(int argc, char **argv) exit(EX_SOFTWARE); } #endif - readable = allsock; - if ((n = select(maxsock + 1, &readable, (fd_set *)0, - (fd_set *)0, (struct timeval *)0)) <= 0) { - if (n < 0 && errno != EINTR) { - syslog(LOG_WARNING, "select: %m"); + + if ((n = kevent(kqsock, NULL, 0, kqevlist, + sizeof kqevlist / sizeof *kqevlist, + (struct timespec *)0)) <= 0) { + if (n == -1 && errno != EINTR) { + syslog(LOG_WARNING, "kevent: %m"); sleep(1); } continue; } - /* handle any queued signal flags */ - if (FD_ISSET(signalpipe[0], &readable)) { - int nsig; - if (ioctl(signalpipe[0], FIONREAD, &nsig) != 0) { - syslog(LOG_ERR, "ioctl: %m"); - exit(EX_OSERR); - } - while (--nsig >= 0) { - char c; - if (read(signalpipe[0], &c, 1) != 1) { - syslog(LOG_ERR, "read: %m"); - exit(EX_OSERR); - } + + for (j = 0; j < n; j++) { + if (kqevlist[j].filter == EVFILT_SIGNAL) { + /* handle any queued signal flags */ if (debug) - warnx("handling signal flag %c", c); - switch(c) { - case 'A': /* sigalrm */ - retry(); - break; - case 'C': /* sigchld */ - reapchild(); - break; - case 'H': /* sighup */ - config(); - break; - } - } - } - for (sep = servtab; n && sep; sep = sep->se_next) - if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) { - n--; + warnx("calling signalhandler for sig %td", + kqevlist[j].ident); + ((void (*)(void))kqevlist[j].udata)(); + } else if (kqevlist[j].filter == EVFILT_PROC) { + sep = (struct servtab *)kqevlist[j].udata; + pid = wait4(kqevlist[j].ident, &status, WNOHANG, + (struct rusage *)0); + if (debug) + warnx("%d reaped, status %#x", pid, status); + if (pid == 0) { + /* XXX - this could leave a zombie */ + syslog(LOG_WARNING, "can't reap pid %td", + kqevlist[j].ident); + continue; + } +#ifdef SANITY_CHECK + for (k = 0; k < sep->se_numchild; k++) + if (sep->se_pids[k] == pid) + break; + if (k != sep->se_numchild) + sep->se_pids[k] = + sep->se_pids[sep->se_numchild - 1]; +#endif + if (sep->se_maxchild && + sep->se_numchild == sep->se_maxchild) + enable(sep); + if (status) + syslog(LOG_WARNING, + "%s[%d]: exit status 0x%x", + sep->se_server, pid, status); + /* XXX - this should never happen */ + if (--sep->se_numchild < 0) + sep->se_numchild = 0; + if (sep->se_free && sep->se_numchild == 0) { + freeconfig(sep); + free((char *)sep); + } + } else { + sep = (struct servtab *)kqevlist[j].udata; if (debug) warnx("someone wants %s", sep->se_service); dofork = !sep->se_bi || sep->se_bi->bi_fork || ISWRAP(sep); @@ -882,21 +880,7 @@ main(int argc, char **argv) if (sep->se_accept && sep->se_socktype == SOCK_STREAM) close(ctrl); } - } -} - -/* - * Add a signal flag to the signal flag queue for later handling - */ - -void -flag_signal(int c) -{ - char ch = c; - - if (write(signalpipe[1], &ch, 1) != 1) { - syslog(LOG_ERR, "write: %m"); - _exit(EX_OSERR); + } } } @@ -908,72 +892,18 @@ flag_signal(int c) void addchild(struct servtab *sep, pid_t pid) { - if (sep->se_maxchild <= 0) - return; #ifdef SANITY_CHECK - if (sep->se_numchild >= sep->se_maxchild) { + if (sep->se_maxchild && sep->se_numchild >= sep->se_maxchild) { syslog(LOG_ERR, "%s: %d >= %d", __func__, sep->se_numchild, sep->se_maxchild); exit(EX_SOFTWARE); } + sep->se_pids[sep->se_numchild] = pid; #endif - sep->se_pids[sep->se_numchild++] = pid; - if (sep->se_numchild == sep->se_maxchild) + sep->se_numchild++; + if (sep->se_maxchild && sep->se_numchild == sep->se_maxchild) disable(sep); -} - -/* - * Some child process has exited. See if it's on somebody's list. - */ - -void -flag_reapchild(int signo __unused) -{ - flag_signal('C'); -} - -void -reapchild(void) -{ - int k, status; - pid_t pid; - struct servtab *sep; - - for (;;) { - pid = wait3(&status, WNOHANG, (struct rusage *)0); - if (pid <= 0) - break; - if (debug) - warnx("%d reaped, %s %u", pid, - WIFEXITED(status) ? "status" : "signal", - WIFEXITED(status) ? WEXITSTATUS(status) - : WTERMSIG(status)); - for (sep = servtab; sep; sep = sep->se_next) { - for (k = 0; k < sep->se_numchild; k++) - if (sep->se_pids[k] == pid) - break; - if (k == sep->se_numchild) - continue; - if (sep->se_numchild == sep->se_maxchild) - enable(sep); - sep->se_pids[k] = sep->se_pids[--sep->se_numchild]; - if (WIFSIGNALED(status) || WEXITSTATUS(status)) - syslog(LOG_WARNING, - "%s[%d]: exited, %s %u", - sep->se_server, pid, - WIFEXITED(status) ? "status" : "signal", - WIFEXITED(status) ? WEXITSTATUS(status) - : WTERMSIG(status)); - break; - } - reapchild_conn(pid); - } -} - -void -flag_config(int signo __unused) -{ - flag_signal('H'); + WATCH_PROC(pid, sep); } void @@ -990,8 +920,10 @@ config(void) syslog(LOG_ERR, "%s: %m", CONFIG); return; } - for (sep = servtab; sep; sep = sep->se_next) + + for (sep = servtab; sep != NULL; sep = sep->se_next) sep->se_checked = 0; + while ((new = getconfigent())) { if (getpwnam(new->se_user) == NULL) { syslog(LOG_ERR, @@ -1037,12 +969,17 @@ config(void) /* copy over outstanding child pids */ if (sep->se_maxchild > 0 && new->se_maxchild > 0) { new->se_numchild = sep->se_numchild; + /* XXX - this can cause problems */ if (new->se_numchild > new->se_maxchild) new->se_numchild = new->se_maxchild; +#ifdef SANITY_CHECK memcpy(new->se_pids, sep->se_pids, new->se_numchild * sizeof(*new->se_pids)); +#endif } +#ifdef SANITY_CHECK SWAP(pid_t *, sep->se_pids, new->se_pids); +#endif sep->se_maxchild = new->se_maxchild; sep->se_numchild = new->se_numchild; sep->se_maxcpm = new->se_maxcpm; @@ -1051,14 +988,11 @@ config(void) sep->se_bi = new->se_bi; /* might need to turn on or off service now */ if (sep->se_fd >= 0) { - if (sep->se_maxchild > 0 - && sep->se_numchild == sep->se_maxchild) { - if (FD_ISSET(sep->se_fd, &allsock)) - disable(sep); - } else { - if (!FD_ISSET(sep->se_fd, &allsock)) - enable(sep); - } + if (sep->se_maxchild + && sep->se_numchild == sep->se_maxchild) + disable(sep); + else + enable(sep); } sep->se_accept = new->se_accept; SWAP(char *, sep->se_user, new->se_user); @@ -1178,8 +1112,11 @@ config(void) print_service("FREE", sep); if (sep->se_rpc && sep->se_rpc_prog > 0) unregisterrpc(sep); - freeconfig(sep); - free(sep); + if (sep->se_numchild == 0) { + freeconfig(sep); + free((char *)sep); + } else + sep->se_free = 1; } (void) sigsetmask(omask); } @@ -1240,12 +1177,6 @@ unregisterrpc(struct servtab *sep) } void -flag_retry(int signo __unused) -{ - flag_signal('A'); -} - -void retry(void) { struct servtab *sep; @@ -1279,12 +1210,12 @@ setup(struct servtab *sep) #define turnon(fd, opt) \ setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on)) if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) && - turnon(sep->se_fd, SO_DEBUG) < 0) + turnon(sep->se_fd, SO_DEBUG) == -1) syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m"); - if (turnon(sep->se_fd, SO_REUSEADDR) < 0) + if (turnon(sep->se_fd, SO_REUSEADDR) == -1) syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m"); #ifdef SO_PRIVSTATE - if (turnon(sep->se_fd, SO_PRIVSTATE) < 0) + if (turnon(sep->se_fd, SO_PRIVSTATE) == -1) syslog(LOG_ERR, "setsockopt (SO_PRIVSTATE): %m"); #endif /* tftpd opens a new connection then needs more infos */ @@ -1292,7 +1223,7 @@ setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on)) (strcmp(sep->se_proto, "udp") == 0) && (sep->se_accept == 0) && (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, - (char *)&on, sizeof (on)) < 0)) + (char *)&on, sizeof (on)) == -1)) syslog(LOG_ERR, "setsockopt (IPV6_RECVPKTINFO): %m"); if (sep->se_family == AF_INET6) { int flag = sep->se_nomapped ? 1 : 0; @@ -1397,8 +1328,7 @@ setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on)) #ifdef IPSEC void -ipsecsetup(sep) - struct servtab *sep; +ipsecsetup(struct servtab *sep) { char *buf; char *policy_in = NULL; @@ -1475,8 +1405,7 @@ void close_sep(struct servtab *sep) { if (sep->se_fd >= 0) { - if (FD_ISSET(sep->se_fd, &allsock)) - disable(sep); + disable(sep); (void) close(sep->se_fd); sep->se_fd = -1; } @@ -1515,7 +1444,7 @@ enter(struct servtab *cp) long omask; sep = (struct servtab *)malloc(sizeof (*sep)); - if (sep == (struct servtab *)0) { + if (sep == NULL) { syslog(LOG_ERR, "malloc: %m"); exit(EX_OSERR); } @@ -1545,14 +1474,9 @@ enable(struct servtab *sep) "%s: %s: is mux", __func__, sep->se_service); exit(EX_SOFTWARE); } - if (FD_ISSET(sep->se_fd, &allsock)) { - syslog(LOG_ERR, - "%s: %s: not off", __func__, sep->se_service); - exit(EX_SOFTWARE); - } nsock++; #endif - FD_SET(sep->se_fd, &allsock); + WATCH_SOCK(sep->se_fd, sep); if (sep->se_fd > maxsock) maxsock = sep->se_fd; } @@ -1574,18 +1498,13 @@ disable(struct servtab *sep) "%s: %s: is mux", __func__, sep->se_service); exit(EX_SOFTWARE); } - if (!FD_ISSET(sep->se_fd, &allsock)) { - syslog(LOG_ERR, - "%s: %s: not on", __func__, sep->se_service); - exit(EX_SOFTWARE); - } if (nsock == 0) { syslog(LOG_ERR, "%s: nsock=0", __func__); exit(EX_SOFTWARE); } nsock--; #endif - FD_CLR(sep->se_fd, &allsock); + UNWATCH_SOCK(sep->se_fd, sep); if (sep->se_fd == maxsock) maxsock--; } @@ -1649,12 +1568,10 @@ more: for (p = cp + 2; p && *p && isspace(*p); p++) ; if (*p == '\0') { - if (policy) - free(policy); + free(policy); policy = NULL; } else if (ipsec_get_policylen(p) >= 0) { - if (policy) - free(policy); + free(policy); policy = newstr(p); } else { syslog(LOG_ERR, @@ -1970,6 +1887,7 @@ more: else sep->se_maxchild = 1; } +#ifdef SANITY_CHECK if (sep->se_maxchild > 0) { sep->se_pids = malloc(sep->se_maxchild * sizeof(*sep->se_pids)); if (sep->se_pids == NULL) { @@ -1977,6 +1895,7 @@ more: exit(EX_OSERR); } } +#endif argc = 0; for (arg = skip(&cp); cp; arg = skip(&cp)) if (argc < MAXARGV) { @@ -2002,29 +1921,22 @@ freeconfig(struct servtab *cp) { int i; - if (cp->se_service) - free(cp->se_service); - if (cp->se_proto) - free(cp->se_proto); - if (cp->se_user) - free(cp->se_user); - if (cp->se_group) - free(cp->se_group); + free(cp->se_service); + free(cp->se_proto); + free(cp->se_user); + free(cp->se_group); #ifdef LOGIN_CAP - if (cp->se_class) - free(cp->se_class); + free(cp->se_class); +#endif + free(cp->se_server); +#ifdef SANITY_CHECK + free(cp->se_pids); #endif - if (cp->se_server) - free(cp->se_server); - if (cp->se_pids) - free(cp->se_pids); for (i = 0; i < MAXARGV; i++) - if (cp->se_argv[i]) - free(cp->se_argv[i]); + free(cp->se_argv[i]); free_connlist(cp); #ifdef IPSEC - if (cp->se_policy) - free(cp->se_policy); + free(cp->se_policy); #endif } @@ -2303,8 +2215,7 @@ cpmip(const struct servtab *sep, int ctrl) strcmp(sep->se_service, chBest->ch_Service) != 0) { chBest->ch_Family = sin4->sin_family; chBest->ch_Addr4 = sin4->sin_addr; - if (chBest->ch_Service) - free(chBest->ch_Service); + free(chBest->ch_Service); chBest->ch_Service = strdup(sep->se_service); bzero(chBest->ch_Times, sizeof(chBest->ch_Times)); } @@ -2317,8 +2228,7 @@ cpmip(const struct servtab *sep, int ctrl) strcmp(sep->se_service, chBest->ch_Service) != 0) { chBest->ch_Family = sin6->sin6_family; chBest->ch_Addr6 = sin6->sin6_addr; - if (chBest->ch_Service) - free(chBest->ch_Service); + free(chBest->ch_Service); chBest->ch_Service = strdup(sep->se_service); bzero(chBest->ch_Times, sizeof(chBest->ch_Times)); } @@ -2356,6 +2266,25 @@ cpmip(const struct servtab *sep, int ctrl) return(r); } +void +watch(short filter, uintptr_t ident, void *data, u_int fflags, int addrm) +{ + struct kevent kev; + int i; + + EV_SET(&kev, ident, filter, addrm? EV_ADD:EV_DELETE, + fflags, 0, data); + + i = kevent(kqsock, &kev, 1, NULL, 0, NULL); + + if (i == -1) + syslog(LOG_ERR, "kevent failed: %m"); + + if (debug) { + warnx("kqueue, ident: %td, addrm: %d, ret: %d, data: %p, errno: %s", ident, addrm, i, data, strerror(errno)); + } +} + static struct conninfo * search_conn(struct servtab *sep, int ctrl) { @@ -2467,26 +2396,6 @@ addchild_conn(struct conninfo *conn, pid_t pid) } static void -reapchild_conn(pid_t pid) -{ - struct procinfo *proc; - struct conninfo *conn; - int i; - - if ((proc = search_proc(pid, 0)) == NULL) - return; - if ((conn = proc->pr_conn) == NULL) - return; - for (i = 0; i < conn->co_numchild; ++i) - if (conn->co_proc[i] == proc) { - conn->co_proc[i] = conn->co_proc[--conn->co_numchild]; - break; - } - free_proc(proc); - free_conn(conn); -} - -static void resize_conn(struct servtab *sep, int maxpip) { struct conninfo *conn; diff --git a/usr.sbin/inetd/inetd.h b/usr.sbin/inetd/inetd.h index 8c05ed9..e84547c 100644 --- a/usr.sbin/inetd/inetd.h +++ b/usr.sbin/inetd/inetd.h @@ -74,7 +74,10 @@ struct servtab { int se_maxchild; /* max number of children */ int se_maxcpm; /* max connects per IP per minute */ int se_numchild; /* current number of children */ + int se_free; /* free when numchild == 0 */ +#ifdef SANITY_CHECK pid_t *se_pids; /* array of child pids */ +#endif char *se_user; /* user name to run as */ char *se_group; /* group name to run as */ #ifdef LOGIN_CAP @@ -145,3 +148,14 @@ struct biltin { int bi_maxchild; /* max number of children, -1=default */ bi_fn_t *bi_fn; /* function which performs it */ }; + +void watch(short, uintptr_t, void *, u_int, int); +#define WATCH_SOCK(fd, data) watch(EVFILT_READ, fd, data, 0, 1) +#define UNWATCH_SOCK(fd, data) watch(EVFILT_READ, fd, data, 0, 0) +#define WATCH_SIG(sig, data) watch(EVFILT_SIGNAL, sig, data, 0, 1) +#define UNWATCH_SIG(sig, data) watch(EVFILT_SIGNAL, sig, data, 0, 0) +#define WATCH_PROC(proc, data) watch(EVFILT_PROC, proc, data, NOTE_EXIT, 1) +#define UNWATCH_PROC(proc, data) watch(EVFILT_PROC, proc, data, NOTE_EXIT, 0) +#define WATCH_FD(fd, data) watch(EVFILT_VNODE, fd, data, NOTE_DELETE|NOTE_WRITE|NOTE_EXTEND|NOTE_RENAME, 1) +#define UNWATCH_FD(fd, data) watch(EVFILT_VNODE, fd, data, NOTE_DELETE|NOTE_WRITE|NOTE_EXTEND|NOTE_RENAME, 0) + |