home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #31 / NN_1992_31.iso / spool / comp / lang / lisp / 3141 < prev    next >
Encoding:
Text File  |  1992-12-27  |  13.7 KB  |  504 lines

  1. Xref: sparky comp.lang.lisp:3141 comp.lang.lisp.x:283
  2. Newsgroups: comp.lang.lisp,comp.lang.lisp.x
  3. Path: sparky!uunet!newsflash.concordia.ca!nstn.ns.ca!dragon.acadiau.ca!850347s
  4. From: 850347s@dragon.acadiau.ca (Hume Smith)
  5. Subject: XLisp UNIX sockets
  6. Message-ID: <1992Dec27.072416.12822@dragon.acadiau.ca>
  7. Organization: Acadia University
  8. Date: Sun, 27 Dec 1992 07:24:16 GMT
  9. Lines: 493
  10.  
  11. my christmas present to the lisp world :-)
  12. a shell script to add tcp socket support to xlisp source.
  13. run it with the xlisp source directory current and recompile.
  14.  
  15.  
  16. #!/bin/sh
  17. # this shell script modifies the UNIX XLisp sources
  18. # to provide socket support.  execute it in the
  19. # xlisp/sources directory.
  20.  
  21. # if you made changes to makebsd, you'll have to
  22. # move them to the new makebsd.
  23.  
  24. # moves   makebsd   to makebsd.bak
  25. # copies  osdefs.h  to osdefs.h.bak
  26. # copies  osptrs.h  to osptrs.h.bak
  27. # creates unixsock.c
  28. # creates socket.txt
  29.  
  30.  
  31. if test -f unixsock.c; then
  32. echo 'unixsock.c exists... have you run this before?'
  33. exit 1
  34. fi
  35.  
  36.  
  37. echo "replacing makebsd"
  38. mv makebsd makebsd.bak
  39. cat - >makebsd <<ENDOFIT
  40. # this for SunOS
  41. CC = /usr/ucb/cc
  42.  
  43. XLISP=xlisp
  44.  
  45. OBJ=xlisp.o xlbfun.o xlcont.o xldbug.o xldmem.o xleval.o \
  46. xlfio.o xlglob.o xlimage.o xlinit.o xlio.o xljump.o xllist.o \
  47. xlmath.o xlobj.o xlpp.o xlprin.o xlread.o xlstr.o xlsubr.o \
  48. xlsym.o xlsys.o unixprim.o unixstuf.o xlseq.o xlstruct.o xlftab.o \
  49. xlmath2.o unixsock.o
  50.  
  51. CFLAGS = -O -DBSD -DUNIX
  52.  
  53. $(XLISP): $(OBJ)
  54.     $(CC) -o $(XLISP) $(OBJ) -lm
  55.     strip $(XLISP)
  56.  
  57. $(OBJ): xlisp.h
  58.  
  59. xlftab.o: osdefs.h osptrs.h
  60.  
  61. clean:
  62.     rm -f *.o Make.log
  63. ENDOFIT
  64.  
  65.  
  66. echo "changing osptrs.h"
  67. cp -p osptrs.h osptrs.h.bak
  68. cat - >> osptrs.h <<ENDOFIT
  69.  
  70. #ifdef UNIX
  71. {"CLIENT", S, xclient},
  72. {"MAKE-SERVER", S, xmakeserver},
  73. {"DESTROY-SERVER", S, xdestserver},
  74. {"ACCEPT", S, xaccept},
  75. {"FLUSH", S, xflush},
  76. #endif
  77. ENDOFIT
  78.  
  79.  
  80. echo "changing osdefs.s"
  81. cp -p osdefs.h osdefs.h.bak
  82. cat - >> osdefs.h <<ENDOFIT
  83.  
  84. #ifdef UNIX
  85. extern LVAL
  86.     xclient(V), xmakeserver(V), xdestserver(V), xaccept(V), xflush(V);
  87. #endif
  88. ENDOFIT
  89.  
  90.  
  91. echo "writing socket.txt"
  92. cat - > socket.txt <<ENDOFIT
  93. The following are provided by unixsock.c and related modifications.
  94.  
  95. (client <port> [<host>])                           Connect to a Server
  96.      <port>   port name or number
  97.      <host>   hostname, dotted IP number 
  98.      returns  an IO stream
  99.  
  100.      Client attempts to establish a TCP stream socket connection to
  101.      the specified server.  It returns an ordinary two-way XLisp
  102.      stream if it is successful.  If <host> is missing, the local
  103.      machine is contacted.  <port> may be a fixnum or a string.
  104.      <host> must be a string, e.g. "dragon.acadiau.ca" or
  105.      "47.208.130.26".
  106.  
  107. (make-server <port> <qlen>)                    Construct a Server Port
  108.      <port>   port number
  109.      <qlen>   queue length
  110.      returns  UNIX file descriptor, fixnum
  111.  
  112. (accept <fd>)                          Accept a Connection to a Server
  113.      <fd>     UNIX file descriptor, fixnum
  114.      returns  an IO stream
  115.  
  116. (destroy-server <fd>)                                 Destroy a Server 
  117.      <fd>     UNIX file descriptor, fixnum
  118.      returns  T on success, NIL on failure
  119.  
  120.      The server end of a socket is much more complicated than the
  121.      client end.  I've almost certainly sacrified some elegance and
  122.      safety here in order to provide as much service as possible.
  123.  
  124.      Notice that make-server does not establish an actual connection.
  125.      All it does is set up a contact point for clients that want to
  126.      connect.  More than one client can attept to connect at one time,
  127.      and more than one can be connected at one time.  To keep track
  128.      of those attempting to connect, make-server provides a queue
  129.      in which up to <qlen> clients may wait.
  130.  
  131.      To actually connect a client requires accept.  Calling accept
  132.      takes the next client in the queue and connects it,
  133.      constructing a new socket separate from the server contact
  134.      point.  The connection is returned in the form of an XLisp
  135.      two-way stream.  The client can now read what the server writes
  136.      and vice versa.
  137.  
  138.      Once you're done with the contact point, you get rid of it
  139.      with destroy-server.  Destroy-server rejects any queued clients
  140.      and causes further connection attempts to be refused.  (Until
  141.      the contact is rebuilt by another program or another call
  142.      to make-server, of course; it is not normally necessary to
  143.      reboot the machine in order to re-use a port.)  Destroy-server
  144.      does not affect connections; the streams returned from client
  145.      and accept are completely independent of the contact point
  146.      itself.  To get rid of them, close them as you would any other
  147.      XLisp stream.
  148.  
  149.      The fixnum you get back from make-server is a UNIX file
  150.      descriptor (an ordinary fixnum).  The OS needs that number to get
  151.      at the contact point it's built.  You must give it to accept and
  152.      destroy-server.  You can also have more than one contact point
  153.      operating at a given time, distinguishing them by file
  154.      descriptor.
  155.  
  156. (flush <stream>)                        Force Buffered Data to be Sent
  157.      <stream> anything but a string stream
  158.      returns  T on success, NIL on failure
  159.  
  160.      Data written to a socket are not immediately sent.  They are
  161.      accumulated in a system buffer, and are not transmitted until
  162.      - the socket is read from
  163.      - the socket is closed
  164.      - the buffer is full
  165.      - an explicit order to send is given
  166.      The last is done by flush.  Probably most of the time a write
  167.      to a socket will be immediately followed by a read anyway, in
  168.      which case flush won't be needed.  But you may, for example,
  169.      recieve a command over the socket and wish to acknowledge it
  170.      before carrying out the order and sending the results.  Then
  171.      you'd need flush.
  172.  
  173.      See fflush in the man pages for details.  You may have to be
  174.      cautious in its application on some systems that buffered unread
  175.      data are not lost.
  176.  
  177. Future
  178.      Adding capability to XLisp for non-blocking IO would
  179.      be very handy for this application.  Since PCs are XLisps main
  180.      machine as far as Tom Almy's concerned (profuse apologies if I'm
  181.      misrepresenting him here), and they're slowly becoming
  182.      multitasking machines, the file support may be extended in this
  183.      direction soon anyway.  (Maybe it's in the soon-to-be ANSI
  184.      standards?)  (Maybe all this stuff's in the standards anyway
  185.      and all this will go out the proverbial window. :-)
  186.  
  187.      UNIX's select() would probably be very nice to have for this
  188.      sort of stuff too.  If I understand the manual, it may well be even
  189.      better than non-blocking IO.  (And the function itself seems
  190.      more Lispish than C anyway.)
  191.  
  192. Acknowledgements
  193.      Thanks to everyone who worked to build XLisp, of course.
  194.  
  195.      Particular thanks to James Wilson for writing
  196.        Berkeley UNIX: A Simple and Comprehensive Guide
  197.      Self-contradictory as the name may sound :-), it's a good gateway
  198.      into the innards of UNIX.  It also provided my first pieces of
  199.      working socket code.
  200.  
  201. Caveats
  202.      I make no warrant for the utility or safety of this code.  If
  203.      a wreck of your network, filesevers, gateways, social life, 
  204.      bank account, or anything else can be traced to its use or
  205.      existence, I am not responsible for it.
  206. ENDOFIT
  207.  
  208.  
  209. echo writing unixsock.c
  210. cut '-c2-' > 'unixsock.c' <<'ENDOFIT'
  211. X/* unixsock - xlisp socket functions
  212. X *
  213. X * by hume.smith@acadiau.ca
  214. X * permission is granted for unrestricted non-commercial use
  215. X * 1992 Dec 25
  216. X *
  217. X * I make no warrant for the utility or safety of this code.  If
  218. X * a wreck of your network, filesevers, gateways, social life, 
  219. X * bank account, or anything else can be traced to its use or
  220. X * existence, I am not responsible for it.
  221. X */
  222. X
  223. X#include "xlisp.h"
  224. X#include <sys/types.h>
  225. X#include <sys/socket.h>
  226. X#include <netdb.h>
  227. X#include <netinet/in.h>
  228. X
  229. Xextern LVAL true;
  230. X
  231. X/* set this if you want access to services(5) */
  232. X#define USESERVICES
  233. X
  234. Xextern long strtol();
  235. Xextern int errno;
  236. X
  237. X/* an ANSI function not defined on all machines */
  238. Xchar *strerror(err)
  239. Xint err;
  240. X{
  241. X    extern int sys_nerr;
  242. X    extern char *sys_errlist[];
  243. X    static char buff[64];
  244. X    
  245. X    return (0 <= err && err<sys_nerr) 
  246. X           ? sys_errlist[err]
  247. X           : sprintf(buff, "(Error %d)", err);
  248. X}
  249. X
  250. X/* convert dotted decimal IP address (string)
  251. X * to struct in_addr.
  252. X * returns
  253. X *    0    ok
  254. X *    1    wrong number of byte fields
  255. X *    2    out-of-range byte field
  256. X *    3    bad character
  257. X *  other  serious problem
  258. X */
  259. Xint IPtol(x,z)
  260. Xchar *x;
  261. Xstruct in_addr *z;
  262. X{
  263. X    u_long r, c;
  264. X    int f, p;
  265. X    
  266. X    for(f=!0, p=r=c=0; ; ++x)
  267. X        if('0'<=*x && *x<='9') {
  268. X            f = 0;
  269. X            c = c*10 + (*x - '0');
  270. X        }
  271. X        else if (*x=='.' || !*x) {
  272. X            if(c&~0xffl || f)
  273. X                return 2;
  274. X            r = r<<8 | c;
  275. X            if(!*x) {
  276. X                if(p != 3)
  277. X                    return 1;
  278. X                z->S_un.S_addr = r;
  279. X                return 0;
  280. X            }
  281. X            ++p;
  282. X            c = 0;
  283. X            f = !0;
  284. X        }
  285. X        else
  286. X            return 3;
  287. X
  288. X    /* should never be reached */
  289. X    return 4;
  290. X}
  291. X
  292. XLVAL xclient()
  293. X{
  294. X    LVAL hostarg, portarg;
  295. X    int port, slot, sd;
  296. X    struct sockaddr_in sin;
  297. X    struct hostent *hp;
  298. X    struct servent *se;
  299. X    char *portname = 0, *hostname = 0, buff[128];
  300. X    
  301. X#ifdef USESERVICES
  302. X    portarg = xlgetarg();
  303. X#else
  304. X    port = (int)getfixnum(xlgafixnum());
  305. X#endif
  306. X    if (moreargs()) {
  307. X        hostname = getstring(hostarg = xlgastring());
  308. X        xllastarg();
  309. X    }
  310. X    
  311. X#ifdef USESERVICES
  312. X    if (stringp(portarg)) {
  313. X        portname = getstring(portarg);
  314. X        if (!(se = getservbyname(portname, "tcp")))
  315. X            xlerror("unknown port", portarg);
  316. X        port = se->s_port;
  317. X        portname = se->s_name;
  318. X    }
  319. X    else if (fixp(portarg)) {
  320. X        port = (int)getfixnum(portarg);
  321. X        if (se = getservbyport(port, "tcp"))
  322. X            portname = se->s_name;
  323. X    }
  324. X    else
  325. X        xlbadtype(portarg);
  326. X#endif
  327. X        
  328. X    /* convert host name / IP address
  329. X     * to internal address form
  330. X     */
  331. X
  332. X    bzero((char *)&sin, sizeof(sin));
  333. X    sin.sin_port = port;
  334. X    if (!IPtol(hostname, &sin.sin_addr)) {
  335. X        /* host is a dotted IP address */
  336. X        sin.sin_family = AF_INET;
  337. X
  338. X        if (hp = gethostbyaddr((char *)&sin.sin_addr, 4, AF_INET)) {
  339. X            /* frill; use the real host name
  340. X             * for a nice file table entry
  341. X             */
  342. X            hostname = hp->h_name;
  343. X        }
  344. X    }
  345. X    else
  346. X    {
  347. X        if (!hostname) {
  348. X            /* host wasn't given... get our own name */
  349. X            if (gethostname(buff, sizeof(buff)))
  350. X                xlfail(strerror(errno));
  351. X            
  352. X            /* point host to the buffer */
  353. X            hostname = buff;
  354. X        }
  355. X        
  356. X        /* look up the host name */
  357. X        if (!(hp = gethostbyname(hostname)))
  358. X            xlfail(strerror(errno));
  359. X        
  360. X        /* set the structure */
  361. X        bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
  362. X        sin.sin_family = hp->h_addrtype;
  363. X        
  364. X        /* use the host's `official' name */
  365. X        hostname = hp->h_name;
  366. X    }
  367. X    
  368. X    /* build a name for file table */
  369. X    if (portname)
  370. X        sprintf(buff, "%s@%s", portname, hostname);
  371. X    else
  372. X        sprintf(buff, "%d@%s", port, hostname);
  373. X
  374. X    /* obtain an available filetable slot */
  375. X    slot = getslot();
  376. X    
  377. X    /* make, connect, promote, and record the socket */
  378. X    if (!(filetab[slot].tname = strdup(buff)) ||
  379. X        (sd = socket(PF_INET, SOCK_STREAM, 0)) < 0 ||
  380. X        connect(sd, (char *)&sin, sizeof(sin)) ||
  381. X        !(filetab[slot].fp = fdopen(sd, "r+"))) {
  382. X        int e = errno;
  383. X
  384. X        /* something went wrong, clean up */
  385. X        if (filetab[slot].tname)
  386. X            free(filetab[slot].tname);
  387. X        if (sd >= 0)
  388. X            close(sd);
  389. X        xlfail(strerror(e));
  390. X    }
  391. X    
  392. X    return cvfile(slot, S_FORREADING|S_FORWRITING);   
  393. X}
  394. X
  395. X
  396. XLVAL xmakeserver()
  397. X{
  398. X    FIXTYPE port;
  399. X    struct sockaddr_in ServA;
  400. X    struct hostent *hp;
  401. X    int ServFD, length;
  402. X    char buf[128];
  403. X
  404. X    port = getfixnum(xlgafixnum());
  405. X    length = (int)getfixnum(xlgafixnum());
  406. X    xllastarg();
  407. X   
  408. X    /* get address of local machine */
  409. X    bzero((char *)&ServA, sizeof(ServA));
  410. X    if(gethostname(buf, sizeof(buf)) ||
  411. X       !(hp = gethostbyname(buf)))
  412. X        xlfail(strerror(errno));
  413. X    bcopy(hp->h_addr, (char *)&ServA.sin_addr, hp->h_length);
  414. X    ServA.sin_family = hp->h_addrtype;
  415. X    ServA.sin_port = port;
  416. X    
  417. X    /* make the socket */
  418. X    if((ServFD = socket(PF_INET, SOCK_STREAM, 0)) < 0)
  419. X        xlfail(strerror(errno));
  420. X    
  421. X    /* address the socket */
  422. X    if(bind(ServFD, (char *)&ServA, sizeof(ServA)) ||
  423. X       listen(ServFD, length)) {
  424. X        close(ServFD);
  425. X        xlfail(strerror(errno));
  426. X    }
  427. X    
  428. X    return cvfixnum((FIXTYPE)ServFD);
  429. X}
  430. X
  431. XLVAL xaccept()
  432. X{
  433. X    int ServFD, ConnFD, ConnL, slot;
  434. X    struct sockaddr_in ConnA;
  435. X    struct hostent *CHost;
  436. X    FILE *ConnS;
  437. X    char buff[128];
  438. X   
  439. X    ServFD = (int)getfixnum(xlgafixnum());
  440. X    xllastarg();
  441. X
  442. X    ConnL = sizeof(ConnA);
  443. X    if((ConnFD = accept(ServFD, (char *)&ConnA, &ConnL)) < 0 ||
  444. X       !(ConnS = fdopen(ConnFD, "r+")))
  445. X        xlfail(strerror(errno));
  446. X
  447. X    if(ConnA.sin_family != AF_INET) {
  448. X        /* we're not really sure where that guy is */
  449. X        strcpy(buff, "client@somewhere");
  450. X    }
  451. X    else if(CHost = gethostbyaddr((char *)&ConnA.sin_addr, 4, AF_INET)) {
  452. X        /* frill; use the client's real host name
  453. X         * for a nice file table entry
  454. X         */
  455. X        sprintf(buff, "%d@%s", ConnA.sin_port, CHost->h_name);
  456. X    }
  457. X    else {
  458. X        /* use the numerical address */
  459. X        sprintf(buff, "%d@%d.%d.%d.%d",
  460. X                ConnA.sin_port,
  461. X                ConnA.sin_addr.S_un.S_un_b.s_b1,
  462. X                ConnA.sin_addr.S_un.S_un_b.s_b2,
  463. X                ConnA.sin_addr.S_un.S_un_b.s_b3,
  464. X                ConnA.sin_addr.S_un.S_un_b.s_b4);
  465. X    }
  466. X    
  467. X    slot = getslot();
  468. X    if(!(filetab[slot].tname = strdup(buff))) {
  469. X        /* seems a shame to have come so far to
  470. X         * fail now, but i don't know if xlisp
  471. X         * likes having nothing here
  472. X         */
  473. X        fclose(ConnS);
  474. X        xlfail("insufficient memory");
  475. X    }
  476. X    filetab[slot].fp = ConnS;
  477. X    return cvfile(slot, S_FORREADING|S_FORWRITING);   
  478. X}
  479. X
  480. X/* returns success */
  481. XLVAL xdestserver()
  482. X{
  483. X   int ServFD;
  484. X   
  485. X   ServFD = (int)getfixnum(xlgafixnum());
  486. X   xllastarg();
  487. X   
  488. X   return close(ServFD) ? NIL : true;
  489. X}
  490. X
  491. X/* returns success */
  492. XLVAL xflush()
  493. X{
  494. X   int fp;
  495. X   
  496. X   fp = getfile(xlgastream());
  497. X   xllastarg();
  498. X   
  499. X   return (fp == CLOSED || fflush(filetab[fp].fp)) ? NIL : true;
  500. X}
  501. ENDOFIT
  502.  
  503. # this is the end of the script
  504.