1 /* $NetBSD: sp_common.c,v 1.38 2014/01/08 01:45:29 pooka Exp $ */ 2 3 /* 4 * Copyright (c) 2010, 2011 Antti Kantee. All Rights Reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 /* 29 * Common client/server sysproxy routines. #included. 30 */ 31 32 #include "rumpuser_port.h" 33 34 #include <sys/types.h> 35 #include <sys/mman.h> 36 #include <sys/queue.h> 37 #include <sys/socket.h> 38 #include <sys/un.h> 39 #include <sys/uio.h> 40 41 #include <arpa/inet.h> 42 #include <netinet/in.h> 43 #include <netinet/tcp.h> 44 45 #include <assert.h> 46 #include <errno.h> 47 #include <fcntl.h> 48 #include <inttypes.h> 49 #include <limits.h> 50 #include <poll.h> 51 #include <pthread.h> 52 #include <stdarg.h> 53 #include <stddef.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <unistd.h> 58 59 /* 60 * XXX: NetBSD's __unused collides with Linux headers, so we cannot 61 * define it before we've included everything. 62 */ 63 #if !defined(__unused) && defined(__GNUC__) 64 #define __unused __attribute__((__unused__)) 65 #endif 66 67 //#define DEBUG 68 #ifdef DEBUG 69 #define DPRINTF(x) mydprintf x 70 static void 71 mydprintf(const char *fmt, ...) 72 { 73 va_list ap; 74 75 va_start(ap, fmt); 76 vfprintf(stderr, fmt, ap); 77 va_end(ap); 78 } 79 #else 80 #define DPRINTF(x) 81 #endif 82 83 #ifndef HOSTOPS 84 #define host_poll poll 85 #define host_read read 86 #define host_sendmsg sendmsg 87 #define host_setsockopt setsockopt 88 #endif 89 90 #define IOVPUT(_io_, _b_) _io_.iov_base = \ 91 (void *)&_b_; _io_.iov_len = sizeof(_b_); 92 #define IOVPUT_WITHSIZE(_io_, _b_, _l_) _io_.iov_base = \ 93 (void *)(_b_); _io_.iov_len = _l_; 94 #define SENDIOV(_spc_, _iov_) dosend(_spc_, _iov_, __arraycount(_iov_)) 95 96 /* 97 * Bah, I hate writing on-off-wire conversions in C 98 */ 99 100 enum { RUMPSP_REQ, RUMPSP_RESP, RUMPSP_ERROR }; 101 enum { RUMPSP_HANDSHAKE, 102 RUMPSP_SYSCALL, 103 RUMPSP_COPYIN, RUMPSP_COPYINSTR, 104 RUMPSP_COPYOUT, RUMPSP_COPYOUTSTR, 105 RUMPSP_ANONMMAP, 106 RUMPSP_PREFORK, 107 RUMPSP_RAISE }; 108 109 enum { HANDSHAKE_GUEST, HANDSHAKE_AUTH, HANDSHAKE_FORK, HANDSHAKE_EXEC }; 110 111 /* 112 * error types used for RUMPSP_ERROR 113 */ 114 enum rumpsp_err { RUMPSP_ERR_NONE = 0, RUMPSP_ERR_TRYAGAIN, RUMPSP_ERR_AUTH, 115 RUMPSP_ERR_INVALID_PREFORK, RUMPSP_ERR_RFORK_FAILED, 116 RUMPSP_ERR_INEXEC, RUMPSP_ERR_NOMEM, RUMPSP_ERR_MALFORMED_REQUEST }; 117 118 /* 119 * The mapping of the above types to errno. They are almost never exposed 120 * to the client after handshake (except for a server resource shortage 121 * and the client trying to be funny). This is a function instead of 122 * an array to catch missing values. Theoretically, the compiled code 123 * should be the same. 124 */ 125 static int 126 errmap(enum rumpsp_err error) 127 { 128 129 switch (error) { 130 /* XXX: no EAUTH on Linux */ 131 case RUMPSP_ERR_NONE: return 0; 132 case RUMPSP_ERR_AUTH: return EPERM; 133 case RUMPSP_ERR_TRYAGAIN: return EAGAIN; 134 case RUMPSP_ERR_INVALID_PREFORK: return ESRCH; 135 case RUMPSP_ERR_RFORK_FAILED: return EIO; /* got a light? */ 136 case RUMPSP_ERR_INEXEC: return EBUSY; 137 case RUMPSP_ERR_NOMEM: return ENOMEM; 138 case RUMPSP_ERR_MALFORMED_REQUEST: return EINVAL; 139 } 140 141 return -1; 142 } 143 144 #define AUTHLEN 4 /* 128bit fork auth */ 145 146 struct rsp_hdr { 147 uint64_t rsp_len; 148 uint64_t rsp_reqno; 149 uint16_t rsp_class; 150 uint16_t rsp_type; 151 /* 152 * We want this structure 64bit-aligned for typecast fun, 153 * so might as well use the following for something. 154 */ 155 union { 156 uint32_t sysnum; 157 uint32_t error; 158 uint32_t handshake; 159 uint32_t signo; 160 } u; 161 }; 162 #define HDRSZ sizeof(struct rsp_hdr) 163 #define rsp_sysnum u.sysnum 164 #define rsp_error u.error 165 #define rsp_handshake u.handshake 166 #define rsp_signo u.signo 167 168 #define MAXBANNER 96 169 170 /* 171 * Data follows the header. We have two types of structured data. 172 */ 173 174 /* copyin/copyout */ 175 struct rsp_copydata { 176 size_t rcp_len; 177 void *rcp_addr; 178 uint8_t rcp_data[0]; 179 }; 180 181 /* syscall response */ 182 struct rsp_sysresp { 183 int rsys_error; 184 register_t rsys_retval[2]; 185 }; 186 187 struct handshake_fork { 188 uint32_t rf_auth[4]; 189 int rf_cancel; 190 }; 191 192 struct respwait { 193 uint64_t rw_reqno; 194 void *rw_data; 195 size_t rw_dlen; 196 int rw_done; 197 int rw_error; 198 199 pthread_cond_t rw_cv; 200 201 TAILQ_ENTRY(respwait) rw_entries; 202 }; 203 204 struct prefork; 205 struct spclient { 206 int spc_fd; 207 int spc_refcnt; 208 int spc_state; 209 210 pthread_mutex_t spc_mtx; 211 pthread_cond_t spc_cv; 212 213 struct lwp *spc_mainlwp; 214 pid_t spc_pid; 215 216 TAILQ_HEAD(, respwait) spc_respwait; 217 218 /* rest of the fields are zeroed upon disconnect */ 219 #define SPC_ZEROFF offsetof(struct spclient, spc_pfd) 220 struct pollfd *spc_pfd; 221 222 struct rsp_hdr spc_hdr; 223 uint8_t *spc_buf; 224 size_t spc_off; 225 226 uint64_t spc_nextreq; 227 uint64_t spc_syscallreq; 228 uint64_t spc_generation; 229 int spc_ostatus, spc_istatus; 230 int spc_reconnecting; 231 int spc_inexec; 232 233 LIST_HEAD(, prefork) spc_pflist; 234 }; 235 #define SPCSTATUS_FREE 0 236 #define SPCSTATUS_BUSY 1 237 #define SPCSTATUS_WANTED 2 238 239 #define SPCSTATE_NEW 0 240 #define SPCSTATE_RUNNING 1 241 #define SPCSTATE_DYING 2 242 243 typedef int (*addrparse_fn)(const char *, struct sockaddr **, int); 244 typedef int (*connecthook_fn)(int); 245 typedef void (*cleanup_fn)(struct sockaddr *); 246 247 static int readframe(struct spclient *); 248 static void handlereq(struct spclient *); 249 250 static __inline void 251 spcresetbuf(struct spclient *spc) 252 { 253 254 spc->spc_buf = NULL; 255 spc->spc_off = 0; 256 } 257 258 static __inline void 259 spcfreebuf(struct spclient *spc) 260 { 261 262 free(spc->spc_buf); 263 spcresetbuf(spc); 264 } 265 266 static void 267 sendlockl(struct spclient *spc) 268 { 269 270 while (spc->spc_ostatus != SPCSTATUS_FREE) { 271 spc->spc_ostatus = SPCSTATUS_WANTED; 272 pthread_cond_wait(&spc->spc_cv, &spc->spc_mtx); 273 } 274 spc->spc_ostatus = SPCSTATUS_BUSY; 275 } 276 277 static void __unused 278 sendlock(struct spclient *spc) 279 { 280 281 pthread_mutex_lock(&spc->spc_mtx); 282 sendlockl(spc); 283 pthread_mutex_unlock(&spc->spc_mtx); 284 } 285 286 static void 287 sendunlockl(struct spclient *spc) 288 { 289 290 if (spc->spc_ostatus == SPCSTATUS_WANTED) 291 pthread_cond_broadcast(&spc->spc_cv); 292 spc->spc_ostatus = SPCSTATUS_FREE; 293 } 294 295 static void 296 sendunlock(struct spclient *spc) 297 { 298 299 pthread_mutex_lock(&spc->spc_mtx); 300 sendunlockl(spc); 301 pthread_mutex_unlock(&spc->spc_mtx); 302 } 303 304 static int 305 dosend(struct spclient *spc, struct iovec *iov, size_t iovlen) 306 { 307 struct msghdr msg; 308 struct pollfd pfd; 309 ssize_t n = 0; 310 int fd = spc->spc_fd; 311 312 pfd.fd = fd; 313 pfd.events = POLLOUT; 314 315 memset(&msg, 0, sizeof(msg)); 316 317 for (;;) { 318 /* not first round? poll */ 319 if (n) { 320 if (host_poll(&pfd, 1, INFTIM) == -1) { 321 if (errno == EINTR) 322 continue; 323 return errno; 324 } 325 } 326 327 msg.msg_iov = iov; 328 msg.msg_iovlen = iovlen; 329 n = host_sendmsg(fd, &msg, MSG_NOSIGNAL); 330 if (n == -1) { 331 if (errno == EPIPE) 332 return ENOTCONN; 333 if (errno != EAGAIN) 334 return errno; 335 continue; 336 } 337 if (n == 0) { 338 return ENOTCONN; 339 } 340 341 /* ok, need to adjust iovec for potential next round */ 342 while (n >= (ssize_t)iov[0].iov_len && iovlen) { 343 n -= iov[0].iov_len; 344 iov++; 345 iovlen--; 346 } 347 348 if (iovlen == 0) { 349 _DIAGASSERT(n == 0); 350 break; 351 } else { 352 iov[0].iov_base = 353 (void *)((uint8_t *)iov[0].iov_base + n); 354 iov[0].iov_len -= n; 355 } 356 } 357 358 return 0; 359 } 360 361 static void 362 doputwait(struct spclient *spc, struct respwait *rw, struct rsp_hdr *rhdr) 363 { 364 365 rw->rw_data = NULL; 366 rw->rw_dlen = rw->rw_done = rw->rw_error = 0; 367 pthread_cond_init(&rw->rw_cv, NULL); 368 369 pthread_mutex_lock(&spc->spc_mtx); 370 rw->rw_reqno = rhdr->rsp_reqno = spc->spc_nextreq++; 371 TAILQ_INSERT_TAIL(&spc->spc_respwait, rw, rw_entries); 372 } 373 374 static void __unused 375 putwait_locked(struct spclient *spc, struct respwait *rw, struct rsp_hdr *rhdr) 376 { 377 378 doputwait(spc, rw, rhdr); 379 pthread_mutex_unlock(&spc->spc_mtx); 380 } 381 382 static void 383 putwait(struct spclient *spc, struct respwait *rw, struct rsp_hdr *rhdr) 384 { 385 386 doputwait(spc, rw, rhdr); 387 sendlockl(spc); 388 pthread_mutex_unlock(&spc->spc_mtx); 389 } 390 391 static void 392 dounputwait(struct spclient *spc, struct respwait *rw) 393 { 394 395 TAILQ_REMOVE(&spc->spc_respwait, rw, rw_entries); 396 pthread_mutex_unlock(&spc->spc_mtx); 397 pthread_cond_destroy(&rw->rw_cv); 398 399 } 400 401 static void __unused 402 unputwait_locked(struct spclient *spc, struct respwait *rw) 403 { 404 405 pthread_mutex_lock(&spc->spc_mtx); 406 dounputwait(spc, rw); 407 } 408 409 static void 410 unputwait(struct spclient *spc, struct respwait *rw) 411 { 412 413 pthread_mutex_lock(&spc->spc_mtx); 414 sendunlockl(spc); 415 416 dounputwait(spc, rw); 417 } 418 419 static void 420 kickwaiter(struct spclient *spc) 421 { 422 struct respwait *rw; 423 int error = 0; 424 425 pthread_mutex_lock(&spc->spc_mtx); 426 TAILQ_FOREACH(rw, &spc->spc_respwait, rw_entries) { 427 if (rw->rw_reqno == spc->spc_hdr.rsp_reqno) 428 break; 429 } 430 if (rw == NULL) { 431 DPRINTF(("no waiter found, invalid reqno %" PRIu64 "?\n", 432 spc->spc_hdr.rsp_reqno)); 433 pthread_mutex_unlock(&spc->spc_mtx); 434 spcfreebuf(spc); 435 return; 436 } 437 DPRINTF(("rump_sp: client %p woke up waiter at %p\n", spc, rw)); 438 rw->rw_data = spc->spc_buf; 439 rw->rw_done = 1; 440 rw->rw_dlen = (size_t)(spc->spc_off - HDRSZ); 441 if (spc->spc_hdr.rsp_class == RUMPSP_ERROR) { 442 error = rw->rw_error = errmap(spc->spc_hdr.rsp_error); 443 } 444 pthread_cond_signal(&rw->rw_cv); 445 pthread_mutex_unlock(&spc->spc_mtx); 446 447 if (error) 448 spcfreebuf(spc); 449 else 450 spcresetbuf(spc); 451 } 452 453 static void 454 kickall(struct spclient *spc) 455 { 456 struct respwait *rw; 457 458 /* DIAGASSERT(mutex_owned(spc_lock)) */ 459 TAILQ_FOREACH(rw, &spc->spc_respwait, rw_entries) 460 pthread_cond_broadcast(&rw->rw_cv); 461 } 462 463 static int 464 readframe(struct spclient *spc) 465 { 466 int fd = spc->spc_fd; 467 size_t left; 468 size_t framelen; 469 ssize_t n; 470 471 /* still reading header? */ 472 if (spc->spc_off < HDRSZ) { 473 DPRINTF(("rump_sp: readframe getting header at offset %zu\n", 474 spc->spc_off)); 475 476 left = HDRSZ - spc->spc_off; 477 /*LINTED: cast ok */ 478 n = host_read(fd, (uint8_t*)&spc->spc_hdr + spc->spc_off, left); 479 if (n == 0) { 480 return -1; 481 } 482 if (n == -1) { 483 if (errno == EAGAIN) 484 return 0; 485 return -1; 486 } 487 488 spc->spc_off += n; 489 if (spc->spc_off < HDRSZ) { 490 return 0; 491 } 492 493 /*LINTED*/ 494 framelen = spc->spc_hdr.rsp_len; 495 496 if (framelen < HDRSZ) { 497 return -1; 498 } else if (framelen == HDRSZ) { 499 return 1; 500 } 501 502 spc->spc_buf = malloc(framelen - HDRSZ); 503 if (spc->spc_buf == NULL) { 504 return -1; 505 } 506 memset(spc->spc_buf, 0, framelen - HDRSZ); 507 508 /* "fallthrough" */ 509 } else { 510 /*LINTED*/ 511 framelen = spc->spc_hdr.rsp_len; 512 } 513 514 left = framelen - spc->spc_off; 515 516 DPRINTF(("rump_sp: readframe getting body at offset %zu, left %zu\n", 517 spc->spc_off, left)); 518 519 if (left == 0) 520 return 1; 521 n = host_read(fd, spc->spc_buf + (spc->spc_off - HDRSZ), left); 522 if (n == 0) { 523 return -1; 524 } 525 if (n == -1) { 526 if (errno == EAGAIN) 527 return 0; 528 return -1; 529 } 530 spc->spc_off += n; 531 left -= n; 532 533 /* got everything? */ 534 if (left == 0) 535 return 1; 536 else 537 return 0; 538 } 539 540 static int 541 tcp_parse(const char *addr, struct sockaddr **sa, int allow_wildcard) 542 { 543 struct sockaddr_in sin; 544 char buf[64]; 545 const char *p; 546 size_t l; 547 int port; 548 549 memset(&sin, 0, sizeof(sin)); 550 SIN_SETLEN(sin, sizeof(sin)); 551 sin.sin_family = AF_INET; 552 553 p = strchr(addr, ':'); 554 if (!p) { 555 fprintf(stderr, "rump_sp_tcp: missing port specifier\n"); 556 return EINVAL; 557 } 558 559 l = p - addr; 560 if (l > sizeof(buf)-1) { 561 fprintf(stderr, "rump_sp_tcp: address too long\n"); 562 return EINVAL; 563 } 564 strncpy(buf, addr, l); 565 buf[l] = '\0'; 566 567 /* special INADDR_ANY treatment */ 568 if (strcmp(buf, "*") == 0 || strcmp(buf, "0") == 0) { 569 sin.sin_addr.s_addr = INADDR_ANY; 570 } else { 571 switch (inet_pton(AF_INET, buf, &sin.sin_addr)) { 572 case 1: 573 break; 574 case 0: 575 fprintf(stderr, "rump_sp_tcp: cannot parse %s\n", buf); 576 return EINVAL; 577 case -1: 578 fprintf(stderr, "rump_sp_tcp: inet_pton failed\n"); 579 return errno; 580 default: 581 assert(/*CONSTCOND*/0); 582 return EINVAL; 583 } 584 } 585 586 if (!allow_wildcard && sin.sin_addr.s_addr == INADDR_ANY) { 587 fprintf(stderr, "rump_sp_tcp: client needs !INADDR_ANY\n"); 588 return EINVAL; 589 } 590 591 /* advance to port number & parse */ 592 p++; 593 l = strspn(p, "0123456789"); 594 if (l == 0) { 595 fprintf(stderr, "rump_sp_tcp: port now found: %s\n", p); 596 return EINVAL; 597 } 598 strncpy(buf, p, l); 599 buf[l] = '\0'; 600 601 if (*(p+l) != '/' && *(p+l) != '\0') { 602 fprintf(stderr, "rump_sp_tcp: junk at end of port: %s\n", addr); 603 return EINVAL; 604 } 605 606 port = atoi(buf); 607 if (port < 0 || port >= (1<<(8*sizeof(in_port_t)))) { 608 fprintf(stderr, "rump_sp_tcp: port %d out of range\n", port); 609 return ERANGE; 610 } 611 sin.sin_port = htons(port); 612 613 *sa = malloc(sizeof(sin)); 614 if (*sa == NULL) 615 return errno; 616 memcpy(*sa, &sin, sizeof(sin)); 617 return 0; 618 } 619 620 static int 621 tcp_connecthook(int s) 622 { 623 int x; 624 625 x = 1; 626 host_setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &x, sizeof(x)); 627 628 return 0; 629 } 630 631 static char parsedurl[256]; 632 633 /*ARGSUSED*/ 634 static int 635 unix_parse(const char *addr, struct sockaddr **sa, int allow_wildcard) 636 { 637 struct sockaddr_un s_un; 638 size_t slen; 639 int savepath = 0; 640 641 if (strlen(addr) >= sizeof(s_un.sun_path)) 642 return ENAMETOOLONG; 643 644 /* 645 * The pathname can be all kinds of spaghetti elementals, 646 * so meek and obidient we accept everything. However, use 647 * full path for easy cleanup in case someone gives a relative 648 * one and the server does a chdir() between now than the 649 * cleanup. 650 */ 651 memset(&s_un, 0, sizeof(s_un)); 652 s_un.sun_family = AF_LOCAL; 653 if (*addr != '/') { 654 char mywd[PATH_MAX]; 655 656 if (getcwd(mywd, sizeof(mywd)) == NULL) { 657 fprintf(stderr, "warning: cannot determine cwd, " 658 "omitting socket cleanup\n"); 659 } else { 660 if (strlen(addr)+strlen(mywd)+1 661 >= sizeof(s_un.sun_path)) 662 return ENAMETOOLONG; 663 strcpy(s_un.sun_path, mywd); 664 strcat(s_un.sun_path, "/"); 665 savepath = 1; 666 } 667 } 668 strcat(s_un.sun_path, addr); 669 #if defined(__linux__) || defined(__sun__) || defined(__CYGWIN__) 670 slen = sizeof(s_un); 671 #else 672 s_un.sun_len = SUN_LEN(&s_un); 673 slen = s_un.sun_len+1; /* get the 0 too */ 674 #endif 675 676 if (savepath && *parsedurl == '\0') { 677 snprintf(parsedurl, sizeof(parsedurl), 678 "unix://%s", s_un.sun_path); 679 } 680 681 *sa = malloc(slen); 682 if (*sa == NULL) 683 return errno; 684 memcpy(*sa, &s_un, slen); 685 686 return 0; 687 } 688 689 static void 690 unix_cleanup(struct sockaddr *sa) 691 { 692 struct sockaddr_un *s_sun = (void *)sa; 693 694 /* 695 * cleanup only absolute paths. see unix_parse() above 696 */ 697 if (*s_sun->sun_path == '/') { 698 unlink(s_sun->sun_path); 699 } 700 } 701 702 /*ARGSUSED*/ 703 static int 704 notsupp(void) 705 { 706 707 fprintf(stderr, "rump_sp: support not yet implemented\n"); 708 return EOPNOTSUPP; 709 } 710 711 static int 712 success(void) 713 { 714 715 return 0; 716 } 717 718 static struct { 719 const char *id; 720 int domain; 721 socklen_t slen; 722 addrparse_fn ap; 723 connecthook_fn connhook; 724 cleanup_fn cleanup; 725 } parsetab[] = { 726 { "tcp", PF_INET, sizeof(struct sockaddr_in), 727 tcp_parse, tcp_connecthook, (cleanup_fn)success }, 728 { "unix", PF_LOCAL, sizeof(struct sockaddr_un), 729 unix_parse, (connecthook_fn)success, unix_cleanup }, 730 { "tcp6", PF_INET6, sizeof(struct sockaddr_in6), 731 (addrparse_fn)notsupp, (connecthook_fn)success, 732 (cleanup_fn)success }, 733 }; 734 #define NPARSE (sizeof(parsetab)/sizeof(parsetab[0])) 735 736 static int 737 parseurl(const char *url, struct sockaddr **sap, unsigned *idxp, 738 int allow_wildcard) 739 { 740 char id[16]; 741 const char *p, *p2; 742 size_t l; 743 unsigned i; 744 int error; 745 746 /* 747 * Parse the url 748 */ 749 750 p = url; 751 p2 = strstr(p, "://"); 752 if (!p2) { 753 fprintf(stderr, "rump_sp: invalid locator ``%s''\n", p); 754 return EINVAL; 755 } 756 l = p2-p; 757 if (l > sizeof(id)-1) { 758 fprintf(stderr, "rump_sp: identifier too long in ``%s''\n", p); 759 return EINVAL; 760 } 761 762 strncpy(id, p, l); 763 id[l] = '\0'; 764 p2 += 3; /* beginning of address */ 765 766 for (i = 0; i < NPARSE; i++) { 767 if (strcmp(id, parsetab[i].id) == 0) { 768 error = parsetab[i].ap(p2, sap, allow_wildcard); 769 if (error) 770 return error; 771 break; 772 } 773 } 774 if (i == NPARSE) { 775 fprintf(stderr, "rump_sp: invalid identifier ``%s''\n", p); 776 return EINVAL; 777 } 778 779 *idxp = i; 780 return 0; 781 } 782