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