* Re: Non-blocking open-network-stream [not found] <m2u1sa7819.fsf@xaital.online-marketwatch.com> @ 2002-02-21 23:45 ` Kim F. Storm 2002-02-22 16:04 ` Stefan Monnier 2002-02-25 22:38 ` Kim F. Storm 0 siblings, 2 replies; 47+ messages in thread From: Kim F. Storm @ 2002-02-21 23:45 UTC (permalink / raw) Cc: emacs-devel Helmut Eller <helmut@xaital.km4u.net> writes: > Hi, > > I read your post about non-blocking open-network-stream in the web > archive of the emacs-devel list and would like to make some comments. > I write to you directly because I'm not subscribed and I have the > impression the list is "For Developers Only". > Thanks a lot for your feedback. I've copied my response to the list. > I tried the code a bit and I noticed that the sentinel is sometimes > not invoked with "run" when the connections completes successfully but > is closed shortly afterwards by the peer. The sentinel was sometimes > invoked sometimes not. The scenario was very simple: a server > accepted the connection and wrote "hello" to the socket and closed the > connection. Emacs executed this: > > (open-network-stream "nb" "x.x" "localhost" 34567 > nil > (lambda (s msg) > (with-current-buffer (process-buffer s) > (insert msg "\n") > (when (equal "failed" msg) > (message "deleting %S" s) > (delete-process s)))) > t) > This seems to be related to the problem you describe below: reading the process output before checking for the completion of the connect. I'll look into it. > You wrote: > > [...] > > The initial process-state is `connecting' which changes to `open' > > or `failed' depending on whether the connect succeeded or failed. > > Notice that the sentinel string for `open' is "run". > > You could modify status_message to return something more meaningful. > Preferably the symbol 'open. > Yes, I considered doing that. However, I didn't know whether existing code may depend on the current behaviour, and although I couldn't find any in CVS, I decided against changing it. > [...] > > + /* Number of bits set in connect_wait_mask. */ > > + int num_pending_connects; > > + > > Any reason to make this non-static? No. I just forgot it. > > [...] > > ! non_blocking = (NILP (non_blocking) ? Qnil : Qt); > > ! #ifdef NON_BLOCKING_CONNECT > > ! is_non_blocking = !NILP (non_blocking); > > ! #else > > ! is_non_blocking = 0; > > ! #endif > > Wouldn't non_blocking_p be a more lispy name? :-) Yes - but is_non_blocking is not a lisp object :-) > > [...] > > --- 1948,1971 ---- > > turn_on_atimers (1); > > > > if (ret == 0 || xerrno == EISCONN) > > ! { > > ! is_non_blocking = 0; > > ! /* The unwind-protect will be discarded afterwards. > > ! Likewise for immediate_quit. */ > > ! break; > > ! } > > Why do you set is_non_blocking to 0? I can see no later use. For > documentation? It is defensive programming -- in case it ever becomes necessary to test on it later in the code, it contains the proper value. > > > ! > > ! #ifdef NON_BLOCKING_CONNECT > > ! #ifdef EINPROGRESS > > ! if (is_non_blocking && xerrno == EINPROGRESS) > > ! break; > > ! #else > > ! #ifdef EWOULDBLOCK > > ! if (is_non_blocking && xerrno == EWOULDBLOCK) > > break; > > + #endif > > + #endif > > What does it mean when connect returns EWOULDBLOCK? My man page > doesn't mention it. > Neither does mine -- but I saw some references on the Web which mentions this as one of the possible error codes from a non-blocking connect. So I included the test in case EINPROGRESS is not defined. > [...] > > --- 2176,2202 ---- > [...] > > ! if (!NILP (non_blocking)) > > ! { > > ! XPROCESS (proc)->status = Qconnecting; > > ! if (!FD_ISSET (inch, &connect_wait_mask)) > > ! { > > ! FD_SET (inch, &connect_wait_mask); > > ! num_pending_connects++; > > ! } > > ! } > > Why is if(!FD_ISET...) necessary? Because num_pending_connects would be updated incorrectly if it is already set. Defensive programming... > > [...] > > + #ifdef NON_BLOCKING_CONNECT > > + if (check_connect && FD_ISSET (channel, &Connecting)) > > + { > > Isn't it possible that channel becomes readable and writable at the > same time? If yes, wouldn't that mean that read_process_output (and > hence the filter) was already called before we get here? It seems you are right. Maybe the easiest fix would be for read_process_output to just return 0 if the process state is Qconnecting. I'll look into that. > > > + struct Lisp_Process *p; > > + struct sockaddr pname; > > + socklen_t pnamelen = sizeof(pname); > > + > > + FD_CLR (channel, &connect_wait_mask); > > + if (--num_pending_connects < 0) > > + abort (); > > + > > + proc = chan_process[channel]; > > + if (NILP (proc)) > > + continue; > > Is it safe to decrement num_pending_connects even if proc is nil? > > [...] Yes, because we only get here if channel is in the Connecting mask which is a subset of connect_wait_mask. Whether there really is a corresponding process doesn't matter. (there should be one, but I'm playing safe here -- cleaning up the connect_wait_mask in any case). > > + > > + p = XPROCESS (proc); > > + XSETINT (p->tick, ++process_tick); > > + > > + /* If connection failed, getpeername fails */ > > + if (getpeername(channel, &pname, &pnamelen) < 0) > > + { > > + /* Preserve status of processes already terminated. */ > > + p->status = Qfailed; > > + deactivate_process (proc); > > + } > > + else > > It would be nice if the error message (obtained with getsockopt > (channel, SOL_SCOKET, SO_ERROR ...)) would be passed to the sentinel. > That would be reasonable. I'll look into that. > A minor note: open-network-stream contains a large chunk of duplicated > code (starting from "/* Kernel bugs (on Ultrix..." to > "report_file_error ("connection failed...))). This are about 70 lines; > should they be in a separate function? Maybe, or the #ifdefs could be rearranged to avoid the duplication. I'll take a look. -- Kim F. Storm <storm@cua.dk> http://www.cua.dk _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Non-blocking open-network-stream 2002-02-21 23:45 ` Non-blocking open-network-stream Kim F. Storm @ 2002-02-22 16:04 ` Stefan Monnier 2002-02-25 22:38 ` Kim F. Storm 1 sibling, 0 replies; 47+ messages in thread From: Stefan Monnier @ 2002-02-22 16:04 UTC (permalink / raw) Cc: Helmut Eller, emacs-devel > > > ! non_blocking = (NILP (non_blocking) ? Qnil : Qt); > > > ! #ifdef NON_BLOCKING_CONNECT > > > ! is_non_blocking = !NILP (non_blocking); > > > ! #else > > > ! is_non_blocking = 0; > > > ! #endif > > > > Wouldn't non_blocking_p be a more lispy name? :-) > Yes - but is_non_blocking is not a lisp object :-) Actually the answer was `no'. The `p' postfix stands for `predicate'. A predicate is not a mere boolean value but a function that returns a boolean. Stefan _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Non-blocking open-network-stream 2002-02-21 23:45 ` Non-blocking open-network-stream Kim F. Storm 2002-02-22 16:04 ` Stefan Monnier @ 2002-02-25 22:38 ` Kim F. Storm 2002-02-26 22:46 ` Helmut Eller 1 sibling, 1 reply; 47+ messages in thread From: Kim F. Storm @ 2002-02-25 22:38 UTC (permalink / raw) Cc: Helmut Eller Here is my second attempt at a patch to support non-blocking open-network-stream. If I don't receive any comments on this, I'll install it later this week. Index: process.c =================================================================== RCS file: /cvs/emacs/src/process.c,v retrieving revision 1.351 diff -c -r1.351 process.c *** process.c 7 Jan 2002 21:16:38 -0000 1.351 --- process.c 25 Feb 2002 22:33:22 -0000 *************** *** 1,6 **** /* Asynchronous subprocess control for GNU Emacs. ! Copyright (C) 1985, 86, 87, 88, 93, 94, 95, 96, 98, 1999, 2001 ! Free Software Foundation, Inc. This file is part of GNU Emacs. --- 1,6 ---- /* Asynchronous subprocess control for GNU Emacs. ! Copyright (C) 1985, 86, 87, 88, 93, 94, 95, 96, 98, 1999, ! 2001, 2002 Free Software Foundation, Inc. This file is part of GNU Emacs. *************** *** 112,118 **** #include "atimer.h" Lisp_Object Qprocessp; ! Lisp_Object Qrun, Qstop, Qsignal, Qopen, Qclosed; Lisp_Object Qlast_nonmenu_event; /* Qexit is declared and initialized in eval.c. */ --- 112,119 ---- #include "atimer.h" Lisp_Object Qprocessp; ! Lisp_Object Qrun, Qstop, Qsignal; ! Lisp_Object Qopen, Qclosed, Qconnect, Qfailed; Lisp_Object Qlast_nonmenu_event; /* Qexit is declared and initialized in eval.c. */ *************** *** 173,178 **** --- 174,199 ---- /* Number of events for which the user or sentinel has been notified. */ int update_tick; + /* Define NON_BLOCKING_CONNECT if we can support non-blocking connects. */ + + #ifdef BROKEN_NON_BLOCKING_CONNECT + #undef NON_BLOCKING_CONNECT + #else + #ifndef NON_BLOCKING_CONNECT + #ifdef HAVE_SOCKETS + #ifdef HAVE_SELECT + #if defined (HAVE_GETPEERNAME) || defined (GNU_LINUX) + #if defined (O_NONBLOCK) || defined (O_NDELAY) + #if defined (EWOULDBLOCK) || defined (EINPROGRESS) + #define NON_BLOCKING_CONNECT + #endif /* EWOULDBLOCK || EINPROGRESS */ + #endif /* O_NONBLOCK || O_NDELAY */ + #endif /* HAVE_GETPEERNAME || GNU_LINUX */ + #endif /* HAVE_SELECT */ + #endif /* HAVE_SOCKETS */ + #endif /* NON_BLOCKING_CONNECT */ + #endif /* BROKEN_NON_BLOCKING_CONNECT */ + #include "sysselect.h" extern int keyboard_bit_set P_ ((SELECT_TYPE *)); *************** *** 195,200 **** --- 216,230 ---- static SELECT_TYPE non_process_wait_mask; + /* Mask of bits indicating the descriptors that we wait for connect to + complete on. Once they complete, they are removed from this mask + and added to the input_wait_mask and non_keyboard_wait_mask. */ + + static SELECT_TYPE connect_wait_mask; + + /* Number of bits set in connect_wait_mask. */ + static int num_pending_connects; + /* The largest descriptor currently in use for a process object. */ static int max_process_desc; *************** *** 335,340 **** --- 365,377 ---- return concat2 (build_string ("exited abnormally with code "), concat2 (string, string2)); } + else if (EQ (symbol, Qfailed)) + { + string = Fnumber_to_string (make_number (code)); + string2 = build_string ("\n"); + return concat2 (build_string ("failed with code "), + concat2 (string, string2)); + } else return Fcopy_sequence (Fsymbol_name (symbol)); } *************** *** 1741,1762 **** deactivate and close it via delete-process */ DEFUN ("open-network-stream", Fopen_network_stream, Sopen_network_stream, ! 4, 4, 0, doc: /* Open a TCP connection for a service to a host. Returns a subprocess-object to represent the connection. Input and output work as for subprocesses; `delete-process' closes it. ! Args are NAME BUFFER HOST SERVICE. NAME is name for process. It is modified if necessary to make it unique. BUFFER is the buffer (or buffer-name) to associate with the process. Process output goes at end of that buffer, unless you specify an output stream or filter function to handle the output. BUFFER may be also nil, meaning that this process is not associated ! with any buffer ! Third arg is name of the host to connect to, or its IP address. ! Fourth arg SERVICE is name of the service desired, or an integer ! specifying a port number to connect to. */) ! (name, buffer, host, service) ! Lisp_Object name, buffer, host, service; { Lisp_Object proc; #ifdef HAVE_GETADDRINFO --- 1778,1809 ---- deactivate and close it via delete-process */ DEFUN ("open-network-stream", Fopen_network_stream, Sopen_network_stream, ! 4, 7, 0, doc: /* Open a TCP connection for a service to a host. Returns a subprocess-object to represent the connection. Input and output work as for subprocesses; `delete-process' closes it. ! Args are NAME BUFFER HOST SERVICE FILTER SENTINEL NON-BLOCKING. NAME is name for process. It is modified if necessary to make it unique. BUFFER is the buffer (or buffer-name) to associate with the process. Process output goes at end of that buffer, unless you specify an output stream or filter function to handle the output. BUFFER may be also nil, meaning that this process is not associated ! with any buffer. ! HOST is name of the host to connect to, or its IP address. ! SERVICE is name of the service desired, or an integer specifying a ! port number to connect to. ! FILTER and SENTINEL are optional args specifying the filter and ! sentinel functions associated with the network stream. ! NON-BLOCKING is optional arg requesting an non-blocking connect. ! When non-nil, open-network-stream will return immediately without ! waiting for the connection to be made. Instead, the sentinel function ! will be called with second arg equal to "run" (if successful) or ! "failed" when the connect completes. ! On systems without non-blocking connect, this function waits ! for the connect to complete and then proceeds emulating a ! non-blocking connect. */) ! (name, buffer, host, service, filter, sentinel, non_blocking) ! Lisp_Object name, buffer, host, service, filter, sentinel, non_blocking; { Lisp_Object proc; #ifdef HAVE_GETADDRINFO *************** *** 1773,1789 **** int port; #endif /* HAVE_GETADDRINFO */ int s = -1, outch, inch; ! struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; int retry = 0; int count = specpdl_ptr - specpdl; int count1; #ifdef WINDOWSNT /* Ensure socket support is loaded if available. */ init_winsock (TRUE); #endif ! GCPRO4 (name, buffer, host, service); CHECK_STRING (name); CHECK_STRING (host); --- 1820,1846 ---- int port; #endif /* HAVE_GETADDRINFO */ int s = -1, outch, inch; ! struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5; int retry = 0; int count = specpdl_ptr - specpdl; int count1; + int is_non_blocking; #ifdef WINDOWSNT /* Ensure socket support is loaded if available. */ init_winsock (TRUE); #endif ! non_blocking = (NILP (non_blocking) ? Qnil : Qt); ! #ifdef NON_BLOCKING_CONNECT ! is_non_blocking = !NILP (non_blocking); ! #else ! is_non_blocking = 0; ! #endif ! ! /* Can only GCPRO 5 variables */ ! sentinel = Fcons (sentinel, filter); ! GCPRO5 (name, buffer, host, service, sentinel); CHECK_STRING (name); CHECK_STRING (host); *************** *** 1867,1872 **** --- 1924,1942 ---- count1 = specpdl_ptr - specpdl; record_unwind_protect (close_file_unwind, make_number (s)); + #ifdef NON_BLOCKING_CONNECT + if (is_non_blocking) + { + #ifdef O_NONBLOCK + ret = fcntl (s, F_SETFL, O_NONBLOCK); + #else + ret = fcntl (s, F_SETFL, O_NDELAY); + #endif + if (ret < 0) + is_non_blocking = 0; + } + #endif + loop: immediate_quit = 1; *************** *** 1885,1893 **** turn_on_atimers (1); if (ret == 0 || xerrno == EISCONN) ! /* The unwind-protect will be discarded afterwards. ! Likewise for immediate_quit. */ break; immediate_quit = 0; --- 1955,1978 ---- turn_on_atimers (1); if (ret == 0 || xerrno == EISCONN) ! { ! is_non_blocking = 0; ! /* The unwind-protect will be discarded afterwards. ! Likewise for immediate_quit. */ ! break; ! } ! ! #ifdef NON_BLOCKING_CONNECT ! #ifdef EINPROGRESS ! if (is_non_blocking && xerrno == EINPROGRESS) ! break; ! #else ! #ifdef EWOULDBLOCK ! if (is_non_blocking && xerrno == EWOULDBLOCK) break; + #endif + #endif + #endif immediate_quit = 0; *************** *** 1989,2001 **** if (interrupt_input) unrequest_sigio (); loop: immediate_quit = 1; QUIT; ! if (connect (s, (struct sockaddr *) &address, sizeof address) == -1 ! && errno != EISCONN) { int xerrno = errno; --- 2074,2115 ---- if (interrupt_input) unrequest_sigio (); + #ifdef NON_BLOCKING_CONNECT + if (is_non_blocking) + { + #ifdef O_NONBLOCK + ret = fcntl (s, F_SETFL, O_NONBLOCK); + #else + ret = fcntl (s, F_SETFL, O_NDELAY); + #endif + if (ret < 0) + is_non_blocking = 0; + } + #endif /* NON_BLOCKING_CONNECT */ + loop: immediate_quit = 1; QUIT; ! ret = connect (s, (struct sockaddr *) &address, sizeof address); ! ! if (ret == 0 || errno == EISCONN) ! { ! is_non_blocking = 0; ! } ! #ifdef NON_BLOCKING_CONNECT ! #ifdef EINPROGRESS ! else if (is_non_blocking && ret == -1 && errno == EINPROGRESS) ! ; ! #else ! #ifdef EWOULDBLOCK ! else if (is_non_blocking && ret == -1 && errno == EWOULDBLOCK) ! ; ! #endif ! #endif ! #endif ! else { int xerrno = errno; *************** *** 2041,2046 **** --- 2155,2161 ---- request_sigio (); #else /* TERM */ + is_non_blocking = 0; s = connect_server (0); if (s < 0) report_file_error ("error creating socket", Fcons (name, Qnil)); *************** *** 2068,2082 **** XPROCESS (proc)->childp = Fcons (host, Fcons (service, Qnil)); XPROCESS (proc)->command_channel_p = Qnil; XPROCESS (proc)->buffer = buffer; ! XPROCESS (proc)->sentinel = Qnil; ! XPROCESS (proc)->filter = Qnil; XPROCESS (proc)->command = Qnil; XPROCESS (proc)->pid = Qnil; XSETINT (XPROCESS (proc)->infd, inch); XSETINT (XPROCESS (proc)->outfd, outch); XPROCESS (proc)->status = Qrun; ! FD_SET (inch, &input_wait_mask); ! FD_SET (inch, &non_keyboard_wait_mask); if (inch > max_process_desc) max_process_desc = inch; --- 2183,2209 ---- XPROCESS (proc)->childp = Fcons (host, Fcons (service, Qnil)); XPROCESS (proc)->command_channel_p = Qnil; XPROCESS (proc)->buffer = buffer; ! XPROCESS (proc)->sentinel = XCAR (sentinel); ! XPROCESS (proc)->filter = XCDR (sentinel); XPROCESS (proc)->command = Qnil; XPROCESS (proc)->pid = Qnil; XSETINT (XPROCESS (proc)->infd, inch); XSETINT (XPROCESS (proc)->outfd, outch); XPROCESS (proc)->status = Qrun; ! if (!NILP (non_blocking)) ! { ! XPROCESS (proc)->status = Qconnect; ! if (!FD_ISSET (inch, &connect_wait_mask)) ! { ! FD_SET (inch, &connect_wait_mask); ! num_pending_connects++; ! } ! } ! else if (!EQ (XPROCESS (proc)->filter, Qt)) ! { ! FD_SET (inch, &input_wait_mask); ! FD_SET (inch, &non_keyboard_wait_mask); ! } if (inch > max_process_desc) max_process_desc = inch; *************** *** 2194,2199 **** --- 2321,2332 ---- chan_process[inchannel] = Qnil; FD_CLR (inchannel, &input_wait_mask); FD_CLR (inchannel, &non_keyboard_wait_mask); + if (FD_ISSET (inchannel, &connect_wait_mask)) + { + FD_CLR (inchannel, &connect_wait_mask); + if (--num_pending_connects < 0) + abort (); + } if (inchannel == max_process_desc) { int i; *************** *** 2358,2367 **** { register int channel, nfds; static SELECT_TYPE Available; int xerrno; Lisp_Object proc; EMACS_TIME timeout, end_time; - SELECT_TYPE Atemp; int wait_channel = -1; struct Lisp_Process *wait_proc = 0; int got_some_input = 0; --- 2491,2501 ---- { register int channel, nfds; static SELECT_TYPE Available; + static SELECT_TYPE Connecting; + int check_connect, no_avail; int xerrno; Lisp_Object proc; EMACS_TIME timeout, end_time; int wait_channel = -1; struct Lisp_Process *wait_proc = 0; int got_some_input = 0; *************** *** 2370,2375 **** --- 2504,2510 ---- Lisp_Object wait_for_cell = Qnil; FD_ZERO (&Available); + FD_ZERO (&Connecting); /* If read_kbd is a process to watch, set wait_proc and wait_channel accordingly. */ *************** *** 2511,2521 **** timeout to get our attention. */ if (update_tick != process_tick && do_display) { Atemp = input_wait_mask; EMACS_SET_SECS_USECS (timeout, 0, 0); if ((select (max (max_process_desc, max_keyboard_desc) + 1, ! &Atemp, (SELECT_TYPE *)0, (SELECT_TYPE *)0, ! &timeout) <= 0)) { /* It's okay for us to do this and then continue with --- 2646,2660 ---- timeout to get our attention. */ if (update_tick != process_tick && do_display) { + SELECT_TYPE Atemp, Ctemp; + Atemp = input_wait_mask; + Ctemp = connect_wait_mask; EMACS_SET_SECS_USECS (timeout, 0, 0); if ((select (max (max_process_desc, max_keyboard_desc) + 1, ! &Atemp, ! (num_pending_connects > 0 ? &Ctemp : (SELECT_TYPE *)0), ! (SELECT_TYPE *)0, &timeout) <= 0)) { /* It's okay for us to do this and then continue with *************** *** 2525,2535 **** } } ! /* Don't wait for output from a non-running process. */ if (wait_proc != 0 && !NILP (wait_proc->raw_status_low)) update_status (wait_proc); if (wait_proc != 0 ! && ! EQ (wait_proc->status, Qrun)) { int nread, total_nread = 0; --- 2664,2676 ---- } } ! /* Don't wait for output from a non-running process. Just ! read whatever data has already been received. */ if (wait_proc != 0 && !NILP (wait_proc->raw_status_low)) update_status (wait_proc); if (wait_proc != 0 ! && ! EQ (wait_proc->status, Qrun) ! && ! EQ (wait_proc->status, Qconnect)) { int nread, total_nread = 0; *************** *** 2568,2578 **** /* Wait till there is something to do */ if (!NILP (wait_for_cell)) ! Available = non_process_wait_mask; ! else if (! XINT (read_kbd)) ! Available = non_keyboard_wait_mask; else ! Available = input_wait_mask; /* If frame size has changed or the window is newly mapped, redisplay now, before we start to wait. There is a race --- 2709,2726 ---- /* Wait till there is something to do */ if (!NILP (wait_for_cell)) ! { ! Available = non_process_wait_mask; ! check_connect = 0; ! } else ! { ! if (! XINT (read_kbd)) ! Available = non_keyboard_wait_mask; ! else ! Available = input_wait_mask; ! check_connect = (num_pending_connects > 0); ! } /* If frame size has changed or the window is newly mapped, redisplay now, before we start to wait. There is a race *************** *** 2587,2601 **** set_waiting_for_input (&timeout); } if (XINT (read_kbd) && detect_input_pending ()) { nfds = 0; ! FD_ZERO (&Available); } else ! nfds = select (max (max_process_desc, max_keyboard_desc) + 1, ! &Available, (SELECT_TYPE *)0, (SELECT_TYPE *)0, ! &timeout); xerrno = errno; --- 2735,2755 ---- set_waiting_for_input (&timeout); } + no_avail = 0; if (XINT (read_kbd) && detect_input_pending ()) { nfds = 0; ! no_avail = 1; } else ! { ! if (check_connect) ! Connecting = connect_wait_mask; ! nfds = select (max (max_process_desc, max_keyboard_desc) + 1, ! &Available, ! (check_connect ? &Connecting : (SELECT_TYPE *)0), ! (SELECT_TYPE *)0, &timeout); ! } xerrno = errno; *************** *** 2611,2617 **** if (nfds < 0) { if (xerrno == EINTR) ! FD_ZERO (&Available); #ifdef ultrix /* Ultrix select seems to return ENOMEM when it is interrupted. Treat it just like EINTR. Bleah. Note --- 2765,2771 ---- if (nfds < 0) { if (xerrno == EINTR) ! no_avail = 1; #ifdef ultrix /* Ultrix select seems to return ENOMEM when it is interrupted. Treat it just like EINTR. Bleah. Note *************** *** 2619,2631 **** "__ultrix__"; the latter is only defined under GCC, but not by DEC's bundled CC. -JimB */ else if (xerrno == ENOMEM) ! FD_ZERO (&Available); #endif #ifdef ALLIANT /* This happens for no known reason on ALLIANT. I am guessing that this is the right response. -- RMS. */ else if (xerrno == EFAULT) ! FD_ZERO (&Available); #endif else if (xerrno == EBADF) { --- 2773,2785 ---- "__ultrix__"; the latter is only defined under GCC, but not by DEC's bundled CC. -JimB */ else if (xerrno == ENOMEM) ! no_avail = 1; #endif #ifdef ALLIANT /* This happens for no known reason on ALLIANT. I am guessing that this is the right response. -- RMS. */ else if (xerrno == EFAULT) ! no_avail = 1; #endif else if (xerrno == EBADF) { *************** *** 2637,2643 **** So, SIGHUP is ignored (see def of PTY_TTY_NAME_SPRINTF in m/ibmrt-aix.h), and here we just ignore the select error. Cleanup occurs c/o status_notify after SIGCLD. */ ! FD_ZERO (&Available); /* Cannot depend on values returned */ #else abort (); #endif --- 2791,2797 ---- So, SIGHUP is ignored (see def of PTY_TTY_NAME_SPRINTF in m/ibmrt-aix.h), and here we just ignore the select error. Cleanup occurs c/o status_notify after SIGCLD. */ ! no_avail = 1; /* Cannot depend on values returned */ #else abort (); #endif *************** *** 2645,2653 **** else error ("select error: %s", emacs_strerror (xerrno)); } #if defined(sun) && !defined(USG5_4) ! else if (nfds > 0 && keyboard_bit_set (&Available) ! && interrupt_input) /* System sometimes fails to deliver SIGIO. David J. Mackenzie says that Emacs doesn't compile under --- 2799,2814 ---- else error ("select error: %s", emacs_strerror (xerrno)); } + + if (no_avail) + { + FD_ZERO (&Available); + check_connect = 0; + } + #if defined(sun) && !defined(USG5_4) ! if (nfds > 0 && keyboard_bit_set (&Available) ! && interrupt_input) /* System sometimes fails to deliver SIGIO. David J. Mackenzie says that Emacs doesn't compile under *************** *** 2746,2751 **** --- 2907,2915 ---- do_pending_window_change (0); /* Check for data from a process. */ + if (no_avail || nfds == 0) + continue; + /* Really FIRST_PROC_DESC should be 0 on Unix, but this is safer in the short run. */ for (channel = 0; channel <= max_process_desc; channel++) *************** *** 2837,2842 **** --- 3001,3047 ---- = Fcons (Qexit, Fcons (make_number (256), Qnil)); } } + #ifdef NON_BLOCKING_CONNECT + if (check_connect && FD_ISSET (channel, &Connecting)) + { + struct Lisp_Process *p; + struct sockaddr pname; + socklen_t pnamelen = sizeof(pname); + + FD_CLR (channel, &connect_wait_mask); + if (--num_pending_connects < 0) + abort (); + + proc = chan_process[channel]; + if (NILP (proc)) + continue; + + p = XPROCESS (proc); + XSETINT (p->tick, ++process_tick); + + /* If connection failed, getpeername fails */ + if (getpeername(channel, &pname, &pnamelen) < 0) + { + char dummy; + + xerrno = errno; + /* Obtain connect failure code through error slippage. */ + if (errno == ENOTCONN && read(channel, &dummy, 1) < 0) + xerrno = errno; + p->status = Fcons (Qfailed, Fcons (make_number (xerrno), Qnil)); + deactivate_process (proc); + } + else + { + p->status = Qrun; + if (!EQ (p->filter, Qt)) + { + FD_SET (XINT (p->infd), &input_wait_mask); + FD_SET (XINT (p->infd), &non_keyboard_wait_mask); + } + } + } + #endif /* NON_BLOCKING_CONNECT */ } /* end for each file descriptor */ } /* end while exit conditions not met */ *************** *** 4419,4424 **** --- 4624,4630 ---- /* If process is still active, read any output that remains. */ while (! EQ (p->filter, Qt) + && ! EQ (p->status, Qconnect) && XINT (p->infd) >= 0 && read_process_output (proc, XINT (p->infd)) > 0); *************** *** 4653,4658 **** --- 4859,4868 ---- staticpro (&Qopen); Qclosed = intern ("closed"); staticpro (&Qclosed); + Qconnect = intern ("connect"); + staticpro (&Qconnect); + Qfailed = intern ("failed"); + staticpro (&Qfailed); Qlast_nonmenu_event = intern ("last-nonmenu-event"); staticpro (&Qlast_nonmenu_event); -- Kim F. Storm <storm@cua.dk> http://www.cua.dk _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Non-blocking open-network-stream 2002-02-25 22:38 ` Kim F. Storm @ 2002-02-26 22:46 ` Helmut Eller 2002-02-27 11:59 ` Kim F. Storm 0 siblings, 1 reply; 47+ messages in thread From: Helmut Eller @ 2002-02-26 22:46 UTC (permalink / raw) Cc: emacs-devel storm@cua.dk (Kim F. Storm) writes: > Here is my second attempt at a patch to support non-blocking > open-network-stream. I think the problem I described last time is still present. The problem is that the filter is invoked before the sentinel. This happens when the stream is readable immediately after the transition from connect state to open state. Consider wait_reading_process_input: > --- 3001,3047 ---- [...] > + XSETINT (p->tick, ++process_tick); > + if (getpeername(channel, &pname, &pnamelen) < 0) [...] > + else > + { > + p->status = Qrun; Tick is incremented and status is set to Qrun. Incrementing tick causes status_notify to be invoked during the next iteration. But status_notify ... > --- 4624,4630 ---- [...] > while (! EQ (p->filter, Qt) > + && ! EQ (p->status, Qconnect) > && XINT (p->infd) >= 0 > && read_process_output (proc, XINT (p->infd)) > 0); ... calls read_process_output (and the filter) before the sentinel. A simple solution is to call the sentinel in wait_reading_process_input without incrementing tick, e.g.: --- 3001,3047 ---- ... + if (getpeername(channel, &pname, &pnamelen) < 0) + XSETINT (p->tick, ++process_tick); ... + else + { + p->status = Qrun; + exec_sentinel (proc, Qopen); Another point: is it a problem to pass the error message and not just the error number to the sentinel? Helmut. _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Non-blocking open-network-stream 2002-02-26 22:46 ` Helmut Eller @ 2002-02-27 11:59 ` Kim F. Storm 2002-02-28 4:08 ` Richard Stallman 0 siblings, 1 reply; 47+ messages in thread From: Kim F. Storm @ 2002-02-27 11:59 UTC (permalink / raw) Cc: emacs-devel Helmut Eller <helmut@xaital.km4u.net> writes: > storm@cua.dk (Kim F. Storm) writes: > > > Here is my second attempt at a patch to support non-blocking > > open-network-stream. > > I think the problem I described last time is still present. The > problem is that the filter is invoked before the sentinel. This > happens when the stream is readable immediately after the transition > from connect state to open state. I've made the chage you suggested. Thanks. There is a general problem with handling a request for non-blocking connect on systems which does not support this. As I have specified this now, open-network-stream will proceed to emulate a non-blocking connect -- which means that it will block. And when the connect completes, it continues to pretend that the connect was non-blocking, so it setup the sentinel to be called after return, and all that jazz. I think it would be much cleaner if (open-network-stream ... t) simply returns nil if it doesn't support non-blocking connect. Then the code can do some smarter things (like delaying the connect), and I don't have to mess up the C code with all sorts of hacks to delay the delivery of a "failed to connect" message on a process which would not otherwise have to exist. Does this sound acceptable (since there is currently no code supporting non-blocking connect, this cannot break anything :-) -- Kim F. Storm <storm@cua.dk> http://www.cua.dk _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Non-blocking open-network-stream 2002-02-27 11:59 ` Kim F. Storm @ 2002-02-28 4:08 ` Richard Stallman 2002-03-01 0:21 ` Kim F. Storm 0 siblings, 1 reply; 47+ messages in thread From: Richard Stallman @ 2002-02-28 4:08 UTC (permalink / raw) Cc: helmut, emacs-devel I think it would be much cleaner if (open-network-stream ... t) simply returns nil if it doesn't support non-blocking connect. I think that is ok. The program could immediately try a blocking connect if that is the right thing to do. _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Non-blocking open-network-stream 2002-02-28 4:08 ` Richard Stallman @ 2002-03-01 0:21 ` Kim F. Storm 2002-03-01 8:01 ` Juanma Barranquero ` (2 more replies) 0 siblings, 3 replies; 47+ messages in thread From: Kim F. Storm @ 2002-03-01 0:21 UTC (permalink / raw) Cc: helmut, emacs-devel Richard Stallman <rms@gnu.org> writes: > I think it would be much cleaner if (open-network-stream ... t) simply > returns nil if it doesn't support non-blocking connect. > > I think that is ok. The program could immediately try a blocking > connect if that is the right thing to do. I have committed the changes to process.c which add the non-blocking connect support to open-network-stream. The next "project" in this area is to add Helmut's server sockets. (do we have papers for that?) However, I think it can be done via open-network-stream: If the HOST argument is nil, a server socket is opened which accepts connections. The sentinel is called - with a newly created process - whenever a connections is accepted. There are some details to be worked out here, but we need to discuss whether we should use this approach or the approach suggested by Helmut. -- Kim F. Storm <storm@cua.dk> http://www.cua.dk _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Non-blocking open-network-stream 2002-03-01 0:21 ` Kim F. Storm @ 2002-03-01 8:01 ` Juanma Barranquero 2002-03-01 10:50 ` Kim F. Storm 2002-03-01 21:23 ` Richard Stallman 2002-03-02 7:59 ` Non-blocking open-network-stream Helmut Eller 2 siblings, 1 reply; 47+ messages in thread From: Juanma Barranquero @ 2002-03-01 8:01 UTC (permalink / raw) Cc: rms, helmut, emacs-devel On 01 Mar 2002 01:21:39 +0100, storm@cua.dk (Kim F. Storm) wrote: > The next "project" in this area is to add Helmut's server sockets. > (do we have papers for that?) > > However, I think it can be done via open-network-stream: > > If the HOST argument is nil, a server socket is opened which > accepts connections. The sentinel is called - with a newly > created process - whenever a connections is accepted. There will be any support for Emacs to act as a server for UDP? /L/e/k/t/u _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Non-blocking open-network-stream 2002-03-01 8:01 ` Juanma Barranquero @ 2002-03-01 10:50 ` Kim F. Storm 2002-03-01 17:10 ` Pavel Janík 0 siblings, 1 reply; 47+ messages in thread From: Kim F. Storm @ 2002-03-01 10:50 UTC (permalink / raw) Cc: helmut, emacs-devel Juanma Barranquero <lektu@terra.es> writes: > On 01 Mar 2002 01:21:39 +0100, storm@cua.dk (Kim F. Storm) wrote: > > > The next "project" in this area is to add Helmut's server sockets. > > (do we have papers for that?) > > > > However, I think it can be done via open-network-stream: > > > > If the HOST argument is nil, a server socket is opened which > > accepts connections. The sentinel is called - with a newly > > created process - whenever a connections is accepted. > > There will be any support for Emacs to act as a server for UDP? I don't even think it can act as a client for UDP currently, so there is really two tasks here. I'd like to look into that later if others think that's something we should support. -- Kim F. Storm http://www.cua.dk _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Non-blocking open-network-stream 2002-03-01 10:50 ` Kim F. Storm @ 2002-03-01 17:10 ` Pavel Janík 0 siblings, 0 replies; 47+ messages in thread From: Pavel Janík @ 2002-03-01 17:10 UTC (permalink / raw) Cc: Juanma Barranquero, helmut, emacs-devel From: storm@cua.dk (Kim F. Storm) Date: 01 Mar 2002 11:50:46 +0100 Hi Kim, > I don't even think it can act as a client for UDP currently, so > there is really two tasks here. I'd like to look into that later > if others think that's something we should support. I think that UDP can wait for some time. The most important thing now is IMHO some examples for people who would like to use it and extend Emacs. Multiplication effect... -- Pavel Janík So calm down guys. And improving the benchmark might not be a bad idea. -- Linus Torvalds in linux-kernel _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Non-blocking open-network-stream 2002-03-01 0:21 ` Kim F. Storm 2002-03-01 8:01 ` Juanma Barranquero @ 2002-03-01 21:23 ` Richard Stallman 2002-03-07 0:08 ` New patch for server sockets and datagram (UDP) support Kim F. Storm 2002-03-02 7:59 ` Non-blocking open-network-stream Helmut Eller 2 siblings, 1 reply; 47+ messages in thread From: Richard Stallman @ 2002-03-01 21:23 UTC (permalink / raw) Cc: helmut, emacs-devel If the HOST argument is nil, a server socket is opened which accepts connections. The sentinel is called - with a newly created process - whenever a connections is accepted. That sounds good to me in principle, if the details work ok. _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* New patch for server sockets and datagram (UDP) support. 2002-03-01 21:23 ` Richard Stallman @ 2002-03-07 0:08 ` Kim F. Storm 2002-03-07 10:56 ` Kim F. Storm ` (2 more replies) 0 siblings, 3 replies; 47+ messages in thread From: Kim F. Storm @ 2002-03-07 0:08 UTC (permalink / raw) Cc: helmut Richard Stallman <rms@gnu.org> writes: > If the HOST argument is nil, a server socket is opened which > accepts connections. The sentinel is called - with a newly > created process - whenever a connections is accepted. > > That sounds good to me in principle, if the details work ok. The following patch adds server socket support via open-network-stream. If the HOST is nil, a server socket for SERVICE is opened in listening state. When a connection is accepted, a new process is created with a name (and buffer) named according to the original process combined with the caller's address, e.g. if the server process is named "p1", the new process will be called something like "p1 <1.2.3.4:928>" Likewise for the buffer. I have removed the NON-BLOCKING argument, and instead added a new TYPE argument, which specifies the type of connection (blocking connect, non-blocking connect, or datagram), and may optionally specify the address family (inet or local [aka. unix]). To open a TCP server socket for "telnet", where client processes have no buffer, do (open-network-stream "telnetd" nil nil "telnet" nil telnetd-filter telnetd-sentinel) To open a UDP (datagram) server socket for "dns", do (open-network-stream "dns" nil nil "dns" 'datagram dns-filter dns-sentinel) Notice that datagram server sockets do not get separate processes for each caller. Instead, there is a new `process-datagram-address' function to get (and set) the client address for the next process-send-... call. To open a LOCAL (UNIX) server socket for "/tmp/xyz", where client processes do have a buffer, do (open-network-stream "xyz" "XYZ" nil "/tmp/xyz" (local) xyz-filter xyz-sentinel) To connect to each of these services, simply specify the hostname instead of nil as the third argument. See also the documentation for the variable `network-server-log-function'. To get a log of accept calls, the following setting can be used: (defun logf (s p m) (if (process-buffer s) (with-current-buffer (process-buffer s) (insert (process-name s) ">>" m)))) (setq network-server-log-function 'logf)) I have tried to make the additions fail-safe by conditioning on AF_LOCAL (or AF_UNIX), and a new DATAGRAM_SOCKETS define. The latter requires that sendto and recvfrom are added to the functions detected by configure. If datagrams are not supported, open-network-stream will return nil when requesting a datagram connection. Index: process.c =================================================================== RCS file: /cvs/emacs/src/process.c,v retrieving revision 1.355 diff -c -r1.355 process.c *** process.c 3 Mar 2002 00:31:22 -0000 1.355 --- process.c 6 Mar 2002 23:40:07 -0000 *************** *** 54,59 **** --- 54,67 ---- #include <netdb.h> #include <netinet/in.h> #include <arpa/inet.h> + #ifndef AF_LOCAL + #ifdef AF_UNIX + #define AF_LOCAL AF_UNIX + #endif + #endif + #ifdef AF_LOCAL + #include <sys/un.h> + #endif #ifdef NEED_NET_ERRNO_H #include <net/errno.h> #endif /* NEED_NET_ERRNO_H */ *************** *** 113,119 **** Lisp_Object Qprocessp; Lisp_Object Qrun, Qstop, Qsignal; ! Lisp_Object Qopen, Qclosed, Qconnect, Qfailed; Lisp_Object Qlast_nonmenu_event; /* Qexit is declared and initialized in eval.c. */ --- 121,128 ---- Lisp_Object Qprocessp; Lisp_Object Qrun, Qstop, Qsignal; ! Lisp_Object Qopen, Qclosed, Qconnect, Qfailed, Qlisten; ! Lisp_Object Qlocal, Qdatagram; Lisp_Object Qlast_nonmenu_event; /* Qexit is declared and initialized in eval.c. */ *************** *** 198,203 **** --- 207,235 ---- #undef NON_BLOCKING_CONNECT #endif + /* Define DATAGRAM_SOCKETS if datagrams can be used safely on + this system. We need to read full packets, so we need a + "non-destructive" select. So we require either native select, + or emulation of select using FIONREAD. */ + + #ifdef GNU_LINUX + #define DATAGRAM_SOCKETS + #endif + + #ifdef BROKEN_DATAGRAM_SOCKETS + #undef DATAGRAM_SOCKETS + #else + #ifndef DATAGRAM_SOCKETS + #ifdef HAVE_SOCKETS + #if defined (HAVE_SELECT) || defined (FIONREAD) + #if defined (HAVE_SENDTO) && defined (HAVE_RECVFROM) && defined (EMSGSIZE) + #define DATAGRAM_SOCKETS + #endif /* HAVE_SENDTO && HAVE_RECVFROM && EMSGSIZE */ + #endif /* HAVE_SELECT || FIONREAD */ + #endif /* HAVE_SOCKETS */ + #endif /* DATAGRAM_SOCKETS */ + #endif /* BROKEN_DATAGRAM_SOCKETS */ + #include "sysselect.h" extern int keyboard_bit_set P_ ((SELECT_TYPE *)); *************** *** 257,262 **** --- 289,310 ---- static struct coding_system *proc_decode_coding_system[MAXDESC]; static struct coding_system *proc_encode_coding_system[MAXDESC]; + #ifdef DATAGRAM_SOCKETS + /* Table of `client address' for datagram sockets. */ + struct sockaddr_and_len { + struct sockaddr *sa; + int len; + } datagram_address[MAXDESC]; + #define DATAGRAM_CHAN_P(chan) (datagram_address[chan].sa != 0) + #define DATAGRAM_CONN_P(proc) (datagram_address[XPROCESS (proc)->infd].sa != 0) + #else + #define DATAGRAM_CHAN_P(chan) (0) + #define DATAGRAM_CONN_P(proc) (0) + #endif + + /* Hook function to call when accepting network connection. */ + Lisp_Object Vnetwork_server_log_function; + static Lisp_Object get_process (); static void exec_sentinel (); *************** *** 367,381 **** return build_string ("finished\n"); string = Fnumber_to_string (make_number (code)); string2 = build_string (coredump ? " (core dumped)\n" : "\n"); ! return concat2 (build_string ("exited abnormally with code "), ! concat2 (string, string2)); } else if (EQ (symbol, Qfailed)) { string = Fnumber_to_string (make_number (code)); string2 = build_string ("\n"); ! return concat2 (build_string ("failed with code "), ! concat2 (string, string2)); } else return Fcopy_sequence (Fsymbol_name (symbol)); --- 415,429 ---- return build_string ("finished\n"); string = Fnumber_to_string (make_number (code)); string2 = build_string (coredump ? " (core dumped)\n" : "\n"); ! return concat3 (build_string ("exited abnormally with code "), ! string, string2); } else if (EQ (symbol, Qfailed)) { string = Fnumber_to_string (make_number (code)); string2 = build_string ("\n"); ! return concat3 (build_string ("failed with code "), ! string, string2); } else return Fcopy_sequence (Fsymbol_name (symbol)); *************** *** 946,952 **** register Lisp_Object tail, tem; Lisp_Object proc, minspace, tem1; register struct Lisp_Process *p; ! char tembuf[80]; XSETFASTINT (minspace, 1); --- 994,1000 ---- register Lisp_Object tail, tem; Lisp_Object proc, minspace, tem1; register struct Lisp_Process *p; ! char tembuf[300]; XSETFASTINT (minspace, 1); *************** *** 1032,1041 **** Findent_to (make_number (49), minspace); ! if (NETCONN_P (proc)) { ! sprintf (tembuf, "(network stream connection to %s)\n", ! XSTRING (XCAR (p->childp))->data); insert_string (tembuf); } else --- 1080,1109 ---- Findent_to (make_number (49), minspace); ! if (EQ (XPROCESS (proc)->status, Qlisten)) ! { ! Lisp_Object port = XCAR (XCDR (p->childp)); ! if (INTEGERP (port)) ! port = Fnumber_to_string (port); ! sprintf (tembuf, "(network %s server on %s)\n", ! (DATAGRAM_CONN_P(proc) ? "datagram" : "stream"), ! XSTRING (port)->data); ! insert_string (tembuf); ! } ! else if (NETCONN_P (proc)) { ! /* For a local socket, there is no host name, ! so display service instead. */ ! Lisp_Object host = XCAR (p->childp); ! if (!STRINGP (host)) ! { ! host = XCAR (XCDR (p->childp)); ! if (INTEGERP (host)) ! host = Fnumber_to_string (host); ! } ! sprintf (tembuf, "(network %s connection to %s)\n", ! (DATAGRAM_CONN_P(proc) ? "datagram" : "stream"), ! XSTRING (host)->data); insert_string (tembuf); } else *************** *** 1793,1799 **** normal connect instead. Input and output work as for subprocesses; `delete-process' closes it. ! Args are NAME BUFFER HOST SERVICE FILTER SENTINEL NON-BLOCKING. NAME is name for process. It is modified if necessary to make it unique. BUFFER is the buffer (or buffer-name) to associate with the process. Process output goes at end of that buffer, unless you specify --- 1861,1867 ---- normal connect instead. Input and output work as for subprocesses; `delete-process' closes it. ! Args are NAME BUFFER HOST SERVICE TYPE FILTER SENTINEL. NAME is name for process. It is modified if necessary to make it unique. BUFFER is the buffer (or buffer-name) to associate with the process. Process output goes at end of that buffer, unless you specify *************** *** 1801,1829 **** BUFFER may be also nil, meaning that this process is not associated with any buffer. HOST is name of the host to connect to, or its IP address. SERVICE is name of the service desired, or an integer specifying a ! port number to connect to. FILTER and SENTINEL are optional args specifying the filter and sentinel functions associated with the network stream. ! NON-BLOCKING is optional arg requesting an non-blocking connect. ! When non-nil, open-network-stream will return immediately without ! waiting for the connection to be made. Instead, the sentinel function ! will be called with second matching "open" (if successful) or ! "failed" when the connect completes. */) ! (name, buffer, host, service, filter, sentinel, non_blocking) ! Lisp_Object name, buffer, host, service, filter, sentinel, non_blocking; { Lisp_Object proc; #ifdef HAVE_GETADDRINFO ! struct addrinfo hints, *res, *lres; char *portstring, portbuf[128]; #else /* HAVE_GETADDRINFO */ - struct sockaddr_in address; - struct servent *svc_info; struct hostent *host_info_ptr, host_info; char *(addr_list[2]); IN_ADDR numeric_addr; - int port; struct _emacs_addrinfo { int ai_family; --- 1869,1926 ---- BUFFER may be also nil, meaning that this process is not associated with any buffer. HOST is name of the host to connect to, or its IP address. + If the HOST arg is nil, a server socket is opened listening on the + specified service (see below). SERVICE is name of the service desired, or an integer specifying a ! port number to connect to. ! The fifth optional arg specifies the type of connection being made. ! It is either a symbol TYPE, or a cons cell (FAMILY . TYPE). ! TYPE is one of the following symbols (default is nil): ! nil -- opens a stream connection; returns when connection is completed. ! t -- opens a non-blocking stream connection; returns immediately ! without waiting for the connection to complete. Instead, the ! sentinel function will be called with second matching "open" ! (if successful) or "failed" when the connect completes. ! datagram -- opens a connection-less datagram socket (typically UDP). ! FAMILY specifies the address family for the connection: ! nil -- Inet (IPv4) address family. ! local -- local (UNIX) address family. FILTER and SENTINEL are optional args specifying the filter and sentinel functions associated with the network stream. ! ! When the HOST is nil, a server process for the specified SERVICE and ! TYPE is created. The server process will listen for and accept ! connections from clients. When a client connection is accepted, a new ! network process is created for the connection with the following ! parameters: ! The client's process name is constructed by concatenating the server ! process' NAME and a client identification string. ! If the FILTER argument is non-nil, the client process will not get a ! separate process buffer; otherwise, the client's process buffer is a ! newly created buffer named after the server process' BUFFER name or ! process NAME concatenated with the client identification string. ! The client process' contact info is set according to the client's ! addressing information (typically an IP address and a port number). ! The connection type and the process filter and sentinel parameters ! are inherited from the server process' TYPE, FILTER and SENTINEL. ! ! Notice that the FILTER and SENTINEL args are never used directly by ! the server process. Also, the BUFFER argument is not used directly by ! the server process, but via `network-server-log-function' hook, a log ! of the accepted (and failed) connections may be recorded in the server ! process' buffer. */) ! (name, buffer, host, service, type, filter, sentinel) ! Lisp_Object name, buffer, host, service, type, filter, sentinel; { Lisp_Object proc; + Lisp_Object contact; #ifdef HAVE_GETADDRINFO ! struct addrinfo ai, hints, *res, *lres; char *portstring, portbuf[128]; #else /* HAVE_GETADDRINFO */ struct hostent *host_info_ptr, host_info; char *(addr_list[2]); IN_ADDR numeric_addr; struct _emacs_addrinfo { int ai_family; *************** *** 1834,1911 **** struct _emacs_addrinfo *ai_next; } ai, *res, *lres; #endif /* HAVE_GETADDRINFO */ int ret = 0; int xerrno = 0; int s = -1, outch, inch; ! struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5, gcpro6; int retry = 0; int count = specpdl_ptr - specpdl; int count1; int is_non_blocking = 0; ! if (!NILP (non_blocking)) { #ifndef NON_BLOCKING_CONNECT return Qnil; #else - non_blocking = Qt; /* Instead of GCPRO */ is_non_blocking = 1; #endif } #ifdef WINDOWSNT /* Ensure socket support is loaded if available. */ init_winsock (TRUE); #endif ! /* Can only GCPRO 5 variables */ ! GCPRO6 (name, buffer, host, service, sentinel, filter); CHECK_STRING (name); - CHECK_STRING (host); ! #ifdef HAVE_GETADDRINFO ! /* SERVICE can either be a string or int. ! Convert to a C string for later use by getaddrinfo. */ ! if (INTEGERP (service)) ! { ! sprintf (portbuf, "%ld", (long) XINT (service)); ! portstring = portbuf; ! } ! else { CHECK_STRING (service); ! portstring = XSTRING (service)->data; } ! #else /* HAVE_GETADDRINFO */ ! if (INTEGERP (service)) ! port = htons ((unsigned short) XINT (service)); ! else { ! CHECK_STRING (service); ! svc_info = getservbyname (XSTRING (service)->data, "tcp"); ! if (svc_info == 0) ! error ("Unknown service \"%s\"", XSTRING (service)->data); ! port = svc_info->s_port; ! } ! #endif /* HAVE_GETADDRINFO */ /* Slow down polling to every ten seconds. Some kernels have a bug which causes retrying connect to fail after a connect. Polling can interfere with gethostbyname too. */ #ifdef POLL_FOR_INPUT ! record_unwind_protect (unwind_stop_other_atimers, Qnil); ! bind_polling_period (10); #endif #ifndef TERM #ifdef HAVE_GETADDRINFO immediate_quit = 1; QUIT; memset (&hints, 0, sizeof (hints)); hints.ai_flags = 0; ! hints.ai_family = AF_UNSPEC; ! hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; ret = getaddrinfo (XSTRING (host)->data, portstring, &hints, &res); if (ret) --- 1931,2160 ---- struct _emacs_addrinfo *ai_next; } ai, *res, *lres; #endif /* HAVE_GETADDRINFO */ + struct sockaddr_in address; + #ifdef AF_LOCAL + struct sockaddr_un address_un; + #endif + struct servent *svc_info; + int port; int ret = 0; int xerrno = 0; int s = -1, outch, inch; ! struct gcpro gcpro1, gcpro2, gcpro3; int retry = 0; int count = specpdl_ptr - specpdl; int count1; int is_non_blocking = 0; + int socktype = SOCK_STREAM; + int family = -1; + + + /* Save arguments for process-contact and clone-process. */ + contact = list5 (host, service, type, filter, sentinel); ! if (CONSP (type)) ! { ! Lisp_Object tem = CAR (type); ! type = CDR (type); ! if (INTEGERP (tem)) ! family = XINT (tem); ! else ! { ! CHECK_SYMBOL (tem); ! if (NILP (tem)) ! family = AF_INET; ! #ifdef AF_LOCAL ! else if (EQ (tem, Qlocal)) ! family = AF_LOCAL; ! #endif ! } ! if (family < 0) ! error ("Unknown address family"); ! } ! ! CHECK_SYMBOL (type); ! if (NILP (host) && EQ (type, Qt)) ! type = Qnil; ! if (EQ (type, Qt)) { #ifndef NON_BLOCKING_CONNECT return Qnil; #else is_non_blocking = 1; #endif } + else if (EQ (type, Qdatagram)) + { + #ifndef DATAGRAM_SOCKETS + return Qnil; + #else + socktype = SOCK_DGRAM; + #endif + } + else if (!NILP (type)) + error ("Unknown connection type"); #ifdef WINDOWSNT /* Ensure socket support is loaded if available. */ init_winsock (TRUE); #endif ! GCPRO3 (name, buffer, contact); CHECK_STRING (name); ! /* Parse SERVICE argument. It is an integer or a string. */ ! switch (family) { + #ifdef AF_LOCAL + case AF_LOCAL: CHECK_STRING (service); ! ! bzero (&address_un, sizeof address_un); ! address_un.sun_family = AF_LOCAL; ! strncpy (address_un.sun_path, XSTRING (service)->data, sizeof address_un.sun_path); ! break; ! #endif ! ! default: ! #ifdef HAVE_GETADDRINFO ! /* We don't use getaddrinfo when opening a server socket. */ ! if (!NILP (host)) ! { ! /* SERVICE can either be a string or int. ! Convert to a C string for later use by getaddrinfo. */ ! if (INTEGERP (service)) ! { ! sprintf (portbuf, "%ld", (long) XINT (service)); ! portstring = portbuf; ! } ! else ! { ! CHECK_STRING (service); ! portstring = XSTRING (service)->data; ! } ! break; ! } ! #endif /* HAVE_GETADDRINFO */ ! if (INTEGERP (service)) ! port = htons ((unsigned short) XINT (service)); ! else ! { ! CHECK_STRING (service); ! svc_info = getservbyname (XSTRING (service)->data, ! (socktype == SOCK_DGRAM ? "udp" : "tcp")); ! if (svc_info == 0) ! report_file_error ("Unknown service", Fcons (service, Qnil)); ! port = svc_info->s_port; ! } ! break; } ! ! /* Open a server socket if no HOST is specified. */ ! if (NILP (host)) { ! struct sockaddr *addrp; ! int addrlen; ! int optval = 1; ! ! if (family < 0) ! family = AF_INET; ! s = socket (family, socktype, 0); ! if (s < 0) ! report_file_error ("Cannot create server socket", Qnil); ! ! count1 = specpdl_ptr - specpdl; ! record_unwind_protect (close_file_unwind, make_number (s)); ! ! switch (family) ! { ! case AF_LOCAL: ! /* address_un was initialized above. */ ! addrp = (struct sockaddr *)&address_un; ! addrlen = sizeof address_un; ! break; ! default: ! bzero (&address, sizeof address); ! address.sin_family = AF_INET; ! address.sin_port = port; ! address.sin_addr.s_addr = INADDR_ANY; ! addrp = (struct sockaddr *)&address; ! addrlen = sizeof address; ! ! if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval)) ! report_file_error ("Cannot set reuse option on server socket.", Qnil); ! break; ! } ! ! if (bind (s, addrp, addrlen)) ! report_file_error ("Cannot bind server socket", Qnil); ! ! #ifdef DATAGRAM_SOCKETS ! if (socktype == SOCK_DGRAM) ! { ! if (!datagram_address[s].sa || datagram_address[s].len != addrlen) ! { ! if (datagram_address[s].sa) ! xfree (datagram_address[s].sa); ! datagram_address[s].sa = (struct sockaddr *) xmalloc (addrlen); ! bzero (datagram_address[s].sa, addrlen); ! datagram_address[s].sa->sa_family = addrp->sa_family; ! datagram_address[s].len = addrlen; ! } ! } ! else ! if (datagram_address[s].sa) ! { ! xfree (datagram_address[s].sa); ! datagram_address[s].sa = 0; ! datagram_address[s].len = 0; ! } ! #endif ! ! if (socktype == SOCK_STREAM && listen (s, 5)) ! report_file_error ("Cannot listen on server socket", Qnil); ! ! /* Discard the unwind protect closing S. */ ! specpdl_ptr = specpdl + count1; + goto socket_opened; + } /* Slow down polling to every ten seconds. Some kernels have a bug which causes retrying connect to fail after a connect. Polling can interfere with gethostbyname too. */ #ifdef POLL_FOR_INPUT ! if (socktype == SOCK_STREAM) ! { ! record_unwind_protect (unwind_stop_other_atimers, Qnil); ! bind_polling_period (10); ! } #endif #ifndef TERM + #ifdef AF_LOCAL + if (family == AF_LOCAL) + { + /* Emulate HAVE_GETADDRINFO for the loop over `res' below. */ + ai.ai_family = AF_LOCAL; + ai.ai_socktype = socktype; + ai.ai_protocol = 0; + ai.ai_addr = (struct sockaddr *) &address_un; + ai.ai_addrlen = sizeof address_un; + ai.ai_next = NULL; + res = &ai; + goto open_client_socket; + } + #endif + + CHECK_STRING (host); + #ifdef HAVE_GETADDRINFO immediate_quit = 1; QUIT; memset (&hints, 0, sizeof (hints)); hints.ai_flags = 0; ! hints.ai_family = family == -1 ? AF_UNSPEC : family; ! hints.ai_socktype = socktype; hints.ai_protocol = 0; ret = getaddrinfo (XSTRING (host)->data, portstring, &hints, &res); if (ret) *************** *** 1919,1943 **** #else /* not HAVE_GETADDRINFO */ ! while (1) ! { ! #if 0 ! #ifdef TRY_AGAIN ! h_errno = 0; ! #endif ! #endif ! immediate_quit = 1; ! QUIT; ! host_info_ptr = gethostbyname (XSTRING (host)->data); ! immediate_quit = 0; ! #if 0 ! #ifdef TRY_AGAIN ! if (! (host_info_ptr == 0 && h_errno == TRY_AGAIN)) ! #endif ! #endif ! break; ! Fsleep_for (make_number (1), Qnil); ! } if (host_info_ptr == 0) /* Attempt to interpret host as numeric inet address */ --- 2168,2180 ---- #else /* not HAVE_GETADDRINFO */ ! /* gethostbyname may fail with TRY_AGAIN, but we don't honour that, ! as it may `hang' emacs for a very long time. */ ! ! immediate_quit = 1; ! QUIT; ! host_info_ptr = gethostbyname (XSTRING (host)->data); ! immediate_quit = 0; if (host_info_ptr == 0) /* Attempt to interpret host as numeric inet address */ *************** *** 1949,1955 **** host_info_ptr = &host_info; host_info.h_name = 0; host_info.h_aliases = 0; ! host_info.h_addrtype = AF_INET; #ifdef h_addr /* Older machines have only one address slot called h_addr. Newer machines have h_addr_list, but #define h_addr to --- 2186,2192 ---- host_info_ptr = &host_info; host_info.h_name = 0; host_info.h_aliases = 0; ! host_info.h_addrtype = family == -1 ? AF_INET : family; #ifdef h_addr /* Older machines have only one address slot called h_addr. Newer machines have h_addr_list, but #define h_addr to *************** *** 1970,1976 **** /* Emulate HAVE_GETADDRINFO for the loop over `res' below. */ ai.ai_family = host_info_ptr->h_addrtype; ! ai.ai_socktype = SOCK_STREAM; ai.ai_protocol = 0; ai.ai_addr = (struct sockaddr *) &address; ai.ai_addrlen = sizeof address; --- 2207,2213 ---- /* Emulate HAVE_GETADDRINFO for the loop over `res' below. */ ai.ai_family = host_info_ptr->h_addrtype; ! ai.ai_socktype = socktype; ai.ai_protocol = 0; ai.ai_addr = (struct sockaddr *) &address; ai.ai_addrlen = sizeof address; *************** *** 1978,1983 **** --- 2215,2221 ---- res = &ai; #endif /* not HAVE_GETADDRINFO */ + open_client_socket: /* Do this in case we never enter the for-loop below. */ count1 = specpdl_ptr - specpdl; s = -1; *************** *** 1991,1996 **** --- 2229,2255 ---- continue; } + #ifdef DATAGRAM_SOCKETS + if (socktype == SOCK_DGRAM) + { + if (!datagram_address[s].sa || datagram_address[s].len != lres->ai_addrlen) + { + if (datagram_address[s].sa) + xfree (datagram_address[s].sa); + datagram_address[s].sa = (struct sockaddr *) xmalloc (lres->ai_addrlen); + } + bcopy (lres->ai_addr, datagram_address[s].sa, lres->ai_addrlen); + datagram_address[s].len = lres->ai_addrlen; + #ifdef HAVE_GETADDRINFO + #ifdef AF_LOCAL + if (family != AF_LOCAL) + #endif + freeaddrinfo (res); + #endif + goto socket_opened; + } + #endif + #ifdef NON_BLOCKING_CONNECT if (is_non_blocking) { *************** *** 2111,2117 **** report_file_error ("connection failed", Fcons (host, Fcons (name, Qnil))); } ! immediate_quit = 0; /* Discard the unwind protect, if any. */ --- 2370,2385 ---- report_file_error ("connection failed", Fcons (host, Fcons (name, Qnil))); } ! ! #ifdef DATAGRAM_SOCKETS ! if (datagram_address[s].sa) ! { ! xfree (datagram_address[s].sa); ! datagram_address[s].sa = 0; ! datagram_address[s].len = 0; ! } ! #endif ! immediate_quit = 0; /* Discard the unwind protect, if any. */ *************** *** 2132,2137 **** --- 2400,2407 ---- send_command (s, C_DUMB, 1, 0); #endif /* TERM */ + socket_opened: + inch = s; outch = s; *************** *** 2149,2155 **** #endif #endif ! XPROCESS (proc)->childp = Fcons (host, Fcons (service, Qnil)); XPROCESS (proc)->command_channel_p = Qnil; XPROCESS (proc)->buffer = buffer; XPROCESS (proc)->sentinel = sentinel; --- 2419,2425 ---- #endif #endif ! XPROCESS (proc)->childp = contact; XPROCESS (proc)->command_channel_p = Qnil; XPROCESS (proc)->buffer = buffer; XPROCESS (proc)->sentinel = sentinel; *************** *** 2158,2167 **** XPROCESS (proc)->pid = Qnil; XSETINT (XPROCESS (proc)->infd, inch); XSETINT (XPROCESS (proc)->outfd, outch); ! XPROCESS (proc)->status = Qrun; #ifdef NON_BLOCKING_CONNECT ! if (!NILP (non_blocking)) { /* We may get here if connect did succeed immediately. However, in that case, we still need to signal this like a non-blocking --- 2428,2438 ---- XPROCESS (proc)->pid = Qnil; XSETINT (XPROCESS (proc)->infd, inch); XSETINT (XPROCESS (proc)->outfd, outch); ! XPROCESS (proc)->status ! = (NILP (host) && !DATAGRAM_CHAN_P (inch)) ? Qlisten : Qrun; #ifdef NON_BLOCKING_CONNECT ! if (EQ (type, Qt)) { /* We may get here if connect did succeed immediately. However, in that case, we still need to signal this like a non-blocking *************** *** 2175,2181 **** } else #endif ! if (!EQ (XPROCESS (proc)->filter, Qt)) { FD_SET (inch, &input_wait_mask); FD_SET (inch, &non_keyboard_wait_mask); --- 2446,2453 ---- } else #endif ! if (NILP (host) || ! !EQ (XPROCESS (proc)->filter, Qt)) { FD_SET (inch, &input_wait_mask); FD_SET (inch, &non_keyboard_wait_mask); *************** *** 2343,2348 **** --- 2615,2701 ---- #endif } \f + DEFUN ("process-datagram-address", Fprocess_datagram_address, Sprocess_datagram_address, + 1, 3, 0, + doc: /* Get the current datagram address associated with PROCESS. + If optional arg NEW-ADDRESS is non-nil, set that as a new datagram + address for PROCESS. The old datagram address is still returned, + unless the third argument IGNORE-OLD is non-nil. */) + (process, new_address, ignore_old) + Lisp_Object process, new_address, ignore_old; + { + Lisp_Object address; + int channel, i, len; + unsigned char *cp; + register struct Lisp_Vector *p; + struct sockaddr *sa; + + CHECK_PROCESS (process); + + #ifndef DATAGRAM_SOCKETS + return Qnil; + #else + if (!DATAGRAM_CONN_P (process)) + return Qnil; + + channel = XPROCESS (process)->infd; + sa = datagram_address[channel].sa; + if (sa->sa_family == AF_INET) + len = 6; + else + len = datagram_address[channel].len - sizeof(sa->sa_family) + 1; + + if (!NILP (new_address)) + if (!VECTORP (new_address) || + XVECTOR (new_address)->size != len || + XFASTINT (XVECTOR (new_address)->contents[0]) != sa->sa_family) + wrong_type_argument (Qvectorp, new_address); + + if (!NILP (ignore_old)) + address = Qnil; + else + { + address = Fmake_vector (make_number (len), Qnil); + p = XVECTOR (address); + i = 0; + p->contents[i++] = make_number (sa->sa_family); + if (sa->sa_family == AF_INET) + { + struct sockaddr_in *sin = (struct sockaddr_in *) sa; + p->contents[i++] = make_number (ntohs (sin->sin_port)); + cp = (unsigned char *)&sin->sin_addr; + } + else + cp = (unsigned char *)datagram_address[channel].sa + sizeof (sa->sa_family); + while (i < len) + p->contents[i++] = make_number (*cp++); + } + + if (!NILP (new_address)) + { + p = XVECTOR (new_address); + len = p->size; + i = 0; + if (sa->sa_family == AF_INET) + { + Lisp_Object port = p->contents[++i]; + struct sockaddr_in *sin = (struct sockaddr_in *) sa; + sin->sin_port = htons (XFASTINT (port)); + cp = (unsigned char *)&sin->sin_addr; + } + else + cp = (unsigned char *)datagram_address[channel].sa + sizeof (sa->sa_family); + while (++i < len) + /* result is undefined if vector contains something + other than integers. But then it's messed up anyway. */ + *cp++ = XFASTINT (p->contents[i]) & 0xff; + } + + return address; + #endif + } + + \f DEFUN ("accept-process-output", Faccept_process_output, Saccept_process_output, 0, 3, 0, doc: /* Allow any pending output from subprocesses to be read by Emacs. *************** *** 2411,2416 **** --- 2764,2956 ---- ? Qt : Qnil); } + /* Accept a connection for server process SERVER on CHANNEL. */ + + static int connect_counter = 0; + + static void + server_accept_connection (server, channel) + Lisp_Object server; + int channel; + { + Lisp_Object proc, caller, name, buffer; + Lisp_Object host, service; + int s; + union u_sockaddr { + struct sockaddr sa; + struct sockaddr_in in; + #ifdef AF_LOCAL + struct sockaddr_un un; + #endif + } saddr; + int len = sizeof saddr; + + /* TODO: Add GCPRO if necessary. */ + + s = accept (channel, &saddr.sa, &len); + + if (s < 0) + { + int code = errno; + + if (code == EAGAIN) + return; + #ifdef EWOULDBLOCK + if (code == EWOULDBLOCK) + return; + #endif + + if (!NILP (Vnetwork_server_log_function)) + apply1 (Vnetwork_server_log_function, + list3 (server, Qnil, + concat3 (build_string ("accept failed with code"), + Fnumber_to_string (make_number (code)), + build_string ("\n")))); + return; + } + + connect_counter++; + + /* Setup a new process to handle the connection. */ + + /* Generate a unique identification of the caller, and build contact + information for this process. */ + host = Qt; + service = Qnil; + switch (saddr.sa.sa_family) + { + case AF_INET: + { + Lisp_Object args[5]; + unsigned char *ip = (unsigned char *)&saddr.in.sin_addr.s_addr; + args[0] = build_string ("%d.%d.%d.%d"); + args[1] = make_number (*ip++); + args[2] = make_number (*ip++); + args[3] = make_number (*ip++); + args[4] = make_number (*ip++); + host = Fformat (5, args); + service = make_number (ntohs (saddr.in.sin_port)); + + args[0] = build_string (" <%s:%d>"); + args[1] = host; + args[2] = service; + caller = Fformat (3, args); + } + break; + + #ifdef AF_LOCAL + case AF_LOCAL: + #endif + default: + caller = Fnumber_to_string (make_number (connect_counter)); + caller = concat3 (build_string (" <*"), caller, build_string ("*>")); + break; + } + + /* Create a new buffer name for this process if it doesn't have a + filter. The new buffer name is based on the buffer name or + process name of the server process concatenated with the caller + identification. */ + + if (!NILP (XPROCESS (server)->filter)) + buffer = Qnil; + else + { + buffer = XPROCESS (server)->buffer; + if (!NILP (buffer)) + buffer = Fbuffer_name (buffer); + else + buffer = XPROCESS (server)->name; + if (!NILP (buffer)) + { + buffer = concat2 (buffer, caller); + buffer = Fget_buffer_create (buffer); + } + } + + /* Generate a unique name for the new server process. Combine the + server process name with the caller identification. */ + + name = concat2 (XPROCESS (server)->name, caller); + proc = make_process (name); + + chan_process[s] = proc; + + #ifdef O_NONBLOCK + fcntl (s, F_SETFL, O_NONBLOCK); + #else + #ifdef O_NDELAY + fcntl (s, F_SETFL, O_NDELAY); + #endif + #endif + + /* Build new contact information for this setup. */ + XPROCESS (proc)->childp = Fcopy_sequence (XPROCESS (server)->childp); + XSETCAR (XPROCESS (proc)->childp, host); + if (!NILP (service)) + XSETCAR (CDR (XPROCESS (proc)->childp), service); + + XPROCESS (proc)->command_channel_p = Qnil; + XPROCESS (proc)->buffer = buffer; + XPROCESS (proc)->sentinel = XPROCESS (server)->sentinel; + XPROCESS (proc)->filter = XPROCESS (server)->filter; + XPROCESS (proc)->command = Qnil; + XPROCESS (proc)->pid = Qnil; + XSETINT (XPROCESS (proc)->infd, s); + XSETINT (XPROCESS (proc)->outfd, s); + XPROCESS (proc)->status = Qrun; + + if (!EQ (XPROCESS (proc)->filter, Qt)) + { + FD_SET (s, &input_wait_mask); + FD_SET (s, &non_keyboard_wait_mask); + } + + if (s > max_process_desc) + max_process_desc = s; + + /* Setup coding system for new process based on server process. + This seems to be the proper thing to do, as the coding system + of the new process should reflect the settings at the time the + server socket was opened; not the current settings. */ + + XPROCESS (proc)->decode_coding_system = XPROCESS (server)->decode_coding_system; + XPROCESS (proc)->encode_coding_system = XPROCESS (server)->encode_coding_system; + + if (!proc_decode_coding_system[s]) + proc_decode_coding_system[s] + = (struct coding_system *) xmalloc (sizeof (struct coding_system)); + setup_coding_system (XPROCESS (proc)->decode_coding_system, + proc_decode_coding_system[s]); + if (!proc_encode_coding_system[s]) + proc_encode_coding_system[s] + = (struct coding_system *) xmalloc (sizeof (struct coding_system)); + setup_coding_system (XPROCESS (proc)->encode_coding_system, + proc_encode_coding_system[s]); + + XPROCESS (proc)->decoding_buf = make_uninit_string (0); + XPROCESS (proc)->decoding_carryover = make_number (0); + XPROCESS (proc)->encoding_buf = make_uninit_string (0); + XPROCESS (proc)->encoding_carryover = make_number (0); + + XPROCESS (proc)->inherit_coding_system_flag + = (NILP (buffer) + ? Qnil : XPROCESS (server)->inherit_coding_system_flag); + + if (!NILP (Vnetwork_server_log_function)) + apply1 (Vnetwork_server_log_function, + list3 (server, proc, + concat3 (build_string ("accept from "), + (STRINGP (host) ? host : build_string ("-")), + build_string ("\n")))); + + if (XPROCESS (proc)->sentinel) + exec_sentinel (proc, + concat3 (build_string ("open from "), + (STRINGP (host) ? host : build_string ("-")), + build_string ("\n"))); + } + /* This variable is different from waiting_for_input in keyboard.c. It is used to communicate to a lisp process-filter/sentinel (via the function Fwaiting_for_user_input_p below) whether emacs was waiting *************** *** 2909,2914 **** --- 3449,3462 ---- if (NILP (proc)) continue; + /* If this is a server stream socket, accept connection. */ + if (EQ (XPROCESS (proc)->status, Qlisten) + && !DATAGRAM_CHAN_P (channel)) + { + server_accept_connection (proc, channel); + continue; + } + /* Read data from the process, starting with our buffered-ahead character if we have one. */ *************** *** 2983,2989 **** { struct Lisp_Process *p; struct sockaddr pname; ! socklen_t pnamelen = sizeof(pname); FD_CLR (channel, &connect_wait_mask); if (--num_pending_connects < 0) --- 3531,3537 ---- { struct Lisp_Process *p; struct sockaddr pname; ! int pnamelen = sizeof(pname); FD_CLR (channel, &connect_wait_mask); if (--num_pending_connects < 0) *************** *** 2999,3005 **** /* getsockopt(,,SO_ERROR,,) is said to hang on some systems. So only use it on systems where it is known to work. */ { ! socklen_t xlen = sizeof(xerrno); if (getsockopt(channel, SOL_SOCKET, SO_ERROR, &xerrno, &xlen)) xerrno = errno; } --- 3547,3553 ---- /* getsockopt(,,SO_ERROR,,) is said to hang on some systems. So only use it on systems where it is known to work. */ { ! int xlen = sizeof(xerrno); if (getsockopt(channel, SOL_SOCKET, SO_ERROR, &xerrno, &xlen)) xerrno = errno; } *************** *** 3106,3111 **** --- 3654,3660 ---- register int opoint; struct coding_system *coding = proc_decode_coding_system[channel]; int carryover = XINT (p->decoding_carryover); + int readmax = 1024; #ifdef VMS VMS_PROC_STUFF *vs, *get_vms_process_pointer(); *************** *** 3137,3154 **** bcopy (vs->inputBuffer, chars + carryover, nbytes); } #else /* not VMS */ ! chars = (char *) alloca (carryover + 1024); if (carryover) /* See the comment above. */ bcopy (XSTRING (p->decoding_buf)->data, chars, carryover); if (proc_buffered_char[channel] < 0) ! nbytes = emacs_read (channel, chars + carryover, 1024 - carryover); else { chars[carryover] = proc_buffered_char[channel]; proc_buffered_char[channel] = -1; ! nbytes = emacs_read (channel, chars + carryover + 1, 1023 - carryover); if (nbytes < 0) nbytes = 1; else --- 3686,3724 ---- bcopy (vs->inputBuffer, chars + carryover, nbytes); } #else /* not VMS */ ! ! #ifdef DATAGRAM_SOCKETS ! /* A datagram is one packet; allow at least 1500+ bytes of data ! corresponding to the typical Ethernet frame size. */ ! if (DATAGRAM_CHAN_P (channel)) ! { ! /* carryover = 0; */ /* Does carryover make sense for datagrams? */ ! readmax += 1024; ! } ! #endif ! ! chars = (char *) alloca (carryover + readmax); if (carryover) /* See the comment above. */ bcopy (XSTRING (p->decoding_buf)->data, chars, carryover); + #ifdef DATAGRAM_SOCKETS + /* We have a working select, so proc_buffered_char is always -1. */ + if (DATAGRAM_CHAN_P (channel)) + { + int len = datagram_address[channel].len; + nbytes = recvfrom (channel, chars + carryover, readmax - carryover, + 0, datagram_address[channel].sa, &len); + } + else + #endif if (proc_buffered_char[channel] < 0) ! nbytes = emacs_read (channel, chars + carryover, readmax - carryover); else { chars[carryover] = proc_buffered_char[channel]; proc_buffered_char[channel] = -1; ! nbytes = emacs_read (channel, chars + carryover + 1, readmax - 1 - carryover); if (nbytes < 0) nbytes = 1; else *************** *** 3614,3622 **** /* Send this batch, using one or more write calls. */ while (this > 0) { old_sigpipe = (SIGTYPE (*) ()) signal (SIGPIPE, send_process_trap); ! rv = emacs_write (XINT (XPROCESS (proc)->outfd), ! (char *) buf, this); signal (SIGPIPE, old_sigpipe); if (rv < 0) --- 4184,4203 ---- /* Send this batch, using one or more write calls. */ while (this > 0) { + int outfd = XINT (XPROCESS (proc)->outfd); old_sigpipe = (SIGTYPE (*) ()) signal (SIGPIPE, send_process_trap); ! #ifdef DATAGRAM_SOCKETS ! if (DATAGRAM_CHAN_P (outfd)) ! { ! rv = sendto (outfd, (char *) buf, this, ! 0, datagram_address[outfd].sa, ! datagram_address[outfd].len); ! if (rv < 0 && errno == EMSGSIZE) ! report_file_error ("sending datagram", Fcons (proc, Qnil)); ! } ! else ! #endif ! rv = emacs_write (outfd, (char *) buf, this); signal (SIGPIPE, old_sigpipe); if (rv < 0) *************** *** 4235,4240 **** --- 4816,4824 ---- Lisp_Object proc; struct coding_system *coding; + if (DATAGRAM_CONN_P (process)) + return process; + proc = get_process (process); coding = proc_encode_coding_system[XINT (XPROCESS (proc)->outfd)]; *************** *** 4829,4834 **** --- 5413,5421 ---- } bzero (proc_decode_coding_system, sizeof proc_decode_coding_system); bzero (proc_encode_coding_system, sizeof proc_encode_coding_system); + #ifdef DATAGRAM_SOCKETS + bzero (datagram_address, sizeof datagram_address); + #endif } void *************** *** 4857,4862 **** --- 5444,5455 ---- staticpro (&Qconnect); Qfailed = intern ("failed"); staticpro (&Qfailed); + Qlisten = intern ("listen"); + staticpro (&Qlisten); + Qlocal = intern ("local"); + staticpro (&Qlocal); + Qdatagram = intern ("datagram"); + staticpro (&Qdatagram); Qlast_nonmenu_event = intern ("last-nonmenu-event"); staticpro (&Qlast_nonmenu_event); *************** *** 4877,4882 **** --- 5470,5481 ---- The value takes effect when `start-process' is called. */); Vprocess_connection_type = Qt; + DEFVAR_LISP ("network-server-log-function", &Vnetwork_server_log_function, + doc: /* Function called when accepting a network connecting. + Arguments are SERVER, PROCESS, and MESSAGE, where SERVER is the server process, + PROCESS is the new process for the connection, and MESSAGE is a string. */); + Vnetwork_server_log_function = Qnil; + defsubr (&Sprocessp); defsubr (&Sget_process); defsubr (&Sget_buffer_process); *************** *** 4906,4911 **** --- 5505,5511 ---- defsubr (&Sopen_network_stream); #endif /* HAVE_SOCKETS */ defsubr (&Saccept_process_output); + defsubr (&Sprocess_datagram_address); defsubr (&Sprocess_send_region); defsubr (&Sprocess_send_string); defsubr (&Sinterrupt_process); -- Kim F. Storm <storm@cua.dk> http://www.cua.dk _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: New patch for server sockets and datagram (UDP) support. 2002-03-07 0:08 ` New patch for server sockets and datagram (UDP) support Kim F. Storm @ 2002-03-07 10:56 ` Kim F. Storm 2002-03-07 11:39 ` Alex Schroeder 2002-03-07 15:18 ` New " Helmut Eller 2002-03-07 12:54 ` Mario Lang 2002-03-08 9:09 ` Richard Stallman 2 siblings, 2 replies; 47+ messages in thread From: Kim F. Storm @ 2002-03-07 10:56 UTC (permalink / raw) Cc: helmut I wrote: > The following patch adds server socket support via open-network-stream. > If the HOST is nil, a server socket for SERVICE is opened in listening > state. > I forgot to say, that the code is working nicely, but the patch is still "work in progress" to let you know how this is progressing and get your comments on the direction it is taking. > I have removed the NON-BLOCKING argument, and instead added a new TYPE > argument, which specifies the type of connection (blocking connect, > non-blocking connect, or datagram), and may optionally specify the > address family (inet or local [aka. unix]). Actually, I would like to get rid of this extra TYPE argument all together by modifying the HOST and SERVICE arguments in a backwards compatible way (when calling open-network-stream): When the HOST and SERVICE are specified as "simple" arguments (strings or integer port number), a TCP/IP connection is created using a blocking connect. To get a non-blocking TCP connect, or a datagram socket, the SERVICE argument is a cons (TYPE . SERVICE), where TYPE is t for a non-blocking connect, and `datagram' for a datagram socket. Likewise, to use another address/protocol family than IP, the HOST argument is a cons (FAMILY . HOST), where FAMILY is `local' for a local (aka UNIX) socket. Also, I think that HOST should be t to get a server socket rather than nil. This is because, for a local (UNIX) socket, there is no hostname, so the hostname would logically be nil for a client. However, there is one problem with this approach: The value returned from `process-contact' for a network stream is specified to be (HOST SERVICE) -- which my patch modifies to (HOST SERVICE TYPE FILTER SENTINEL). The only use I've found for process-contact is in the clone-process function where it is obviously assumed to return the parameters originally given to open-network-stream (thus the change). Now, if I want to get rid of TYPE by modifing the possible arguments for HOST and SERVICE, this would obviously have to be reflected in the value returned by process-contact as well. So if code currently exists which expects (car (process-contact p)) or (cadr (process-contact p)) to return the hostname or service, that code will fail after the change. But I haven't found any such code though -- do you know of any code using process-contact? My suggestion is to change `process-contact' to do the following: For a network conntecton, the value is a list (HOST SERVICE FILTER SENTINEL) with the same format as the corresponding arguments to `open-network-stream'. The current doc string says: For a net connection, the value is a cons cell of the form (HOST SERVICE). Below you can see the difference between using the TYPE argument and encoding the information in the HOST and SERVICE args (and using HOST=t for a server socket). > > To open a TCP server socket for "telnet", where client > processes have no buffer, do > > (open-network-stream "telnetd" nil nil "telnet" nil > telnetd-filter telnetd-sentinel) (open-network-stream "telnetd" nil t "telnet" telnetd-filter telnetd-sentinel) > > To open a UDP (datagram) server socket for "dns", do > > (open-network-stream "dns" nil nil "dns" 'datagram > dns-filter dns-sentinel) (open-network-stream "dns" nil t '(datagram . "dns") dns-filter dns-sentinel) > > To open a LOCAL (UNIX) server socket for "/tmp/xyz", where > client processes do have a buffer, do > > (open-network-stream "xyz" "XYZ" nil "/tmp/xyz" '(local) > xyz-filter xyz-sentinel) (open-network-stream "xyz" "XYZ" '(local . t) "/tmp/xyz" xyz-filter xyz-sentinel) To connect to each of these services, specify the hostname (or nil for a local socket) instead of t in the third argument (and modify other parameters according to the desired use), e.g. ;; non-blocking connect, use buffer, no filter (open-network-stream "telnet" "TELNET" "hostname" '(t . "telnet") nil telnet-client-sentinel) ;; datagram "connect", no buffer, use filter (open-network-stream "dns" nil "hostname" '(datagram . "dns") dns-filter dns-sentinel) ;; local socket, non-blocking connect, use buffer, no filter (open-network-stream "xyz" "XYZ" '(local) '(t . "/tmp/xyz") nil xyz-sentinel) What do you think? -- Kim F. Storm <storm@cua.dk> http://www.cua.dk _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: New patch for server sockets and datagram (UDP) support. 2002-03-07 10:56 ` Kim F. Storm @ 2002-03-07 11:39 ` Alex Schroeder 2002-03-07 12:39 ` Kim F. Storm 2002-03-07 15:18 ` New " Helmut Eller 1 sibling, 1 reply; 47+ messages in thread From: Alex Schroeder @ 2002-03-07 11:39 UTC (permalink / raw) storm@cua.dk (Kim F. Storm) writes: > Actually, I would like to get rid of this extra TYPE argument > all together by modifying the HOST and SERVICE arguments in a > backwards compatible way (when calling open-network-stream): Can you explain the benefit of such a change? AFAICT, you described the changes, discussed a potential problem, but there seemed to be no advantages. Personally, I like it when information is transmitted via arguments instead of datastructures, ie. I prefer a TYPE argument to encoding information in a cons cell (TYPE . SERVICE). What about the comments by Mario and Helmut. I think Mario wants to implement DCC for an IRC client, and I think he needs to be able to *not* specify a port. Here's the quote from http://www.irchelp.org/irchelp/rfc/ctcpspec.html: The initial socket for a DCC connection is created by the side that initiates (Offers) the connection. This socket should be a TCP socket bound to INADDR_ANY, listening for connections. Other than that, I note that currently we have (open-network-stream NAME BUFFER HOST SERVICE) and later we will have (open-network-stream NAME BUFFER HOST SERVICE TYPE FILTER SENTINEL) or something similar... Since the function is the same, we cannot test using boundp but will habe to use some condition-case in order to handle XEmacs or older versions of Emacs, correct? Perhaps splitting these things up into more functions might be easier to understand and document. One lisp function for one type of functionality, instead of big black boxes to do it all. If we still want a blackbox, we could write it later using the two or three existing functions. Alex. -- http://www.emacswiki.org/ _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: New patch for server sockets and datagram (UDP) support. 2002-03-07 11:39 ` Alex Schroeder @ 2002-03-07 12:39 ` Kim F. Storm 2002-03-07 14:51 ` Alex Schroeder 2002-03-08 21:06 ` Richard Stallman 0 siblings, 2 replies; 47+ messages in thread From: Kim F. Storm @ 2002-03-07 12:39 UTC (permalink / raw) Cc: emacs-devel Alex Schroeder <alex@gnu.org> writes: > storm@cua.dk (Kim F. Storm) writes: > > > Actually, I would like to get rid of this extra TYPE argument > > all together by modifying the HOST and SERVICE arguments in a > > backwards compatible way (when calling open-network-stream): > > Can you explain the benefit of such a change? AFAICT, you described > the changes, discussed a potential problem, but there seemed to be no > advantages. Personally, I like it when information is transmitted via > arguments instead of datastructures, ie. I prefer a TYPE argument to > encoding information in a cons cell (TYPE . SERVICE). Yes, that is the other route we can take - and then also split the current TYPE argument into TYPE and FAMILY. I have absolutely no objections to doing that -- actually, that it what I originally wanted to do, but then I was told that open-network-stream has too many arguments already... So expect to see 8 arguments in the next version :-) > > What about the comments by Mario and Helmut. I think Mario wants to > implement DCC for an IRC client, and I think he needs to be able to > *not* specify a port. Here's the quote from > http://www.irchelp.org/irchelp/rfc/ctcpspec.html: > > The initial socket for a DCC connection is created by the side > that initiates (Offers) the connection. This socket should be > a TCP socket bound to INADDR_ANY, listening for connections. Ok, I'll add that. The selected port number will be available in (cadr (process-contact ddc)) (i.e. the SERVICE element). > > Other than that, I note that currently we have (open-network-stream > NAME BUFFER HOST SERVICE) and later we will have (open-network-stream > NAME BUFFER HOST SERVICE TYPE FILTER SENTINEL) or something > similar... Since the function is the same, we cannot test using > boundp but will habe to use some condition-case in order to handle > XEmacs or older versions of Emacs, correct? Although not very obvious, you can use (boundp 'network-server-log-function) to test for server socket support. And you can use (fboundp 'process-datagram-address) to test for datagram support [if I change the code so that this function is only available when DATAGRAM_SOCKETS is defined.] Alternatively, using something like (featurep 'server-sockets) (featurep 'datagram-sockets) would be a more generic approach. > > Perhaps splitting these things up into more functions might be easier > to understand and document. One lisp function for one type of > functionality, instead of big black boxes to do it all. If we still > want a blackbox, we could write it later using the two or three > existing functions. > I agree that the combined functionality of open-network-stream is a large "black box". But why is that a problem? Maybe it isn't very pretty (it wasn't that before either), but it shares a fair amount of code and functionality between the various uses of the function. IMO, We should do what you suggest, but the other way round: We could rename the C-level function to, say, open-network-connection and write lisp-level wrappers (in simple.el) around it like open-network-stream, open-network-stream-nowait, open-network-stream-server, open-local-stream-nowait, open-local-stream, open-local-stream-server, open-datagram-server, open-datagram-client, etc. etc. Then you don't have 10 different C functions all doing variations of the same thing, but still the user will have all the `specific-purpose' functions handy. -- Kim F. Storm <storm@cua.dk> http://www.cua.dk _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: New patch for server sockets and datagram (UDP) support. 2002-03-07 12:39 ` Kim F. Storm @ 2002-03-07 14:51 ` Alex Schroeder 2002-03-08 21:06 ` Richard Stallman 1 sibling, 0 replies; 47+ messages in thread From: Alex Schroeder @ 2002-03-07 14:51 UTC (permalink / raw) Cc: emacs-devel storm@cua.dk (Kim F. Storm) writes: > Yes, that is the other route we can take - and then also split the > current TYPE argument into TYPE and FAMILY. I have absolutely no > objections to doing that -- actually, that it what I originally wanted > to do, but then I was told that open-network-stream has too > many arguments already... Haha, that was a cheap trick, then. :) Anyway, if you provide a list of special purpose functions as discussed at the end, then this is a non-issue anyway. Thanks. >> The initial socket for a DCC connection is created by the side >> that initiates (Offers) the connection. This socket should be >> a TCP socket bound to INADDR_ANY, listening for connections. > > Ok, I'll add that. The selected port number will be available in > (cadr (process-contact ddc)) (i.e. the SERVICE element). Thanks for considering this. > Alternatively, using something like > (featurep 'server-sockets) > (featurep 'datagram-sockets) > would be a more generic approach. That would be nice, or combined with the functions you mention next, this seems good. > We could rename the C-level function to, say, open-network-connection > and write lisp-level wrappers (in simple.el) around it like > open-network-stream, open-network-stream-nowait, open-network-stream-server, > open-local-stream-nowait, open-local-stream, open-local-stream-server, > open-datagram-server, open-datagram-client, > etc. etc. > > Then you don't have 10 different C functions all doing variations of > the same thing, but still the user will have all the `specific-purpose' > functions handy. That sounds ok to me because I only care about the Lisp level. Other people shall judge maintenace issues or code issues on the C level. Thanks, Kim. Alex. -- http://www.emacswiki.org/ _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: New patch for server sockets and datagram (UDP) support. 2002-03-07 12:39 ` Kim F. Storm 2002-03-07 14:51 ` Alex Schroeder @ 2002-03-08 21:06 ` Richard Stallman 2002-03-13 15:56 ` Kim F. Storm 1 sibling, 1 reply; 47+ messages in thread From: Richard Stallman @ 2002-03-08 21:06 UTC (permalink / raw) Cc: alex, emacs-devel We could rename the C-level function to, say, open-network-connection and write lisp-level wrappers (in simple.el) around it like open-network-stream, open-network-stream-nowait, open-network-stream-server, open-local-stream-nowait, open-local-stream, open-local-stream-server, open-datagram-server, open-datagram-client, etc. etc. That is taking things to an absurd extreme. We certainly do not want to define all these as separate functions--it would be cumbersome. However, to have two or three convenience or compatibility functions for a few larger categories of cases could be a good idea. _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: New patch for server sockets and datagram (UDP) support. 2002-03-08 21:06 ` Richard Stallman @ 2002-03-13 15:56 ` Kim F. Storm 2002-03-13 23:19 ` Final(?) " Kim F. Storm 0 siblings, 1 reply; 47+ messages in thread From: Kim F. Storm @ 2002-03-13 15:56 UTC (permalink / raw) Richard Stallman <rms@gnu.org> writes: > We could rename the C-level function to, say, open-network-connection > and write lisp-level wrappers (in simple.el) around it like > open-network-stream, open-network-stream-nowait, open-network-stream-server, > open-local-stream-nowait, open-local-stream, open-local-stream-server, > open-datagram-server, open-datagram-client, > etc. etc. > > That is taking things to an absurd extreme. We certainly do not want > to define all these as separate functions--it would be cumbersome. I agree. > > However, to have two or three convenience or compatibility functions > for a few larger categories of cases could be a good idea. In the patch I'll send out later today, only three such functions are defined: open-network-stream open-network-stream-nowait open-network-stream-server -- Kim F. Storm <storm@cua.dk> http://www.cua.dk _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Final(?) patch for server sockets and datagram (UDP) support. 2002-03-13 15:56 ` Kim F. Storm @ 2002-03-13 23:19 ` Kim F. Storm 2002-03-14 0:50 ` Al Petrofsky 2002-03-17 22:02 ` I have installed the " Kim F. Storm 0 siblings, 2 replies; 47+ messages in thread From: Kim F. Storm @ 2002-03-13 23:19 UTC (permalink / raw) Cc: helmut The following (large) patch encompasses the majority of the requirements raised by people for enhancements to the networking support in emacs: server sockets, datagrams, local (unix) sockets. I still need to add checks for sendto, recvfrom, and getsockname to configure.in/configure. I'll do that eventually, but until then, the patch explicitly defines these for GNU_LINUX. Updates to the Elisp manual are also missing (of course :-) Index: etc/NEWS =================================================================== RCS file: /cvs/emacs/etc/NEWS,v retrieving revision 1.624 diff -c -r1.624 NEWS *** etc/NEWS 13 Mar 2002 09:34:06 -0000 1.624 --- etc/NEWS 13 Mar 2002 23:13:57 -0000 *************** *** 654,671 **** change group you start for any given buffer should be the last one finished. ! ** You can now use non-blocking connect to open network streams. ! The function open-network-stream has a new optional 7th argument. ! If non-nil, that function will initiate a non-blocking connect and ! return immediately before the connection is established. ! ! It returns nil if the system does not support non-blocking connects; ! the caller may then make a normal (blocking) open-network-stream. ! ! The filter and sentinel functions can now be specified as arguments ! to open-network-stream. When the non-blocking connect completes, the ! sentinel is called with the status matching "open" or "failed". ** New function substring-no-properties. --- 654,705 ---- change group you start for any given buffer should be the last one finished. ! ** Enhanced networking support. ! *** There is a new `make-network-process' function which supports ! opening of stream and datagram connections to a server, as well as ! create a stream or datagram server inside emacs. ! ! - A server is started using :server t arg. ! - Datagram connection is selected using :datagram t arg. ! - A server can open on a random port using :service t arg. ! - Local sockets are supported using :family 'local arg. ! - Non-blocking connect is supported using :nowait t arg. ! ! *** Original open-network-stream is now emulated using make-network-process. ! ! *** New function open-network-stream-nowait. ! ! This function initiates a non-blocking connect and returns immediately ! before the connection is established. The filter and sentinel ! functions can be specified as arguments to open-network-stream-nowait. ! When the non-blocking connect completes, the sentinel is called with ! the status matching "open" or "failed". ! ! *** New function open-network-stream-server. ! ! *** New functions process-datagram-address and set-process-datagram-address. ! ! *** By default, the function process-contact still returns (HOST SERVICE) ! for a network process. Using the new optional KEY arg, the complete list ! of network process properties or a specific property can be selected. ! ! Using :local and :remote as the KEY, the address of the local or ! remote end-point is returned. An Inet address is represented as a 5 ! element vector, where the first 4 elements contain the IP address and ! the fifth is the port number. ! ! *** Network processes can now be stopped and restarted with ! `stop-process' and `continue-process'. For a server process, no ! connections are accepted in the stopped state. For a client process, ! no input is received in the stopped state. ! ! *** Function list-processes now has an optional argument; if non-nil, ! only the processes whose query-on-exit flag is set are listed. ! ! *** New set-process-query-on-exit-flag and process-query-on-exit-flag ! functions. The existing process-kill-without-query function is still ! supported, but new code should use the new functions. ** New function substring-no-properties. Index: src/ChangeLog =================================================================== RCS file: /cvs/emacs/src/ChangeLog,v retrieving revision 1.2520 diff -c -r1.2520 ChangeLog *** src/ChangeLog 13 Mar 2002 17:07:45 -0000 1.2520 --- src/ChangeLog 13 Mar 2002 23:13:58 -0000 *************** *** 1,3 **** --- 1,60 ---- + 2002-03-13 Kim F. Storm <storm@cua.dk> + + The following changes adds support for network server processes, + datagram connections, and local (unix) sockets. + + * process.h (struct Lisp_Process): New member log. + Doc fix: Member command used to indicate stopped network process. + Doc fix: Member childp contains plist for network process. + Doc fix: Member kill_without_query is inverse of query-on-exit flag. + + * process.c (Qlocal, QCname, QCbuffer, QChost, QCservice, QCfamily) + (QClocal, QCremote, QCserver, QCdatagram, QCnowait, QCnoquery) + (QCstop, QCfilter, QCsentinel, QClog, QCfeature): New variables. + (NETCONN1_P): New macro. + (DATAGRAM_SOCKETS): New conditional symbol. + (datagram_address): New array. + (DATAGRAM_CONN_P, DATAGRAM_CHAN_P): New macros. + (status_message): Use concat3. + (Fprocess_status): Add `listen' status to doc string. Return `stop' + for a stopped network process. + (Fset_process_buffer): Update contact plist for network process. + (Fset_process_filter): Ditto. Don't enable input for stopped + network processes. Server must listen, even if filter is t. + (Fset_process_query_on_exit_flag, Fprocess_query_on_exit_flag): + New functions. + (Fprocess_kill_without_query): Removed. Now defined in simple.el. + (Fprocess_contact): Added KEY argument. Handle datagrams. + (list_processes_1): Optionally show only processes with the query + on exit flag set. Dynamically adjust column widths. Omit tty + column if not needed. Report stopped network processes. + Identify server and datagram network processes. + (Flist_processes): New optional arg `query-only'. + (conv_sockaddr_to_lisp, get_lisp_to_sockaddr_size) + (conv_lisp_to_sockaddr): New helper functions. + (Fprocess_datagram_address, Fset_process_datagram_address): + New lisp functions. + (network_process_featurep, unwind_request_sigio): New helper functions. + (Fopen_network_stream): Removed. Now defined in simple.el. + (Fmake_network_process): New lisp function. Code is based on previous + Fopen_network_stream, but heavily reworked with new property list based + argument list, support for datagrams, server processes, and local + sockets in addition to old client-only functionality. + (server_accept_connection): New function. + (wait_reading_process_input): Use it to handle incoming connects. + Do not enable input on a new connection if process is stopped. + (read_process_output): Handle datagram sockets. Use 2k buffer for them. + (send_process): Handle datagram sockets. + (Fstop_process, Fcontinue_process): Apply to network processes. A stopped + network process is indicated by setting command field to t . + (Fprocess_send_eof): No-op if datagram connection. + (Fstatus_notify): Don't read input for a stream server socket or a + stopped network process. + (init_process): Initialize datagram_address array. + (syms_of_process): Intern and staticpro new variables, defsubr new + functions. + + 2002-03-13 Stefan Monnier <monnier@cs.yale.edu> * xterm.c (x_set_toolkit_scroll_bar_thumb) <USE_MOTIF>: Index: src/process.h =================================================================== RCS file: /cvs/emacs/src/process.h,v retrieving revision 1.18 diff -c -r1.18 process.h *** src/process.h 14 Oct 2001 20:14:49 -0000 1.18 --- src/process.h 13 Mar 2002 23:13:58 -0000 *************** *** 40,52 **** Lisp_Object tty_name; /* Name of this process */ Lisp_Object name; ! /* List of command arguments that this process was run with */ Lisp_Object command; /* (funcall FILTER PROC STRING) (if FILTER is non-nil) to dispose of a bunch of chars from the process all at once */ Lisp_Object filter; /* (funcall SENTINEL PROCESS) when process state changes */ Lisp_Object sentinel; /* Buffer that output is going to */ Lisp_Object buffer; /* Number of this process */ --- 40,56 ---- Lisp_Object tty_name; /* Name of this process */ Lisp_Object name; ! /* List of command arguments that this process was run with. ! Is set to t for a stopped network process; nil otherwise. */ Lisp_Object command; /* (funcall FILTER PROC STRING) (if FILTER is non-nil) to dispose of a bunch of chars from the process all at once */ Lisp_Object filter; /* (funcall SENTINEL PROCESS) when process state changes */ Lisp_Object sentinel; + /* (funcall LOG SERVER CLIENT MESSAGE) when a server process + accepts a connection from a client. */ + Lisp_Object log; /* Buffer that output is going to */ Lisp_Object buffer; /* Number of this process */ *************** *** 54,64 **** /* Non-nil if this is really a command channel */ Lisp_Object command_channel_p; /* t if this is a real child process. ! For a net connection, it is (HOST SERVICE). */ Lisp_Object childp; /* Marker set to end of last buffer-inserted output from this process */ Lisp_Object mark; ! /* Non-nil means kill silently if Emacs is exited. */ Lisp_Object kill_without_query; /* Record the process status in the raw form in which it comes from `wait'. This is to avoid consing in a signal handler. */ --- 58,69 ---- /* Non-nil if this is really a command channel */ Lisp_Object command_channel_p; /* t if this is a real child process. ! For a net connection, it is a plist based on the arguments to make-network-process. */ Lisp_Object childp; /* Marker set to end of last buffer-inserted output from this process */ Lisp_Object mark; ! /* Non-nil means kill silently if Emacs is exited. ! This is the inverse of the `query-on-exit' flag. */ Lisp_Object kill_without_query; /* Record the process status in the raw form in which it comes from `wait'. This is to avoid consing in a signal handler. */ Index: src/process.c =================================================================== RCS file: /cvs/emacs/src/process.c,v retrieving revision 1.355 diff -c -r1.355 process.c *** src/process.c 3 Mar 2002 00:31:22 -0000 1.355 --- src/process.c 13 Mar 2002 23:14:00 -0000 *************** *** 54,59 **** --- 54,67 ---- #include <netdb.h> #include <netinet/in.h> #include <arpa/inet.h> + #ifndef AF_LOCAL + #ifdef AF_UNIX + #define AF_LOCAL AF_UNIX + #endif + #endif + #ifdef AF_LOCAL + #include <sys/un.h> + #endif #ifdef NEED_NET_ERRNO_H #include <net/errno.h> #endif /* NEED_NET_ERRNO_H */ *************** *** 113,119 **** Lisp_Object Qprocessp; Lisp_Object Qrun, Qstop, Qsignal; ! Lisp_Object Qopen, Qclosed, Qconnect, Qfailed; Lisp_Object Qlast_nonmenu_event; /* Qexit is declared and initialized in eval.c. */ --- 121,132 ---- Lisp_Object Qprocessp; Lisp_Object Qrun, Qstop, Qsignal; ! Lisp_Object Qopen, Qclosed, Qconnect, Qfailed, Qlisten; ! Lisp_Object Qlocal; ! Lisp_Object QCname, QCbuffer, QChost, QCservice, QCfamily; ! Lisp_Object QClocal, QCremote; ! Lisp_Object QCserver, QCdatagram, QCnowait, QCnoquery, QCstop; ! Lisp_Object QCfilter, QCsentinel, QClog, QCfeature; Lisp_Object Qlast_nonmenu_event; /* Qexit is declared and initialized in eval.c. */ *************** *** 122,129 **** --- 135,144 ---- #ifdef HAVE_SOCKETS #define NETCONN_P(p) (GC_CONSP (XPROCESS (p)->childp)) + #define NETCONN1_P(p) (GC_CONSP ((p)->childp)) #else #define NETCONN_P(p) 0 + #define NETCONN1_P(p) 0 #endif /* HAVE_SOCKETS */ /* Define first descriptor number available for subprocesses. */ *************** *** 194,203 **** --- 209,247 ---- #endif /* NON_BLOCKING_CONNECT */ #endif /* BROKEN_NON_BLOCKING_CONNECT */ + /* Define DATAGRAM_SOCKETS if datagrams can be used safely on + this system. We need to read full packets, so we need a + "non-destructive" select. So we require either native select, + or emulation of select using FIONREAD. */ + + #ifdef GNU_LINUX + /* These are not yet in configure.in (they will be eventually) + -- so add them here temporarily. ++kfs */ + #define HAVE_RECVFROM + #define HAVE_SENDTO + #define HAVE_GETSOCKNAME + #endif + + #ifdef BROKEN_DATAGRAM_SOCKETS + #undef DATAGRAM_SOCKETS + #else + #ifndef DATAGRAM_SOCKETS + #ifdef HAVE_SOCKETS + #if defined (HAVE_SELECT) || defined (FIONREAD) + #if defined (HAVE_SENDTO) && defined (HAVE_RECVFROM) && defined (EMSGSIZE) + #define DATAGRAM_SOCKETS + #endif /* HAVE_SENDTO && HAVE_RECVFROM && EMSGSIZE */ + #endif /* HAVE_SELECT || FIONREAD */ + #endif /* HAVE_SOCKETS */ + #endif /* DATAGRAM_SOCKETS */ + #endif /* BROKEN_DATAGRAM_SOCKETS */ + #ifdef TERM #undef NON_BLOCKING_CONNECT + #undef DATAGRAM_SOCKETS #endif + #include "sysselect.h" extern int keyboard_bit_set P_ ((SELECT_TYPE *)); *************** *** 257,262 **** --- 301,319 ---- static struct coding_system *proc_decode_coding_system[MAXDESC]; static struct coding_system *proc_encode_coding_system[MAXDESC]; + #ifdef DATAGRAM_SOCKETS + /* Table of `partner address' for datagram sockets. */ + struct sockaddr_and_len { + struct sockaddr *sa; + int len; + } datagram_address[MAXDESC]; + #define DATAGRAM_CHAN_P(chan) (datagram_address[chan].sa != 0) + #define DATAGRAM_CONN_P(proc) (datagram_address[XPROCESS (proc)->infd].sa != 0) + #else + #define DATAGRAM_CHAN_P(chan) (0) + #define DATAGRAM_CONN_P(proc) (0) + #endif + static Lisp_Object get_process (); static void exec_sentinel (); *************** *** 367,381 **** return build_string ("finished\n"); string = Fnumber_to_string (make_number (code)); string2 = build_string (coredump ? " (core dumped)\n" : "\n"); ! return concat2 (build_string ("exited abnormally with code "), ! concat2 (string, string2)); } else if (EQ (symbol, Qfailed)) { string = Fnumber_to_string (make_number (code)); string2 = build_string ("\n"); ! return concat2 (build_string ("failed with code "), ! concat2 (string, string2)); } else return Fcopy_sequence (Fsymbol_name (symbol)); --- 424,438 ---- return build_string ("finished\n"); string = Fnumber_to_string (make_number (code)); string2 = build_string (coredump ? " (core dumped)\n" : "\n"); ! return concat3 (build_string ("exited abnormally with code "), ! string, string2); } else if (EQ (symbol, Qfailed)) { string = Fnumber_to_string (make_number (code)); string2 = build_string ("\n"); ! return concat3 (build_string ("failed with code "), ! string, string2); } else return Fcopy_sequence (Fsymbol_name (symbol)); *************** *** 635,640 **** --- 692,698 ---- exit -- for a process that has exited. signal -- for a process that has got a fatal signal. open -- for a network stream connection that is open. + listen -- for a network stream server that is listening. closed -- for a network stream connection that is closed. connect -- when waiting for a non-blocking connection to complete. failed -- when a non-blocking connection has failed. *************** *** 661,672 **** status = p->status; if (CONSP (status)) status = XCAR (status); ! if (NETCONN_P (process)) { ! if (EQ (status, Qrun)) ! status = Qopen; ! else if (EQ (status, Qexit)) status = Qclosed; } return status; } --- 719,732 ---- status = p->status; if (CONSP (status)) status = XCAR (status); ! if (NETCONN1_P (p)) { ! if (EQ (status, Qexit)) status = Qclosed; + else if (EQ (p->command, Qt)) + status = Qstop; + else if (EQ (status, Qrun)) + status = Qopen; } return status; } *************** *** 737,746 **** (process, buffer) register Lisp_Object process, buffer; { CHECK_PROCESS (process); if (!NILP (buffer)) CHECK_BUFFER (buffer); ! XPROCESS (process)->buffer = buffer; return buffer; } --- 797,811 ---- (process, buffer) register Lisp_Object process, buffer; { + struct Lisp_Process *p; + CHECK_PROCESS (process); if (!NILP (buffer)) CHECK_BUFFER (buffer); ! p = XPROCESS (process); ! p->buffer = buffer; ! if (NETCONN1_P (p)) ! p->childp = Fplist_put (p->childp, QCbuffer, buffer); return buffer; } *************** *** 791,802 **** if (XINT (p->infd) >= 0) { ! if (EQ (filter, Qt)) { FD_CLR (XINT (p->infd), &input_wait_mask); FD_CLR (XINT (p->infd), &non_keyboard_wait_mask); } ! else if (EQ (XPROCESS (process)->filter, Qt)) { FD_SET (XINT (p->infd), &input_wait_mask); FD_SET (XINT (p->infd), &non_keyboard_wait_mask); --- 856,868 ---- if (XINT (p->infd) >= 0) { ! if (EQ (filter, Qt) && !EQ (p->status, Qlisten)) { FD_CLR (XINT (p->infd), &input_wait_mask); FD_CLR (XINT (p->infd), &non_keyboard_wait_mask); } ! else if (EQ (p->filter, Qt) ! && !EQ (p->command, Qt)) /* Network process not stopped. */ { FD_SET (XINT (p->infd), &input_wait_mask); FD_SET (XINT (p->infd), &non_keyboard_wait_mask); *************** *** 804,809 **** --- 870,877 ---- } p->filter = filter; + if (NETCONN1_P (p)) + p->childp = Fplist_put (p->childp, QCfilter, filter); return filter; } *************** *** 899,930 **** return XPROCESS (process)->inherit_coding_system_flag; } ! DEFUN ("process-kill-without-query", Fprocess_kill_without_query, ! Sprocess_kill_without_query, 1, 2, 0, ! doc: /* Say no query needed if PROCESS is running when Emacs is exited. ! Optional second argument if non-nil says to require a query. ! Value is t if a query was formerly required. */) ! (process, value) ! register Lisp_Object process, value; { - Lisp_Object tem; - CHECK_PROCESS (process); ! tem = XPROCESS (process)->kill_without_query; ! XPROCESS (process)->kill_without_query = Fnull (value); ! ! return Fnull (tem); } ! DEFUN ("process-contact", Fprocess_contact, Sprocess_contact, 1, 1, 0, ! doc: /* Return the contact info of PROCESS; t for a real child. ! For a net connection, the value is a cons cell of the form (HOST SERVICE). */) (process) register Lisp_Object process; { CHECK_PROCESS (process); ! return XPROCESS (process)->childp; } #if 0 /* Turned off because we don't currently record this info --- 967,1030 ---- return XPROCESS (process)->inherit_coding_system_flag; } ! DEFUN ("set-process-query-on-exit-flag", ! Fset_process_query_on_exit_flag, Sset_process_query_on_exit_flag, ! 2, 2, 0, ! doc: /* Specify if query is needed for PROCESS when Emacs is exited. ! If the second argument FLAG is non-nil, emacs will query the user before ! exiting if PROCESS is running. */) ! (process, flag) ! register Lisp_Object process, flag; { CHECK_PROCESS (process); ! XPROCESS (process)->kill_without_query = Fnull (flag); ! return flag; } ! DEFUN ("process-query-on-exit-flag", ! Fprocess_query_on_exit_flag, Sprocess_query_on_exit_flag, 1, 1, 0, ! doc: /* Return the current value of query on exit flag for PROCESS. */) (process) register Lisp_Object process; { CHECK_PROCESS (process); ! return Fnull (XPROCESS (process)->kill_without_query); ! } ! ! #ifdef DATAGRAM_SOCKETS ! Lisp_Object Fprocess_datagram_address (); ! #endif ! ! DEFUN ("process-contact", Fprocess_contact, Sprocess_contact, ! 1, 2, 0, ! doc: /* Return the contact info of PROCESS; t for a real child. ! For a net connection, the value depends on the optional KEY arg. ! If KEY is nil, value is a cons cell of the form (HOST SERVICE), ! if KEY is t, the complete contact information for the connection is ! returned, else the specific value for the keyword KEY is returned. ! See `make-network-process' for a list of keywords. */) ! (process, key) ! register Lisp_Object process, key; ! { ! Lisp_Object contact; ! ! CHECK_PROCESS (process); ! contact = XPROCESS (process)->childp; ! ! #ifdef DATAGRAM_SOCKETS ! if (DATAGRAM_CONN_P (process) ! && (EQ (key, Qt) || EQ (key, QCremote))) ! contact = Fplist_put (contact, QCremote, ! Fprocess_datagram_address (process)); ! #endif ! ! if (!NETCONN_P (process) || EQ (key, Qt)) ! return contact; ! if (NILP (key)) ! return Fcons (Fplist_get (contact, QChost), ! Fcons (Fplist_get (contact, QCservice), Qnil)); ! return Fplist_get (contact, key); } #if 0 /* Turned off because we don't currently record this info *************** *** 941,952 **** #endif \f Lisp_Object ! list_processes_1 () { register Lisp_Object tail, tem; Lisp_Object proc, minspace, tem1; register struct Lisp_Process *p; ! char tembuf[80]; XSETFASTINT (minspace, 1); --- 1041,1095 ---- #endif \f Lisp_Object ! list_processes_1 (query_only) ! Lisp_Object query_only; { register Lisp_Object tail, tem; Lisp_Object proc, minspace, tem1; register struct Lisp_Process *p; ! char tembuf[300]; ! int w_proc, w_buffer, w_tty; ! Lisp_Object i_status, i_buffer, i_tty, i_command; ! ! w_proc = 4; /* Proc */ ! w_buffer = 6; /* Buffer */ ! w_tty = 0; /* Omit if no ttys */ ! ! for (tail = Vprocess_alist; !NILP (tail); tail = Fcdr (tail)) ! { ! int i; ! ! proc = Fcdr (Fcar (tail)); ! p = XPROCESS (proc); ! if (NILP (p->childp)) ! continue; ! if (!NILP (query_only) && !NILP (p->kill_without_query)) ! continue; ! if (STRINGP (p->name) ! && ( i = XSTRING (p->name)->size, (i > w_proc))) ! w_proc = i; ! if (!NILP (p->buffer)) ! { ! if (NILP (XBUFFER (p->buffer)->name) && w_buffer < 8) ! w_buffer = 8; /* (Killed) */ ! else if ((i = XSTRING (XBUFFER (p->buffer)->name)->size, (i > w_buffer))) ! w_buffer = i; ! } ! if (STRINGP (p->tty_name) ! && (i = XSTRING (p->tty_name)->size, (i > w_tty))) ! w_tty = i; ! } ! ! XSETFASTINT (i_status, w_proc + 1); ! XSETFASTINT (i_buffer, XFASTINT (i_status) + 9); ! if (w_tty) ! { ! XSETFASTINT (i_tty, XFASTINT (i_buffer) + w_buffer + 1); ! XSETFASTINT (i_command, XFASTINT (i_buffer) + w_tty + 1); ! } else { ! i_tty = Qnil; ! XSETFASTINT (i_command, XFASTINT (i_buffer) + w_buffer + 1); ! } XSETFASTINT (minspace, 1); *************** *** 955,963 **** current_buffer->truncate_lines = Qt; ! write_string ("\ ! Proc Status Buffer Tty Command\n\ ! ---- ------ ------ --- -------\n", -1); for (tail = Vprocess_alist; !NILP (tail); tail = Fcdr (tail)) { --- 1098,1122 ---- current_buffer->truncate_lines = Qt; ! write_string ("Proc", -1); ! Findent_to (i_status, minspace); write_string ("Status", -1); ! Findent_to (i_buffer, minspace); write_string ("Buffer", -1); ! if (!NILP (i_tty)) ! { ! Findent_to (i_tty, minspace); write_string ("Tty", -1); ! } ! Findent_to (i_command, minspace); write_string ("Command", -1); ! write_string ("\n", -1); ! ! write_string ("----", -1); ! Findent_to (i_status, minspace); write_string ("------", -1); ! Findent_to (i_buffer, minspace); write_string ("------", -1); ! if (!NILP (i_tty)) ! { ! Findent_to (i_tty, minspace); write_string ("---", -1); ! } ! Findent_to (i_command, minspace); write_string ("-------", -1); ! write_string ("\n", -1); for (tail = Vprocess_alist; !NILP (tail); tail = Fcdr (tail)) { *************** *** 967,975 **** p = XPROCESS (proc); if (NILP (p->childp)) continue; Finsert (1, &p->name); ! Findent_to (make_number (13), minspace); if (!NILP (p->raw_status_low)) update_status (p); --- 1126,1136 ---- p = XPROCESS (proc); if (NILP (p->childp)) continue; + if (!NILP (query_only) && !NILP (p->kill_without_query)) + continue; Finsert (1, &p->name); ! Findent_to (i_status, minspace); if (!NILP (p->raw_status_low)) update_status (p); *************** *** 989,1000 **** #endif Fprinc (symbol, Qnil); } ! else if (NETCONN_P (proc)) { ! if (EQ (symbol, Qrun)) ! write_string ("open", -1); ! else if (EQ (symbol, Qexit)) write_string ("closed", -1); else Fprinc (symbol, Qnil); } --- 1150,1163 ---- #endif Fprinc (symbol, Qnil); } ! else if (NETCONN1_P (p)) { ! if (EQ (symbol, Qexit)) write_string ("closed", -1); + else if (EQ (p->command, Qt)) + write_string ("stopped", -1); + else if (EQ (symbol, Qrun)) + write_string ("open", -1); else Fprinc (symbol, Qnil); } *************** *** 1015,1021 **** if (EQ (symbol, Qsignal) || EQ (symbol, Qexit)) remove_process (proc); ! Findent_to (make_number (22), minspace); if (NILP (p->buffer)) insert_string ("(none)"); else if (NILP (XBUFFER (p->buffer)->name)) --- 1178,1184 ---- if (EQ (symbol, Qsignal) || EQ (symbol, Qexit)) remove_process (proc); ! Findent_to (i_buffer, minspace); if (NILP (p->buffer)) insert_string ("(none)"); else if (NILP (XBUFFER (p->buffer)->name)) *************** *** 1023,1041 **** else Finsert (1, &XBUFFER (p->buffer)->name); ! Findent_to (make_number (37), minspace); ! ! if (STRINGP (p->tty_name)) ! Finsert (1, &p->tty_name); ! else ! insert_string ("(none)"); ! Findent_to (make_number (49), minspace); ! if (NETCONN_P (proc)) { ! sprintf (tembuf, "(network stream connection to %s)\n", ! XSTRING (XCAR (p->childp))->data); insert_string (tembuf); } else --- 1186,1224 ---- else Finsert (1, &XBUFFER (p->buffer)->name); ! if (!NILP (i_tty)) ! { ! Findent_to (i_tty, minspace); ! if (STRINGP (p->tty_name)) ! Finsert (1, &p->tty_name); ! } ! Findent_to (i_command, minspace); ! if (EQ (p->status, Qlisten)) ! { ! Lisp_Object port = Fplist_get (p->childp, QCservice); ! if (INTEGERP (port)) ! port = Fnumber_to_string (port); ! sprintf (tembuf, "(network %s server on %s)\n", ! (DATAGRAM_CHAN_P (p->infd) ? "datagram" : "stream"), ! XSTRING (port)->data); ! insert_string (tembuf); ! } ! else if (NETCONN1_P (p)) { ! /* For a local socket, there is no host name, ! so display service instead. */ ! Lisp_Object host = Fplist_get (p->childp, QChost); ! if (!STRINGP (host)) ! { ! host = Fplist_get (p->childp, QCservice); ! if (INTEGERP (host)) ! host = Fnumber_to_string (host); ! } ! sprintf (tembuf, "(network %s connection to %s)\n", ! (DATAGRAM_CHAN_P (p->infd) ? "datagram" : "stream"), ! XSTRING (host)->data); insert_string (tembuf); } else *************** *** 1056,1069 **** return Qnil; } ! DEFUN ("list-processes", Flist_processes, Slist_processes, 0, 0, "", doc: /* Display a list of all processes. Any process listed as exited or signaled is actually eliminated after the listing is made. */) ! () { internal_with_output_to_temp_buffer ("*Process List*", ! list_processes_1, Qnil); return Qnil; } --- 1239,1255 ---- return Qnil; } ! DEFUN ("list-processes", Flist_processes, Slist_processes, 0, 1, "P", doc: /* Display a list of all processes. + If optional argument QUERY-ONLY is non-nil, only processes with + the query-on-exit flag set will be listed. Any process listed as exited or signaled is actually eliminated after the listing is made. */) ! (query_only) ! Lisp_Object query_only; { internal_with_output_to_temp_buffer ("*Process List*", ! list_processes_1, query_only); return Qnil; } *************** *** 1776,1829 **** } #endif /* not VMS */ #ifdef HAVE_SOCKETS ! /* open a TCP network connection to a given HOST/SERVICE. Treated ! exactly like a normal process when reading and writing. Only differences are in status display and process deletion. A network connection has no PID; you cannot signal it. All you can do is ! deactivate and close it via delete-process */ ! ! DEFUN ("open-network-stream", Fopen_network_stream, Sopen_network_stream, ! 4, 7, 0, ! doc: /* Open a TCP connection for a service to a host. ! Returns a subprocess-object to represent the connection. ! Returns nil if a non-blocking connect is attempted on a system which ! cannot support that; in that case, the caller should attempt a ! normal connect instead. Input and output work as for subprocesses; `delete-process' closes it. ! Args are NAME BUFFER HOST SERVICE FILTER SENTINEL NON-BLOCKING. ! NAME is name for process. It is modified if necessary to make it unique. ! BUFFER is the buffer (or buffer-name) to associate with the process. ! Process output goes at end of that buffer, unless you specify ! an output stream or filter function to handle the output. ! BUFFER may be also nil, meaning that this process is not associated ! with any buffer. ! HOST is name of the host to connect to, or its IP address. ! SERVICE is name of the service desired, or an integer specifying a ! port number to connect to. ! FILTER and SENTINEL are optional args specifying the filter and ! sentinel functions associated with the network stream. ! NON-BLOCKING is optional arg requesting an non-blocking connect. ! When non-nil, open-network-stream will return immediately without ! waiting for the connection to be made. Instead, the sentinel function ! will be called with second matching "open" (if successful) or ! "failed" when the connect completes. */) ! (name, buffer, host, service, filter, sentinel, non_blocking) ! Lisp_Object name, buffer, host, service, filter, sentinel, non_blocking; { Lisp_Object proc; #ifdef HAVE_GETADDRINFO ! struct addrinfo hints, *res, *lres; ! char *portstring, portbuf[128]; #else /* HAVE_GETADDRINFO */ - struct sockaddr_in address; - struct servent *svc_info; - struct hostent *host_info_ptr, host_info; - char *(addr_list[2]); - IN_ADDR numeric_addr; - int port; struct _emacs_addrinfo { int ai_family; --- 1962,2360 ---- } #endif /* not VMS */ + \f #ifdef HAVE_SOCKETS ! /* Convert an internal struct sockaddr to a lisp object (vector or string). ! The address family of sa is not included in the result. */ ! ! static Lisp_Object ! conv_sockaddr_to_lisp (sa, len) ! struct sockaddr *sa; ! int len; ! { ! Lisp_Object address; ! int i; ! unsigned char *cp; ! register struct Lisp_Vector *p; ! ! switch (sa->sa_family) ! { ! case AF_INET: ! { ! struct sockaddr_in *sin = (struct sockaddr_in *) sa; ! len = sizeof (sin->sin_addr) + 1; ! address = Fmake_vector (make_number (len), Qnil); ! p = XVECTOR (address); ! p->contents[--len] = make_number (ntohs (sin->sin_port)); ! cp = (unsigned char *)&sin->sin_addr; ! break; ! } ! #ifdef AF_LOCAL ! case AF_LOCAL: ! { ! struct sockaddr_un *sun = (struct sockaddr_un *) sa; ! for (i = 0; i < sizeof (sun->sun_path); i++) ! if (sun->sun_path[i] == 0) ! break; ! return make_unibyte_string (sun->sun_path, i); ! } ! #endif ! default: ! len -= sizeof (sa->sa_family); ! address = Fcons (make_number (sa->sa_family), ! Fmake_vector (make_number (len), Qnil)); ! p = XVECTOR (XCDR (address)); ! cp = (unsigned char *) sa + sizeof (sa->sa_family); ! break; ! } ! ! i = 0; ! while (i < len) ! p->contents[i++] = make_number (*cp++); ! ! return address; ! } ! ! ! /* Get family and required size for sockaddr structure to hold ADDRESS. */ ! ! static int ! get_lisp_to_sockaddr_size (address, familyp) ! Lisp_Object address; ! int *familyp; ! { ! register struct Lisp_Vector *p; ! ! if (VECTORP (address)) ! { ! p = XVECTOR (address); ! if (p->size == 5) ! { ! *familyp = AF_INET; ! return sizeof (struct sockaddr_in); ! } ! } ! #ifdef AF_LOCAL ! else if (STRINGP (address)) ! { ! *familyp = AF_LOCAL; ! return sizeof (struct sockaddr_un); ! } ! #endif ! else if (CONSP (address) && INTEGERP (XCAR (address)) && VECTORP (XCDR (address))) ! { ! struct sockaddr *sa; ! *familyp = XINT (XCAR (address)); ! p = XVECTOR (XCDR (address)); ! return p->size + sizeof (sa->sa_family); ! } ! return 0; ! } ! ! /* Convert an address object (vector or string) to an internal sockaddr. ! Format of address has already been validated by size_lisp_to_sockaddr. */ ! ! static void ! conv_lisp_to_sockaddr (family, address, sa, len) ! int family; ! Lisp_Object address; ! struct sockaddr *sa; ! int len; ! { ! register struct Lisp_Vector *p; ! register unsigned char *cp; ! register int i; ! ! bzero (sa, len); ! sa->sa_family = family; ! ! if (VECTORP (address)) ! { ! p = XVECTOR (address); ! if (family == AF_INET) ! { ! struct sockaddr_in *sin = (struct sockaddr_in *) sa; ! len = sizeof (sin->sin_addr) + 1; ! i = XINT (p->contents[--len]); ! sin->sin_port = htons (i); ! cp = (unsigned char *)&sin->sin_addr; ! } ! } ! else if (STRINGP (address)) ! { ! #ifdef AF_LOCAL ! if (family == AF_LOCAL) ! { ! struct sockaddr_un *sun = (struct sockaddr_un *) sa; ! cp = XSTRING (address)->data; ! for (i = 0; i < sizeof (sun->sun_path) && *cp; i++) ! sun->sun_path[i] = *cp++; ! } ! #endif ! return; ! } ! else ! { ! p = XVECTOR (XCDR (address)); ! cp = (unsigned char *)sa + sizeof (sa->sa_family); ! } ! ! for (i = 0; i < len; i++) ! if (INTEGERP (p->contents[i])) ! *cp++ = XFASTINT (p->contents[i]) & 0xff; ! } ! ! #ifdef DATAGRAM_SOCKETS ! DEFUN ("process-datagram-address", Fprocess_datagram_address, Sprocess_datagram_address, ! 1, 1, 0, ! doc: /* Get the current datagram address associated with PROCESS. */) ! (process) ! Lisp_Object process; ! { ! int channel; ! ! CHECK_PROCESS (process); ! ! if (!DATAGRAM_CONN_P (process)) ! return Qnil; ! ! channel = XPROCESS (process)->infd; ! return conv_sockaddr_to_lisp (datagram_address[channel].sa, ! datagram_address[channel].len); ! } ! ! DEFUN ("set-process-datagram-address", Fset_process_datagram_address, Sset_process_datagram_address, ! 2, 2, 0, ! doc: /* Set the datagram address for PROCESS to ADDRESS. ! Returns nil upon error setting address, ADDRESS otherwise. */) ! (process, address) ! Lisp_Object process, address; ! { ! int channel; ! int family, len; ! ! CHECK_PROCESS (process); ! ! if (!DATAGRAM_CONN_P (process)) ! return Qnil; ! ! channel = XPROCESS (process)->infd; ! ! len = get_lisp_to_sockaddr_size (address, &family); ! if (datagram_address[channel].len != len) ! return Qnil; ! conv_lisp_to_sockaddr (family, address, datagram_address[channel].sa, len); ! return address; ! } ! #endif ! \f ! /* Check whether a given KEY VALUE pair is supported on this system. */ ! ! static int ! network_process_featurep (key, value) ! Lisp_Object key, value; ! { ! ! if (EQ (key, QCnowait)) ! { ! #ifdef NON_BLOCKING_CONNECT ! return 1; ! #else ! return NILP (value); ! #endif ! } ! ! if (EQ (key, QCdatagram)) ! { ! #ifdef DATAGRAM_SOCKETS ! return 1; ! #else ! return NILP (value); ! #endif ! } ! ! if (EQ (key, QCfamily)) ! { ! if (NILP (value)) ! return 1; ! #ifdef AF_LOCAL ! if (EQ (key, Qlocal)) ! return 1; ! #endif ! return 0; ! } ! ! if (EQ (key, QCname)) ! return STRINGP (value); ! ! if (EQ (key, QCbuffer)) ! return (NILP (value) || STRINGP (value) || BUFFERP (value)); ! ! if (EQ (key, QClocal) || EQ (key, QCremote)) ! { ! int family; ! return get_lisp_to_sockaddr_size (value, &family); ! } ! ! if (EQ (key, QChost)) ! return (NILP (value) || STRINGP (value)); ! ! if (EQ (key, QCservice)) ! { ! #ifdef HAVE_GETSOCKNAME ! if (EQ (value, Qt)) ! return 1; ! #endif ! return (INTEGERP (value) || STRINGP (value)); ! } ! ! if (EQ (key, QCserver)) ! { ! #ifndef TERM ! return 1; ! #else ! return NILP (value); ! #endif ! } ! ! if (EQ (key, QCsentinel)) ! return 1; ! if (EQ (key, QCfilter)) ! return 1; ! if (EQ (key, QClog)) ! return 1; ! if (EQ (key, QCnoquery)) ! return 1; ! if (EQ (key, QCstop)) ! return 1; ! ! return 0; ! } ! ! /* A version of request_sigio suitable for a record_unwind_protect. */ ! ! Lisp_Object ! unwind_request_sigio (dummy) ! Lisp_Object dummy; ! { ! if (interrupt_input) ! request_sigio (); ! return Qnil; ! } ! ! /* Create a network stream/datagram client/server process. Treated ! exactly like a normal process when reading and writing. Primary differences are in status display and process deletion. A network connection has no PID; you cannot signal it. All you can do is ! stop/continue it and deactivate/close it via delete-process */ + DEFUN ("make-network-process", Fmake_network_process, Smake_network_process, + 0, MANY, 0, + doc: /* Create and return a network server or client process. Input and output work as for subprocesses; `delete-process' closes it. ! ! Arguments are specified as keyword/argument pairs. The following ! arguments are defined: ! ! :name NAME -- NAME is name for process. It is modified if necessary ! to make it unique. ! ! :buffer BUFFER -- BUFFER is the buffer (or buffer-name) to associate ! with the process. Process output goes at end of that buffer, unless ! you specify an output stream or filter function to handle the output. ! BUFFER may be also nil, meaning that this process is not associated ! with any buffer. ! ! :host HOST -- HOST is name of the host to connect to, or its IP ! address. If specified for a server process, only clients on that host ! may connect. The symbol `local' specifies the local host. ! ! :service SERVICE -- SERVICE is name of the service desired, or an ! integer specifying a port number to connect to. If SERVICE is t, ! a random port number is selected for the server. ! ! :local ADDRESS -- ADDRESS is the local address used for the ! connection. This parameter is ignored when opening a client process. ! When specified for a server process, the HOST and SERVICE are ignored. ! ! :remote ADDRESS -- ADDRESS is the remote partner's address for the ! connection. This parameter is ignored when opening a server process. ! When specified for a client process, the HOST and SERVICE are ignored. ! ! :family FAMILY -- FAMILY is the address (and protocol) family for the ! service specified by HOST and SERVICE. The default address family is ! Inet (or IPv4) for the host and port number specified by HOST and ! SERVICE. Other address families supported are: ! local -- for a local (i.e. UNIX) address specified by SERVICE. ! ! :datagram BOOL -- Create a datagram type connection if BOOL is ! non-nil. Default is a stream type connection. ! ! :nowait BOOL -- If BOOL is non-nil for a stream type client process, ! return without waiting for the connection to complete; instead, the ! sentinel function will be called with second arg matching "open" (if ! successful) or "failed" when the connect completes. Default is to use ! a blocking connect (i.e. wait) for stream type connections. ! ! :noquery BOOL -- Query the user unless BOOL is non-nil, and process is ! running when emacs is exited. ! ! :stop BOOL -- Start process in the `stopped' state if BOOL non-nil. ! In the stopped state, a server process does not accept new ! connections, and a client process does not handle incoming traffic. ! The stopped state is cleared by `continue-process' and set by ! `stop-process'. ! ! :filter FILTER -- Install FILTER as the process filter. ! ! :sentinel SENTINEL -- Install SENTINEL as the process sentinel. ! ! :log LOG -- Install LOG as the server process log function. This ! function is called as when the server accepts a network connection from a ! client. The arguments are SERVER, CLIENT, and MESSAGE, where SERVER ! is the server process, CLIENT is the new process for the connection, ! and MESSAGE is a string. ! ! :server BOOL -- if BOOL is non-nil, create a server process for the ! specified FAMILY, SERVICE, and connection type (stream or datagram). ! Default is a client process. ! ! A server process will listen for and accept connections from ! clients. When a client connection is accepted, a new network process ! is created for the connection with the following parameters: ! - The client's process name is constructed by concatenating the server ! process' NAME and a client identification string. ! - If the FILTER argument is non-nil, the client process will not get a ! separate process buffer; otherwise, the client's process buffer is a newly ! created buffer named after the server process' BUFFER name or process ! NAME concatenated with the client identification string. ! - The connection type and the process filter and sentinel parameters are ! inherited from the server process' TYPE, FILTER and SENTINEL. ! - The client process' contact info is set according to the client's ! addressing information (typically an IP address and a port number). ! ! Notice that the FILTER and SENTINEL args are never used directly by ! the server process. Also, the BUFFER argument is not used directly by ! the server process, but via `network-server-log-function' hook, a log ! of the accepted (and failed) connections may be recorded in the server ! process' buffer. ! ! The following special call returns t iff a given KEY VALUE ! pair is supported on this system: ! (make-network-process :feature KEY VALUE) */) ! (nargs, args) ! int nargs; ! Lisp_Object *args; { Lisp_Object proc; + Lisp_Object contact; + struct Lisp_Process *p; #ifdef HAVE_GETADDRINFO ! struct addrinfo ai, *res, *lres; ! struct addrinfo hints; ! char *portstring, portbuf[128]; #else /* HAVE_GETADDRINFO */ struct _emacs_addrinfo { int ai_family; *************** *** 1834,1983 **** struct _emacs_addrinfo *ai_next; } ai, *res, *lres; #endif /* HAVE_GETADDRINFO */ int ret = 0; int xerrno = 0; int s = -1, outch, inch; ! struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5, gcpro6; int retry = 0; int count = specpdl_ptr - specpdl; int count1; ! int is_non_blocking = 0; ! if (!NILP (non_blocking)) { ! #ifndef NON_BLOCKING_CONNECT ! return Qnil; ! #else ! non_blocking = Qt; /* Instead of GCPRO */ ! is_non_blocking = 1; ! #endif } #ifdef WINDOWSNT /* Ensure socket support is loaded if available. */ init_winsock (TRUE); #endif ! /* Can only GCPRO 5 variables */ ! GCPRO6 (name, buffer, host, service, sentinel, filter); ! CHECK_STRING (name); ! CHECK_STRING (host); ! #ifdef HAVE_GETADDRINFO ! /* SERVICE can either be a string or int. ! Convert to a C string for later use by getaddrinfo. */ ! if (INTEGERP (service)) { ! sprintf (portbuf, "%ld", (long) XINT (service)); ! portstring = portbuf; } ! else { ! CHECK_STRING (service); ! portstring = XSTRING (service)->data; } ! #else /* HAVE_GETADDRINFO */ if (INTEGERP (service)) port = htons ((unsigned short) XINT (service)); else { CHECK_STRING (service); svc_info = getservbyname (XSTRING (service)->data, "tcp"); if (svc_info == 0) ! error ("Unknown service \"%s\"", XSTRING (service)->data); port = svc_info->s_port; } - #endif /* HAVE_GETADDRINFO */ /* Slow down polling to every ten seconds. Some kernels have a bug which causes retrying connect to fail after a connect. Polling can interfere with gethostbyname too. */ #ifdef POLL_FOR_INPUT ! record_unwind_protect (unwind_stop_other_atimers, Qnil); ! bind_polling_period (10); #endif - #ifndef TERM #ifdef HAVE_GETADDRINFO ! immediate_quit = 1; ! QUIT; ! memset (&hints, 0, sizeof (hints)); ! hints.ai_flags = 0; ! hints.ai_family = AF_UNSPEC; ! hints.ai_socktype = SOCK_STREAM; ! hints.ai_protocol = 0; ! ret = getaddrinfo (XSTRING (host)->data, portstring, &hints, &res); ! if (ret) #ifdef HAVE_GAI_STRERROR ! error ("%s/%s %s", XSTRING (host)->data, portstring, gai_strerror(ret)); #else ! error ("%s/%s getaddrinfo error %d", XSTRING (host)->data, portstring, ! ret); #endif ! immediate_quit = 0; ! #else /* not HAVE_GETADDRINFO */ ! while (1) { ! #if 0 ! #ifdef TRY_AGAIN ! h_errno = 0; ! #endif ! #endif immediate_quit = 1; QUIT; host_info_ptr = gethostbyname (XSTRING (host)->data); immediate_quit = 0; - #if 0 - #ifdef TRY_AGAIN - if (! (host_info_ptr == 0 && h_errno == TRY_AGAIN)) - #endif - #endif - break; - Fsleep_for (make_number (1), Qnil); - } ! if (host_info_ptr == 0) ! /* Attempt to interpret host as numeric inet address */ ! { ! numeric_addr = inet_addr ((char *) XSTRING (host)->data); ! if (NUMERIC_ADDR_ERROR) ! error ("Unknown host \"%s\"", XSTRING (host)->data); ! ! host_info_ptr = &host_info; ! host_info.h_name = 0; ! host_info.h_aliases = 0; ! host_info.h_addrtype = AF_INET; ! #ifdef h_addr ! /* Older machines have only one address slot called h_addr. ! Newer machines have h_addr_list, but #define h_addr to ! be its first element. */ ! host_info.h_addr_list = &(addr_list[0]); ! #endif ! host_info.h_addr = (char*)(&numeric_addr); ! addr_list[1] = 0; ! /* numeric_addr isn't null-terminated; it has fixed length. */ ! host_info.h_length = sizeof (numeric_addr); ! } ! ! bzero (&address, sizeof address); ! bcopy (host_info_ptr->h_addr, (char *) &address.sin_addr, ! host_info_ptr->h_length); ! address.sin_family = host_info_ptr->h_addrtype; ! address.sin_port = port; ! ! /* Emulate HAVE_GETADDRINFO for the loop over `res' below. */ ! ai.ai_family = host_info_ptr->h_addrtype; ! ai.ai_socktype = SOCK_STREAM; ! ai.ai_protocol = 0; ! ai.ai_addr = (struct sockaddr *) &address; ! ai.ai_addrlen = sizeof address; ! ai.ai_next = NULL; ! res = &ai; #endif /* not HAVE_GETADDRINFO */ /* Do this in case we never enter the for-loop below. */ count1 = specpdl_ptr - specpdl; s = -1; --- 2365,2677 ---- struct _emacs_addrinfo *ai_next; } ai, *res, *lres; #endif /* HAVE_GETADDRINFO */ + struct sockaddr *sa = 0; + struct sockaddr_in address_in; + #ifdef AF_LOCAL + struct sockaddr_un address_un; + #endif + int port; int ret = 0; int xerrno = 0; int s = -1, outch, inch; ! struct gcpro gcpro1; int retry = 0; int count = specpdl_ptr - specpdl; int count1; ! Lisp_Object QCaddress; /* one of QClocal or QCremote */ ! Lisp_Object tem; ! Lisp_Object name, buffer, host, service, address; ! Lisp_Object filter, sentinel; ! int is_non_blocking_client = 0; ! int is_server = 0; ! int socktype = SOCK_STREAM; ! int family = -1; ! ! if (nargs == 0) ! return Qnil; ! /* Handle :feature KEY VALUE query. */ ! if (EQ (args[0], QCfeature)) { ! if (nargs != 3) ! return Qnil; ! return network_process_featurep (args[1], args[2]) ? Qt : Qnil; } + /* Save arguments for process-contact and clone-process. */ + contact = Flist (nargs, args); + GCPRO1 (contact); + #ifdef WINDOWSNT /* Ensure socket support is loaded if available. */ init_winsock (TRUE); #endif ! /* :datagram BOOL */ ! tem = Fplist_get (contact, QCdatagram); ! if (!NILP (tem)) ! { ! #ifndef DATAGRAM_SOCKETS ! error ("Datagram connections not supported"); ! #else ! socktype = SOCK_DGRAM; ! #endif ! } ! /* :server BOOL */ ! tem = Fplist_get (contact, QCserver); ! if (!NILP (tem)) { ! #ifdef TERM ! error ("Network servers not supported"); ! #else ! is_server = 1; ! #endif } ! ! /* Make QCaddress an alias for :local (server) or :remote (client). */ ! QCaddress = is_server ? QClocal : QCremote; ! ! /* :wait BOOL */ ! if (!is_server && socktype == SOCK_STREAM ! && (tem = Fplist_get (contact, QCnowait), !NILP (tem))) { ! #ifndef NON_BLOCKING_CONNECT ! error ("Non-blocking connect not supported"); ! #else ! is_non_blocking_client = 1; ! #endif } ! ! name = Fplist_get (contact, QCname); ! buffer = Fplist_get (contact, QCbuffer); ! filter = Fplist_get (contact, QCfilter); ! sentinel = Fplist_get (contact, QCsentinel); ! ! CHECK_STRING (name); ! ! #ifdef TERM ! /* Let's handle TERM before things get complicated ... */ ! host = Fplist_get (contact, QChost); ! CHECK_STRING (host); ! ! service = Fplist_get (contact, QCservice); if (INTEGERP (service)) port = htons ((unsigned short) XINT (service)); else { + struct servent *svc_info; CHECK_STRING (service); svc_info = getservbyname (XSTRING (service)->data, "tcp"); if (svc_info == 0) ! error ("Unknown service: %s", XSTRING (service)->data); port = svc_info->s_port; } + s = connect_server (0); + if (s < 0) + report_file_error ("error creating socket", Fcons (name, Qnil)); + send_command (s, C_PORT, 0, "%s:%d", XSTRING (host)->data, ntohs (port)); + send_command (s, C_DUMB, 1, 0); + + #else /* not TERM */ + + /* Initialize addrinfo structure in case we don't use getaddrinfo. */ + ai.ai_socktype = socktype; + ai.ai_protocol = 0; + ai.ai_next = NULL; + res = &ai; + + /* :local ADDRESS or :remote ADDRESS */ + address = Fplist_get (contact, QCaddress); + if (!NILP (address)) + { + host = service = Qnil; + + if (!(ai.ai_addrlen = get_lisp_to_sockaddr_size (address, &family))) + error ("Malformed :address"); + ai.ai_family = family; + ai.ai_addr = alloca (ai.ai_addrlen); + conv_lisp_to_sockaddr (family, address, ai.ai_addr, ai.ai_addrlen); + goto open_socket; + } + + /* :family FAMILY -- nil (for Inet), local, or integer. */ + tem = Fplist_get (contact, QCfamily); + if (INTEGERP (tem)) + family = XINT (tem); + else + { + if (NILP (tem)) + family = AF_INET; + #ifdef AF_LOCAL + else if (EQ (tem, Qlocal)) + family = AF_LOCAL; + #endif + } + if (family < 0) + error ("Unknown address family"); + ai.ai_family = family; + + /* :service SERVICE -- string, integer (port number), or t (random port). */ + service = Fplist_get (contact, QCservice); + + #ifdef AF_LOCAL + if (family == AF_LOCAL) + { + /* Host is not used. */ + host = Qnil; + CHECK_STRING (service); + bzero (&address_un, sizeof address_un); + address_un.sun_family = AF_LOCAL; + strncpy (address_un.sun_path, XSTRING (service)->data, sizeof address_un.sun_path); + ai.ai_addr = (struct sockaddr *) &address_un; + ai.ai_addrlen = sizeof address_un; + goto open_socket; + } + #endif + + /* :host HOST -- hostname, ip address, or 'local for localhost. */ + host = Fplist_get (contact, QChost); + if (!NILP (host)) + { + if (EQ (host, Qlocal)) + host = build_string ("localhost"); + CHECK_STRING (host); + } /* Slow down polling to every ten seconds. Some kernels have a bug which causes retrying connect to fail after a connect. Polling can interfere with gethostbyname too. */ #ifdef POLL_FOR_INPUT ! if (socktype == SOCK_STREAM) ! { ! record_unwind_protect (unwind_stop_other_atimers, Qnil); ! bind_polling_period (10); ! } #endif #ifdef HAVE_GETADDRINFO ! /* If we have a host, use getaddrinfo to resolve both host and service. ! Otherwise, use getservbyname to lookup the service. */ ! if (!NILP (host)) ! { ! ! /* SERVICE can either be a string or int. ! Convert to a C string for later use by getaddrinfo. */ ! if (EQ (service, Qt)) ! portstring = "0"; ! else if (INTEGERP (service)) ! { ! sprintf (portbuf, "%ld", (long) XINT (service)); ! portstring = portbuf; ! } ! else ! { ! CHECK_STRING (service); ! portstring = XSTRING (service)->data; ! } ! ! immediate_quit = 1; ! QUIT; ! memset (&hints, 0, sizeof (hints)); ! hints.ai_flags = 0; ! hints.ai_family = NILP (Fplist_member (QCfamily)) ? AF_UNSPEC : family; ! hints.ai_socktype = socktype; ! hints.ai_protocol = 0; ! ret = getaddrinfo (XSTRING (host)->data, portstring, &hints, &res); ! if (ret) #ifdef HAVE_GAI_STRERROR ! error ("%s/%s %s", XSTRING (host)->data, portstring, gai_strerror(ret)); #else ! error ("%s/%s getaddrinfo error %d", XSTRING (host)->data, portstring, ret); #endif ! immediate_quit = 0; ! ! goto open_socket; ! } ! #endif /* HAVE_GETADDRINFO */ ! /* We end up here if getaddrinfo is not defined, or in case no hostname ! has been specified (e.g. for a local server process). */ ! if (EQ (service, Qt)) ! port = 0; ! else if (INTEGERP (service)) ! port = htons ((unsigned short) XINT (service)); ! else { ! struct servent *svc_info; ! CHECK_STRING (service); ! svc_info = getservbyname (XSTRING (service)->data, ! (socktype == SOCK_DGRAM ? "udp" : "tcp")); ! if (svc_info == 0) ! error ("Unknown service: %s", XSTRING (service)->data); ! port = svc_info->s_port; ! } ! ! bzero (&address_in, sizeof address_in); ! address_in.sin_family = family; ! address_in.sin_addr.s_addr = INADDR_ANY; ! address_in.sin_port = port; ! ! #ifndef HAVE_GETADDRINFO ! if (!NILP (host)) ! { ! struct hostent *host_info_ptr; ! ! /* gethostbyname may fail with TRY_AGAIN, but we don't honour that, ! as it may `hang' emacs for a very long time. */ immediate_quit = 1; QUIT; host_info_ptr = gethostbyname (XSTRING (host)->data); immediate_quit = 0; ! if (host_info_ptr) ! { ! bcopy (host_info_ptr->h_addr, (char *) &address_in.sin_addr, ! host_info_ptr->h_length); ! family = host_info_ptr->h_addrtype; ! address_in.sin_family = family; ! } ! else ! /* Attempt to interpret host as numeric inet address */ ! { ! IN_ADDR numeric_addr; ! numeric_addr = inet_addr ((char *) XSTRING (host)->data); ! if (NUMERIC_ADDR_ERROR) ! error ("Unknown host \"%s\"", XSTRING (host)->data); ! ! bcopy ((char *)&numeric_addr, (char *) &address_in.sin_addr, ! sizeof (address_in.sin_addr)); ! } ! ! } #endif /* not HAVE_GETADDRINFO */ + ai.ai_family = family; + ai.ai_addr = (struct sockaddr *) &address_in; + ai.ai_addrlen = sizeof address_in; + + open_socket: + + /* Kernel bugs (on Ultrix at least) cause lossage (not just EINTR) + when connect is interrupted. So let's not let it get interrupted. + Note we do not turn off polling, because polling is only used + when not interrupt_input, and thus not normally used on the systems + which have this bug. On systems which use polling, there's no way + to quit if polling is turned off. */ + if (interrupt_input + && !is_server && socktype == SOCK_STREAM) + { + /* Comment from KFS: The original open-network-stream code + didn't unwind protect this, but it seems like the proper + thing to do. In any case, I don't see how it could harm to + do this -- and it makes cleanup (using unbind_to) easier. */ + record_unwind_protect (unwind_request_sigio, Qnil); + unrequest_sigio (); + } + /* Do this in case we never enter the for-loop below. */ count1 = specpdl_ptr - specpdl; s = -1; *************** *** 1991,1998 **** continue; } #ifdef NON_BLOCKING_CONNECT ! if (is_non_blocking) { #ifdef O_NONBLOCK ret = fcntl (s, F_SETFL, O_NONBLOCK); --- 2685,2697 ---- continue; } + #ifdef DATAGRAM_SOCKETS + if (!is_server && socktype == SOCK_DGRAM) + break; + #endif /* DATAGRAM_SOCKETS */ + #ifdef NON_BLOCKING_CONNECT ! if (is_non_blocking_client) { #ifdef O_NONBLOCK ret = fcntl (s, F_SETFL, O_NONBLOCK); *************** *** 2008,2028 **** } } #endif ! ! /* Kernel bugs (on Ultrix at least) cause lossage (not just EINTR) ! when connect is interrupted. So let's not let it get interrupted. ! Note we do not turn off polling, because polling is only used ! when not interrupt_input, and thus not normally used on the systems ! which have this bug. On systems which use polling, there's no way ! to quit if polling is turned off. */ ! if (interrupt_input) ! unrequest_sigio (); ! /* Make us close S if quit. */ - count1 = specpdl_ptr - specpdl; record_unwind_protect (close_file_unwind, make_number (s)); ! loop: immediate_quit = 1; QUIT; --- 2707,2752 ---- } } #endif ! /* Make us close S if quit. */ record_unwind_protect (close_file_unwind, make_number (s)); ! if (is_server) ! { ! /* Configure as a server socket. */ ! #ifdef AF_LOCAL ! if (family != AF_LOCAL) ! #endif ! { ! int optval = 1; ! if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval)) ! report_file_error ("Cannot set reuse option on server socket.", Qnil); ! } ! ! if (bind (s, lres->ai_addr, lres->ai_addrlen)) ! report_file_error ("Cannot bind server socket", Qnil); ! ! #ifdef HAVE_GETSOCKNAME ! if (EQ (service, Qt)) ! { ! struct sockaddr_in sa1; ! int len1 = sizeof (sa1); ! if (getsockname (s, (struct sockaddr *)&sa1, &len1) == 0) ! { ! ((struct sockaddr_in *)(lres->ai_addr))->sin_port = sa1.sin_port; ! service = make_number (sa1.sin_port); ! contact = Fplist_put (contact, QCservice, service); ! } ! } ! #endif ! ! if (socktype == SOCK_STREAM && listen (s, 5)) ! report_file_error ("Cannot listen on server socket", Qnil); ! ! break; ! } ! ! retry_connect: immediate_quit = 1; QUIT; *************** *** 2046,2052 **** if (ret == 0 || xerrno == EISCONN) { - is_non_blocking = 0; /* The unwind-protect will be discarded afterwards. Likewise for immediate_quit. */ break; --- 2770,2775 ---- *************** *** 2054,2064 **** #ifdef NON_BLOCKING_CONNECT #ifdef EINPROGRESS ! if (is_non_blocking && xerrno == EINPROGRESS) break; #else #ifdef EWOULDBLOCK ! if (is_non_blocking && xerrno == EWOULDBLOCK) break; #endif #endif --- 2777,2787 ---- #ifdef NON_BLOCKING_CONNECT #ifdef EINPROGRESS ! if (is_non_blocking_client && xerrno == EINPROGRESS) break; #else #ifdef EWOULDBLOCK ! if (is_non_blocking_client && xerrno == EWOULDBLOCK) break; #endif #endif *************** *** 2067,2073 **** immediate_quit = 0; if (xerrno == EINTR) ! goto loop; if (xerrno == EADDRINUSE && retry < 20) { /* A delay here is needed on some FreeBSD systems, --- 2790,2796 ---- immediate_quit = 0; if (xerrno == EINTR) ! goto retry_connect; if (xerrno == EADDRINUSE && retry < 20) { /* A delay here is needed on some FreeBSD systems, *************** *** 2075,2136 **** and should be infrequent. */ Fsleep_for (make_number (1), Qnil); retry++; ! goto loop; } /* Discard the unwind protect closing S. */ specpdl_ptr = specpdl + count1; - count1 = specpdl_ptr - specpdl; - emacs_close (s); s = -1; } #ifdef HAVE_GETADDRINFO ! freeaddrinfo (res); #endif if (s < 0) { - if (interrupt_input) - request_sigio (); - /* If non-blocking got this far - and failed - assume non-blocking is not supported after all. This is probably a wrong assumption, but ! the normal blocking calls to open-network-stream handles this error ! better. */ ! if (is_non_blocking) ! { ! #ifdef POLL_FOR_INPUT ! unbind_to (count, Qnil); ! #endif return Qnil; - } errno = xerrno; ! report_file_error ("connection failed", ! Fcons (host, Fcons (name, Qnil))); } - - immediate_quit = 0; - - /* Discard the unwind protect, if any. */ - specpdl_ptr = specpdl + count1; - - #ifdef POLL_FOR_INPUT - unbind_to (count, Qnil); - #endif ! if (interrupt_input) ! request_sigio (); ! ! #else /* TERM */ ! s = connect_server (0); ! if (s < 0) ! report_file_error ("error creating socket", Fcons (name, Qnil)); ! send_command (s, C_PORT, 0, "%s:%d", XSTRING (host)->data, ntohs (port)); ! send_command (s, C_DUMB, 1, 0); ! #endif /* TERM */ inch = s; outch = s; --- 2798,2861 ---- and should be infrequent. */ Fsleep_for (make_number (1), Qnil); retry++; ! goto retry_connect; } /* Discard the unwind protect closing S. */ specpdl_ptr = specpdl + count1; emacs_close (s); s = -1; } + if (s >= 0) + { + #ifdef DATAGRAM_SOCKETS + if (socktype == SOCK_DGRAM) + { + if (datagram_address[s].sa) + abort (); + datagram_address[s].sa = (struct sockaddr *) xmalloc (lres->ai_addrlen); + datagram_address[s].len = lres->ai_addrlen; + if (is_server) + bzero (datagram_address[s].sa, lres->ai_addrlen); + else + bcopy (lres->ai_addr, datagram_address[s].sa, lres->ai_addrlen); + } + #endif + contact = Fplist_put (contact, QCaddress, + conv_sockaddr_to_lisp (lres->ai_addr, lres->ai_addrlen)); + } + #ifdef HAVE_GETADDRINFO ! if (res != &ai) ! freeaddrinfo (res); #endif + immediate_quit = 0; + + /* Discard the unwind protect for closing S, if any. */ + specpdl_ptr = specpdl + count1; + + /* Unwind bind_polling_period and request_sigio. */ + unbind_to (count, Qnil); + if (s < 0) { /* If non-blocking got this far - and failed - assume non-blocking is not supported after all. This is probably a wrong assumption, but ! the normal blocking calls to open-network-stream handles this error ! better. */ ! if (is_non_blocking_client) return Qnil; errno = xerrno; ! if (is_server) ! report_file_error ("make server process failed", contact); ! else ! report_file_error ("make client process failed", contact); } ! #endif /* not TERM */ inch = s; outch = s; *************** *** 2149,2172 **** #endif #endif ! XPROCESS (proc)->childp = Fcons (host, Fcons (service, Qnil)); ! XPROCESS (proc)->command_channel_p = Qnil; ! XPROCESS (proc)->buffer = buffer; ! XPROCESS (proc)->sentinel = sentinel; ! XPROCESS (proc)->filter = filter; ! XPROCESS (proc)->command = Qnil; ! XPROCESS (proc)->pid = Qnil; ! XSETINT (XPROCESS (proc)->infd, inch); ! XSETINT (XPROCESS (proc)->outfd, outch); ! XPROCESS (proc)->status = Qrun; #ifdef NON_BLOCKING_CONNECT ! if (!NILP (non_blocking)) { /* We may get here if connect did succeed immediately. However, in that case, we still need to signal this like a non-blocking connection. */ ! XPROCESS (proc)->status = Qconnect; if (!FD_ISSET (inch, &connect_wait_mask)) { FD_SET (inch, &connect_wait_mask); --- 2874,2903 ---- #endif #endif ! p = XPROCESS (proc); ! ! p->childp = contact; ! p->buffer = buffer; ! p->sentinel = sentinel; ! p->filter = filter; ! p->log = Fplist_get (contact, QClog); ! if (tem = Fplist_get (contact, QCnoquery), !NILP (tem)) ! p->kill_without_query = Qt; ! if ((tem = Fplist_get (contact, QCstop), !NILP (tem))) ! p->command = Qt; ! p->pid = Qnil; ! XSETINT (p->infd, inch); ! XSETINT (p->outfd, outch); ! if (is_server && socktype == SOCK_STREAM) ! p->status = Qlisten; #ifdef NON_BLOCKING_CONNECT ! if (is_non_blocking_client) { /* We may get here if connect did succeed immediately. However, in that case, we still need to signal this like a non-blocking connection. */ ! p->status = Qconnect; if (!FD_ISSET (inch, &connect_wait_mask)) { FD_SET (inch, &connect_wait_mask); *************** *** 2175,2181 **** } else #endif ! if (!EQ (XPROCESS (proc)->filter, Qt)) { FD_SET (inch, &input_wait_mask); FD_SET (inch, &non_keyboard_wait_mask); --- 2906,2915 ---- } else #endif ! /* A server may have a client filter setting of Qt, but it must ! still listen for incoming connects unless it is stopped. */ ! if ((!EQ (p->filter, Qt) && !EQ (p->command, Qt)) ! || (EQ (p->status, Qlisten) && NILP (p->command))) { FD_SET (inch, &input_wait_mask); FD_SET (inch, &non_keyboard_wait_mask); *************** *** 2214,2220 **** else val = Qnil; } ! XPROCESS (proc)->decode_coding_system = val; if (!NILP (Vcoding_system_for_write)) val = Vcoding_system_for_write; --- 2948,2954 ---- else val = Qnil; } ! p->decode_coding_system = val; if (!NILP (Vcoding_system_for_write)) val = Vcoding_system_for_write; *************** *** 2237,2262 **** else val = Qnil; } ! XPROCESS (proc)->encode_coding_system = val; } if (!proc_decode_coding_system[inch]) proc_decode_coding_system[inch] = (struct coding_system *) xmalloc (sizeof (struct coding_system)); ! setup_coding_system (XPROCESS (proc)->decode_coding_system, proc_decode_coding_system[inch]); if (!proc_encode_coding_system[outch]) proc_encode_coding_system[outch] = (struct coding_system *) xmalloc (sizeof (struct coding_system)); ! setup_coding_system (XPROCESS (proc)->encode_coding_system, proc_encode_coding_system[outch]); ! XPROCESS (proc)->decoding_buf = make_uninit_string (0); ! XPROCESS (proc)->decoding_carryover = make_number (0); ! XPROCESS (proc)->encoding_buf = make_uninit_string (0); ! XPROCESS (proc)->encoding_carryover = make_number (0); ! XPROCESS (proc)->inherit_coding_system_flag = (NILP (buffer) || !inherit_process_coding_system ? Qnil : Qt); --- 2971,2996 ---- else val = Qnil; } ! p->encode_coding_system = val; } if (!proc_decode_coding_system[inch]) proc_decode_coding_system[inch] = (struct coding_system *) xmalloc (sizeof (struct coding_system)); ! setup_coding_system (p->decode_coding_system, proc_decode_coding_system[inch]); if (!proc_encode_coding_system[outch]) proc_encode_coding_system[outch] = (struct coding_system *) xmalloc (sizeof (struct coding_system)); ! setup_coding_system (p->encode_coding_system, proc_encode_coding_system[outch]); ! p->decoding_buf = make_uninit_string (0); ! p->decoding_carryover = make_number (0); ! p->encoding_buf = make_uninit_string (0); ! p->encoding_carryover = make_number (0); ! p->inherit_coding_system_flag = (NILP (buffer) || !inherit_process_coding_system ? Qnil : Qt); *************** *** 2295,2300 **** --- 3029,3042 ---- XSETINT (p->infd, -1); XSETINT (p->outfd, -1); + #ifdef DATAGRAM_SOCKETS + if (DATAGRAM_CHAN_P (inchannel)) + { + xfree (datagram_address[inchannel].sa); + datagram_address[inchannel].sa = 0; + datagram_address[inchannel].len = 0; + } + #endif chan_process[inchannel] = Qnil; FD_CLR (inchannel, &input_wait_mask); FD_CLR (inchannel, &non_keyboard_wait_mask); *************** *** 2411,2416 **** --- 3153,3353 ---- ? Qt : Qnil); } + /* Accept a connection for server process SERVER on CHANNEL. */ + + static int connect_counter = 0; + + static void + server_accept_connection (server, channel) + Lisp_Object server; + int channel; + { + Lisp_Object proc, caller, name, buffer; + Lisp_Object contact, host, service; + struct Lisp_Process *ps= XPROCESS (server); + struct Lisp_Process *p; + int s; + union u_sockaddr { + struct sockaddr sa; + struct sockaddr_in in; + #ifdef AF_LOCAL + struct sockaddr_un un; + #endif + } saddr; + int len = sizeof saddr; + + s = accept (channel, &saddr.sa, &len); + + if (s < 0) + { + int code = errno; + + if (code == EAGAIN) + return; + #ifdef EWOULDBLOCK + if (code == EWOULDBLOCK) + return; + #endif + + if (!NILP (ps->log)) + call3 (ps->log, server, Qnil, + concat3 (build_string ("accept failed with code"), + Fnumber_to_string (make_number (code)), + build_string ("\n"))); + return; + } + + connect_counter++; + + /* Setup a new process to handle the connection. */ + + /* Generate a unique identification of the caller, and build contact + information for this process. */ + host = Qt; + service = Qnil; + switch (saddr.sa.sa_family) + { + case AF_INET: + { + Lisp_Object args[5]; + unsigned char *ip = (unsigned char *)&saddr.in.sin_addr.s_addr; + args[0] = build_string ("%d.%d.%d.%d"); + args[1] = make_number (*ip++); + args[2] = make_number (*ip++); + args[3] = make_number (*ip++); + args[4] = make_number (*ip++); + host = Fformat (5, args); + service = make_number (ntohs (saddr.in.sin_port)); + + args[0] = build_string (" <%s:%d>"); + args[1] = host; + args[2] = service; + caller = Fformat (3, args); + } + break; + + #ifdef AF_LOCAL + case AF_LOCAL: + #endif + default: + caller = Fnumber_to_string (make_number (connect_counter)); + caller = concat3 (build_string (" <*"), caller, build_string ("*>")); + break; + } + + /* Create a new buffer name for this process if it doesn't have a + filter. The new buffer name is based on the buffer name or + process name of the server process concatenated with the caller + identification. */ + + if (!NILP (ps->filter) && !EQ (ps->filter, Qt)) + buffer = Qnil; + else + { + buffer = ps->buffer; + if (!NILP (buffer)) + buffer = Fbuffer_name (buffer); + else + buffer = ps->name; + if (!NILP (buffer)) + { + buffer = concat2 (buffer, caller); + buffer = Fget_buffer_create (buffer); + } + } + + /* Generate a unique name for the new server process. Combine the + server process name with the caller identification. */ + + name = concat2 (ps->name, caller); + proc = make_process (name); + + chan_process[s] = proc; + + #ifdef O_NONBLOCK + fcntl (s, F_SETFL, O_NONBLOCK); + #else + #ifdef O_NDELAY + fcntl (s, F_SETFL, O_NDELAY); + #endif + #endif + + p = XPROCESS (proc); + + /* Build new contact information for this setup. */ + contact = Fcopy_sequence (ps->childp); + contact = Fplist_put (contact, QChost, host); + if (!NILP (service)) + contact = Fplist_put (contact, QCservice, service); + contact = Fplist_put (contact, QCremote, + conv_sockaddr_to_lisp (&saddr.sa, len)); + #ifdef HAVE_GETSOCKNAME + len = sizeof saddr; + if (getsockname (channel, &saddr.sa, &len) == 0) + contact = Fplist_put (contact, QClocal, + conv_sockaddr_to_lisp (&saddr.sa, len)); + #endif + + p->childp = contact; + p->buffer = buffer; + p->sentinel = ps->sentinel; + p->filter = ps->filter; + p->command = Qnil; + p->pid = Qnil; + XSETINT (p->infd, s); + XSETINT (p->outfd, s); + p->status = Qrun; + + /* Client processes for accepted connections are not stopped initially. */ + if (!EQ (p->filter, Qt)) + { + FD_SET (s, &input_wait_mask); + FD_SET (s, &non_keyboard_wait_mask); + } + + if (s > max_process_desc) + max_process_desc = s; + + /* Setup coding system for new process based on server process. + This seems to be the proper thing to do, as the coding system + of the new process should reflect the settings at the time the + server socket was opened; not the current settings. */ + + p->decode_coding_system = ps->decode_coding_system; + p->encode_coding_system = ps->encode_coding_system; + + if (!proc_decode_coding_system[s]) + proc_decode_coding_system[s] + = (struct coding_system *) xmalloc (sizeof (struct coding_system)); + setup_coding_system (p->decode_coding_system, + proc_decode_coding_system[s]); + if (!proc_encode_coding_system[s]) + proc_encode_coding_system[s] + = (struct coding_system *) xmalloc (sizeof (struct coding_system)); + setup_coding_system (p->encode_coding_system, + proc_encode_coding_system[s]); + + p->decoding_buf = make_uninit_string (0); + p->decoding_carryover = make_number (0); + p->encoding_buf = make_uninit_string (0); + p->encoding_carryover = make_number (0); + + p->inherit_coding_system_flag + = (NILP (buffer) ? Qnil : ps->inherit_coding_system_flag); + + if (!NILP (ps->log)) + call3 (ps->log, server, proc, + concat3 (build_string ("accept from "), + (STRINGP (host) ? host : build_string ("-")), + build_string ("\n"))); + + if (p->sentinel) + exec_sentinel (proc, + concat3 (build_string ("open from "), + (STRINGP (host) ? host : build_string ("-")), + build_string ("\n"))); + } + /* This variable is different from waiting_for_input in keyboard.c. It is used to communicate to a lisp process-filter/sentinel (via the function Fwaiting_for_user_input_p below) whether emacs was waiting *************** *** 2909,2914 **** --- 3846,3858 ---- if (NILP (proc)) continue; + /* If this is a server stream socket, accept connection. */ + if (EQ (XPROCESS (proc)->status, Qlisten)) + { + server_accept_connection (proc, channel); + continue; + } + /* Read data from the process, starting with our buffered-ahead character if we have one. */ *************** *** 2983,2989 **** { struct Lisp_Process *p; struct sockaddr pname; ! socklen_t pnamelen = sizeof(pname); FD_CLR (channel, &connect_wait_mask); if (--num_pending_connects < 0) --- 3927,3933 ---- { struct Lisp_Process *p; struct sockaddr pname; ! int pnamelen = sizeof(pname); FD_CLR (channel, &connect_wait_mask); if (--num_pending_connects < 0) *************** *** 2999,3005 **** /* getsockopt(,,SO_ERROR,,) is said to hang on some systems. So only use it on systems where it is known to work. */ { ! socklen_t xlen = sizeof(xerrno); if (getsockopt(channel, SOL_SOCKET, SO_ERROR, &xerrno, &xlen)) xerrno = errno; } --- 3943,3949 ---- /* getsockopt(,,SO_ERROR,,) is said to hang on some systems. So only use it on systems where it is known to work. */ { ! int xlen = sizeof(xerrno); if (getsockopt(channel, SOL_SOCKET, SO_ERROR, &xerrno, &xlen)) xerrno = errno; } *************** *** 3028,3034 **** status_notify to do it later, it will read input from the process before calling the sentinel. */ exec_sentinel (proc, build_string ("open\n")); ! if (!EQ (p->filter, Qt)) { FD_SET (XINT (p->infd), &input_wait_mask); FD_SET (XINT (p->infd), &non_keyboard_wait_mask); --- 3972,3978 ---- status_notify to do it later, it will read input from the process before calling the sentinel. */ exec_sentinel (proc, build_string ("open\n")); ! if (!EQ (p->filter, Qt) && !EQ (p->command, Qt)) { FD_SET (XINT (p->infd), &input_wait_mask); FD_SET (XINT (p->infd), &non_keyboard_wait_mask); *************** *** 3106,3111 **** --- 4050,4056 ---- register int opoint; struct coding_system *coding = proc_decode_coding_system[channel]; int carryover = XINT (p->decoding_carryover); + int readmax = 1024; #ifdef VMS VMS_PROC_STUFF *vs, *get_vms_process_pointer(); *************** *** 3137,3154 **** bcopy (vs->inputBuffer, chars + carryover, nbytes); } #else /* not VMS */ ! chars = (char *) alloca (carryover + 1024); if (carryover) /* See the comment above. */ bcopy (XSTRING (p->decoding_buf)->data, chars, carryover); if (proc_buffered_char[channel] < 0) ! nbytes = emacs_read (channel, chars + carryover, 1024 - carryover); else { chars[carryover] = proc_buffered_char[channel]; proc_buffered_char[channel] = -1; ! nbytes = emacs_read (channel, chars + carryover + 1, 1023 - carryover); if (nbytes < 0) nbytes = 1; else --- 4082,4120 ---- bcopy (vs->inputBuffer, chars + carryover, nbytes); } #else /* not VMS */ ! ! #ifdef DATAGRAM_SOCKETS ! /* A datagram is one packet; allow at least 1500+ bytes of data ! corresponding to the typical Ethernet frame size. */ ! if (DATAGRAM_CHAN_P (channel)) ! { ! /* carryover = 0; */ /* Does carryover make sense for datagrams? */ ! readmax += 1024; ! } ! #endif ! ! chars = (char *) alloca (carryover + readmax); if (carryover) /* See the comment above. */ bcopy (XSTRING (p->decoding_buf)->data, chars, carryover); + #ifdef DATAGRAM_SOCKETS + /* We have a working select, so proc_buffered_char is always -1. */ + if (DATAGRAM_CHAN_P (channel)) + { + int len = datagram_address[channel].len; + nbytes = recvfrom (channel, chars + carryover, readmax - carryover, + 0, datagram_address[channel].sa, &len); + } + else + #endif if (proc_buffered_char[channel] < 0) ! nbytes = emacs_read (channel, chars + carryover, readmax - carryover); else { chars[carryover] = proc_buffered_char[channel]; proc_buffered_char[channel] = -1; ! nbytes = emacs_read (channel, chars + carryover + 1, readmax - 1 - carryover); if (nbytes < 0) nbytes = 1; else *************** *** 3614,3622 **** /* Send this batch, using one or more write calls. */ while (this > 0) { old_sigpipe = (SIGTYPE (*) ()) signal (SIGPIPE, send_process_trap); ! rv = emacs_write (XINT (XPROCESS (proc)->outfd), ! (char *) buf, this); signal (SIGPIPE, old_sigpipe); if (rv < 0) --- 4580,4599 ---- /* Send this batch, using one or more write calls. */ while (this > 0) { + int outfd = XINT (XPROCESS (proc)->outfd); old_sigpipe = (SIGTYPE (*) ()) signal (SIGPIPE, send_process_trap); ! #ifdef DATAGRAM_SOCKETS ! if (DATAGRAM_CHAN_P (outfd)) ! { ! rv = sendto (outfd, (char *) buf, this, ! 0, datagram_address[outfd].sa, ! datagram_address[outfd].len); ! if (rv < 0 && errno == EMSGSIZE) ! report_file_error ("sending datagram", Fcons (proc, Qnil)); ! } ! else ! #endif ! rv = emacs_write (outfd, (char *) buf, this); signal (SIGPIPE, old_sigpipe); if (rv < 0) *************** *** 4071,4080 **** DEFUN ("stop-process", Fstop_process, Sstop_process, 0, 2, 0, doc: /* Stop process PROCESS. May be process or name of one. ! See function `interrupt-process' for more details on usage. */) (process, current_group) Lisp_Object process, current_group; { #ifndef SIGTSTP error ("no SIGTSTP support"); #else --- 5048,5074 ---- DEFUN ("stop-process", Fstop_process, Sstop_process, 0, 2, 0, doc: /* Stop process PROCESS. May be process or name of one. ! See function `interrupt-process' for more details on usage. ! If PROCESS is a network process, inhibit handling of incoming traffic. */) (process, current_group) Lisp_Object process, current_group; { + #ifdef HAVE_SOCKETS + if (PROCESSP (process) && NETCONN_P (process)) + { + struct Lisp_Process *p; + + p = XPROCESS (process); + if (NILP (p->command) + && XINT (p->infd) >= 0) + { + FD_CLR (XINT (p->infd), &input_wait_mask); + FD_CLR (XINT (p->infd), &non_keyboard_wait_mask); + } + p->command = Qt; + return process; + } + #endif #ifndef SIGTSTP error ("no SIGTSTP support"); #else *************** *** 4085,4094 **** DEFUN ("continue-process", Fcontinue_process, Scontinue_process, 0, 2, 0, doc: /* Continue process PROCESS. May be process or name of one. ! See function `interrupt-process' for more details on usage. */) (process, current_group) Lisp_Object process, current_group; { #ifdef SIGCONT process_send_signal (process, SIGCONT, current_group, 0); #else --- 5079,5106 ---- DEFUN ("continue-process", Fcontinue_process, Scontinue_process, 0, 2, 0, doc: /* Continue process PROCESS. May be process or name of one. ! See function `interrupt-process' for more details on usage. ! If PROCESS is a network process, resume handling of incoming traffic. */) (process, current_group) Lisp_Object process, current_group; { + #ifdef HAVE_SOCKETS + if (PROCESSP (process) && NETCONN_P (process)) + { + struct Lisp_Process *p; + + p = XPROCESS (process); + if (EQ (p->command, Qt) + && XINT (p->infd) >= 0 + && (!EQ (p->filter, Qt) || EQ (p->status, Qlisten))) + { + FD_SET (XINT (p->infd), &input_wait_mask); + FD_SET (XINT (p->infd), &non_keyboard_wait_mask); + } + p->command = Qnil; + return process; + } + #endif #ifdef SIGCONT process_send_signal (process, SIGCONT, current_group, 0); #else *************** *** 4235,4240 **** --- 5247,5255 ---- Lisp_Object proc; struct coding_system *coding; + if (DATAGRAM_CONN_P (process)) + return process; + proc = get_process (process); coding = proc_encode_coding_system[XINT (XPROCESS (proc)->outfd)]; *************** *** 4619,4624 **** --- 5634,5641 ---- /* If process is still active, read any output that remains. */ while (! EQ (p->filter, Qt) && ! EQ (p->status, Qconnect) + && ! EQ (p->status, Qlisten) + && ! EQ (p->command, Qt) /* Network process not stopped. */ && XINT (p->infd) >= 0 && read_process_output (proc, XINT (p->infd)) > 0); *************** *** 4829,4834 **** --- 5846,5854 ---- } bzero (proc_decode_coding_system, sizeof proc_decode_coding_system); bzero (proc_encode_coding_system, sizeof proc_encode_coding_system); + #ifdef DATAGRAM_SOCKETS + bzero (datagram_address, sizeof datagram_address); + #endif } void *************** *** 4857,4863 **** staticpro (&Qconnect); Qfailed = intern ("failed"); staticpro (&Qfailed); ! Qlast_nonmenu_event = intern ("last-nonmenu-event"); staticpro (&Qlast_nonmenu_event); --- 5877,5920 ---- staticpro (&Qconnect); Qfailed = intern ("failed"); staticpro (&Qfailed); ! Qlisten = intern ("listen"); ! staticpro (&Qlisten); ! Qlocal = intern ("local"); ! staticpro (&Qlocal); ! ! QCname = intern (":name"); ! staticpro (&QCname); ! QCbuffer = intern (":buffer"); ! staticpro (&QCbuffer); ! QChost = intern (":host"); ! staticpro (&QChost); ! QCservice = intern (":service"); ! staticpro (&QCservice); ! QCfamily = intern (":family"); ! staticpro (&QCfamily); ! QClocal = intern (":local"); ! staticpro (&QClocal); ! QCremote = intern (":remote"); ! staticpro (&QCremote); ! QCserver = intern (":server"); ! staticpro (&QCserver); ! QCdatagram = intern (":datagram"); ! staticpro (&QCdatagram); ! QCnowait = intern (":nowait"); ! staticpro (&QCnowait); ! QCfilter = intern (":filter"); ! staticpro (&QCfilter); ! QCsentinel = intern (":sentinel"); ! staticpro (&QCsentinel); ! QClog = intern (":log"); ! staticpro (&QClog); ! QCnoquery = intern (":noquery"); ! staticpro (&QCnoquery); ! QCstop = intern (":stop"); ! staticpro (&QCstop); ! QCfeature = intern (":feature"); ! staticpro (&QCfeature); ! Qlast_nonmenu_event = intern ("last-nonmenu-event"); staticpro (&Qlast_nonmenu_event); *************** *** 4897,4910 **** defsubr (&Sset_process_window_size); defsubr (&Sset_process_inherit_coding_system_flag); defsubr (&Sprocess_inherit_coding_system_flag); ! defsubr (&Sprocess_kill_without_query); defsubr (&Sprocess_contact); defsubr (&Slist_processes); defsubr (&Sprocess_list); defsubr (&Sstart_process); #ifdef HAVE_SOCKETS ! defsubr (&Sopen_network_stream); #endif /* HAVE_SOCKETS */ defsubr (&Saccept_process_output); defsubr (&Sprocess_send_region); defsubr (&Sprocess_send_string); --- 5954,5972 ---- defsubr (&Sset_process_window_size); defsubr (&Sset_process_inherit_coding_system_flag); defsubr (&Sprocess_inherit_coding_system_flag); ! defsubr (&Sset_process_query_on_exit_flag); ! defsubr (&Sprocess_query_on_exit_flag); defsubr (&Sprocess_contact); defsubr (&Slist_processes); defsubr (&Sprocess_list); defsubr (&Sstart_process); #ifdef HAVE_SOCKETS ! defsubr (&Smake_network_process); #endif /* HAVE_SOCKETS */ + #ifdef DATAGRAM_SOCKETS + defsubr (&Sprocess_datagram_address); + defsubr (&Sset_process_datagram_address); + #endif defsubr (&Saccept_process_output); defsubr (&Sprocess_send_region); defsubr (&Sprocess_send_string); Index: lisp/ChangeLog =================================================================== RCS file: /cvs/emacs/lisp/ChangeLog,v retrieving revision 1.3574 diff -c -r1.3574 ChangeLog *** lisp/ChangeLog 13 Mar 2002 17:41:53 -0000 1.3574 --- lisp/ChangeLog 13 Mar 2002 23:14:01 -0000 *************** *** 1,3 **** --- 1,24 ---- + 2002-03-13 Kim F. Storm <storm@cua.dk> + + The following changes are related to the enhanced network process + support. + + * simple.el (clone-process): Use make-network-process to clone + network processes. Get command list via (process-contact ... t). + Use set-process-query-on-exit-flag and process-query-on-exit-flag + instead of process-kill-without-query. + (open-network-stream): Replaces C-version from process.c. + (open-network-stream-nowait, open-network-stream-server): New + functions. + (process-kill-without-query): Replaces C-version from process.c. + + * files.el (save-buffers-kill-emacs): Also check for active server + processes. Use process-query-on-exit-flag. Only list processes + which has the query-on-exit flag set in connection with user query. + + * shadowfile.el (shadow-save-buffers-kill-emacs): Also check for + active server processes. Use process-query-on-exit-flag. + 2002-03-13 Francesco Potorti` <pot@gnu.org> * progmodes/etags.el (tag-exact-file-name-match-p) Index: lisp/simple.el =================================================================== RCS file: /cvs/emacs/lisp/simple.el,v retrieving revision 1.524 diff -c -r1.524 simple.el *** lisp/simple.el 9 Mar 2002 09:05:08 -0000 1.524 --- lisp/simple.el 13 Mar 2002 23:14:03 -0000 *************** *** 3932,3948 **** (setq newname (substring newname 0 (match-beginning 0)))) (when (memq (process-status process) '(run stop open)) (let* ((process-connection-type (process-tty-name process)) - (old-kwoq (process-kill-without-query process nil)) (new-process (if (memq (process-status process) '(open)) ! (apply 'open-network-stream newname ! (if (process-buffer process) (current-buffer)) ! (process-contact process)) (apply 'start-process newname (if (process-buffer process) (current-buffer)) (process-command process))))) ! (process-kill-without-query new-process old-kwoq) ! (process-kill-without-query process old-kwoq) (set-process-inherit-coding-system-flag new-process (process-inherit-coding-system-flag process)) (set-process-filter new-process (process-filter process)) --- 3932,3949 ---- (setq newname (substring newname 0 (match-beginning 0)))) (when (memq (process-status process) '(run stop open)) (let* ((process-connection-type (process-tty-name process)) (new-process (if (memq (process-status process) '(open)) ! (let ((args (process-contact process t))) ! (setq args (plist-put args :name newname)) ! (setq args (plist-put args :buffer ! (if (process-buffer process) (current-buffer)))) ! (apply 'make-network-process args)) (apply 'start-process newname (if (process-buffer process) (current-buffer)) (process-command process))))) ! (set-process-query-on-exit-flag ! new-process (process-query-on-exit-flag process)) (set-process-inherit-coding-system-flag new-process (process-inherit-coding-system-flag process)) (set-process-filter new-process (process-filter process)) *************** *** 4202,4207 **** --- 4203,4290 ---- (message "Delete key deletes %s" (if normal-erase-is-backspace "forward" "backward")))) + + ;;; make-network-process wrappers + + (if (fboundp 'make-network-process) + (progn + + (defun open-network-stream (name buffer host service) + "Open a TCP connection for a service to a host. + Returns a subprocess-object to represent the connection. + Input and output work as for subprocesses; `delete-process' closes it. + Args are NAME BUFFER HOST SERVICE. + NAME is name for process. It is modified if necessary to make it unique. + BUFFER is the buffer (or buffer-name) to associate with the process. + Process output goes at end of that buffer, unless you specify + an output stream or filter function to handle the output. + BUFFER may be also nil, meaning that this process is not associated + with any buffer + Third arg is name of the host to connect to, or its IP address. + Fourth arg SERVICE is name of the service desired, or an integer + specifying a port number to connect to." + (make-network-process :name name :buffer buffer + :host host :service service)) + + (defun open-network-stream-nowait (name buffer host service &optional sentinel filter) + "Initiate connection to a TCP connection for a service to a host. + It returns nil if non-blocking connects are not supported; otherwise, + it returns a subprocess-object to represent the connection. + + This function is similar to `open-network-stream', except that this + function returns before the connection is established. When the + connection is completed, the sentinel function will be called with + second arg matching `open' (if successful) or `failed' (on error). + + Args are NAME BUFFER HOST SERVICE SENTINEL FILTER. + NAME, BUFFER, HOST, and SERVICE are as for `open-network-stream'. + Optional args, SENTINEL and FILTER specifies the sentinel and filter + functions to be used for this network stream." + (if (make-network-process :feature :nowait t) + (make-network-process :name name :buffer buffer :nowait t + :host host :service service + :filter filter :sentinel sentinel))) + + (defun open-network-stream-server (name buffer service &optional sentinel filter) + "Create a network server process for a TCP service. + It returns nil if server processes are not supported; otherwise, + it returns a subprocess-object to represent the server. + + When a client connects to the specified service, a new subprocess + is created to handle the new connection, and the sentinel function + is called for the new process. + + Args are NAME BUFFER SERVICE SENTINEL FILTER. + NAME is name for the server process. Client processes are named by + appending the ip-address and port number of the client to NAME. + BUFFER is the buffer (or buffer-name) to associate with the server + process. Client processes will not get a buffer if a process filter + is specified or BUFFER is nil; otherwise, a new buffer is created for + the client process. The name is similar to the process name. + Third arg SERVICE is name of the service desired, or an integer + specifying a port number to connect to. It may also be t to selected + an unused port number for the server. + Optional args, SENTINEL and FILTER specifies the sentinel and filter + functions to be used for the client processes; the server process + does not use these function." + (if (make-network-process :feature :server t) + (make-network-process :name name :buffer buffer + :service service :server t :noquery t))) + + )) ;; (fboundp 'make-network-process) + + + ;; compatibility + + (defun process-kill-without-query (process &optional flag) + "Say no query needed if PROCESS is running when Emacs is exited. + Optional second argument if non-nil says to require a query. + Value is t if a query was formerly required. + New code should not use this function; use `process-query-on-exit-flag' + or `set-process-query-on-exit-flag' instead." + (let ((old (process-query-on-exit-flag process))) + (set-process-query-on-exit-flag process nil) + old)) ;;; Misc Index: lisp/files.el =================================================================== RCS file: /cvs/emacs/lisp/files.el,v retrieving revision 1.552 diff -c -r1.552 files.el *** lisp/files.el 6 Mar 2002 18:19:43 -0000 1.552 --- lisp/files.el 13 Mar 2002 23:14:04 -0000 *************** *** 3808,3821 **** (let ((processes (process-list)) active) (while processes ! (and (memq (process-status (car processes)) '(run stop open)) ! (let ((val (process-kill-without-query (car processes)))) ! (process-kill-without-query (car processes) val) ! val) (setq active t)) (setq processes (cdr processes))) (or (not active) ! (list-processes) (yes-or-no-p "Active processes exist; kill them and exit anyway? ")))) ;; Query the user for other things, perhaps. (run-hook-with-args-until-failure 'kill-emacs-query-functions) --- 3808,3819 ---- (let ((processes (process-list)) active) (while processes ! (and (memq (process-status (car processes)) '(run stop open listen)) ! (process-query-on-exit-flag (car processes)) (setq active t)) (setq processes (cdr processes))) (or (not active) ! (list-processes t) (yes-or-no-p "Active processes exist; kill them and exit anyway? ")))) ;; Query the user for other things, perhaps. (run-hook-with-args-until-failure 'kill-emacs-query-functions) Index: lisp/shadowfile.el =================================================================== RCS file: /cvs/emacs/lisp/shadowfile.el,v retrieving revision 1.17 diff -c -r1.17 shadowfile.el *** lisp/shadowfile.el 16 Jul 2001 12:22:59 -0000 1.17 --- lisp/shadowfile.el 13 Mar 2002 23:14:05 -0000 *************** *** 775,784 **** (let ((processes (process-list)) active) (while processes ! (and (memq (process-status (car processes)) '(run stop open)) ! (let ((val (process-kill-without-query (car processes)))) ! (process-kill-without-query (car processes) val) ! val) (setq active t)) (setq processes (cdr processes))) (or (not active) --- 775,782 ---- (let ((processes (process-list)) active) (while processes ! (and (memq (process-status (car processes)) '(run stop open listen)) ! (process-query-on-exit-flag (car processes)) (setq active t)) (setq processes (cdr processes))) (or (not active) -- Kim F. Storm <storm@cua.dk> http://www.cua.dk _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Final(?) patch for server sockets and datagram (UDP) support. 2002-03-13 23:19 ` Final(?) " Kim F. Storm @ 2002-03-14 0:50 ` Al Petrofsky 2002-03-14 9:30 ` Kim F. Storm 2002-03-14 12:42 ` Richard Stallman 2002-03-17 22:02 ` I have installed the " Kim F. Storm 1 sibling, 2 replies; 47+ messages in thread From: Al Petrofsky @ 2002-03-14 0:50 UTC (permalink / raw) Cc: emacs-devel, helmut It's always been a bit confusing that emacs uses the term process for sockets that have no associated process. That confusion will get a little worse now that emacs processes will include server and datagram sockets, which don't even share processes' stream-like nature. I'm not saying we should rename everything now to fix this, but I think it would help if the start of the "make-network-process" doc string immediately disclaimed any relationship to a unix process. > :host HOST -- HOST is name of the host to connect to, or its IP > address. If specified for a server process, only clients on that host > may connect. The symbol `local' specifies the local host. Don't you mean something like "If specified for a server process, it must be a valid name or address for the local host, and only clients connecting to that address will get through"? > :local ADDRESS -- ADDRESS is the local address used for the > connection. This parameter is ignored when opening a client process. > When specified for a server process, the HOST and SERVICE are ignored. > > :remote ADDRESS -- ADDRESS is the remote partner's address for the > connection. This parameter is ignored when opening a server process. > When specified for a client process, the HOST and SERVICE are ignored. These seemed pointless until I read the process-contact doc, and the NEWS. (The format of the address wasn't documented in either function's doc string.) I don't think it's a good idea to add two more arguments to make-network-process just so that process-contact is easier to document. Is there some other point? -al _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Final(?) patch for server sockets and datagram (UDP) support. 2002-03-14 0:50 ` Al Petrofsky @ 2002-03-14 9:30 ` Kim F. Storm 2002-03-14 12:42 ` Richard Stallman 1 sibling, 0 replies; 47+ messages in thread From: Kim F. Storm @ 2002-03-14 9:30 UTC (permalink / raw) Cc: emacs-devel, helmut Al Petrofsky <al@petrofsky.org> writes: > It's always been a bit confusing that emacs uses the term process for > sockets that have no associated process. That confusion will get a > little worse now that emacs processes will include server and datagram > sockets, which don't even share processes' stream-like nature. I agree that the "tight" coupling with processes sometimes makes it quite hard to describe the functionality in a natural way. It would be easier just to say "datagram socket" than "a datagram type network process" or some such. However, even with datagram sockets and server sockets, we still use all (or most) of the *-process-* API functions to control, monitor, and send and receive data on the socket, so from that POV, I found that naming the function make-network-PROCESS was really the best choice. > > I'm not saying we should rename everything now to fix this, but I > think it would help if the start of the "make-network-process" doc > string immediately disclaimed any relationship to a unix process. > > > :host HOST -- HOST is name of the host to connect to, or its IP > > address. If specified for a server process, only clients on that host > > may connect. The symbol `local' specifies the local host. > > Don't you mean something like "If specified for a server process, it > must be a valid name or address for the local host, and only clients > connecting to that address will get through"? Exactly! Thanks for spotting this. > > > :local ADDRESS -- ADDRESS is the local address used for the > > connection. This parameter is ignored when opening a client process. > > When specified for a server process, the HOST and SERVICE are ignored. > > > > :remote ADDRESS -- ADDRESS is the remote partner's address for the > > connection. This parameter is ignored when opening a server process. > > When specified for a client process, the HOST and SERVICE are ignored. > > These seemed pointless until I read the process-contact doc, and the > NEWS. (The format of the address wasn't documented in either > function's doc string.) I don't think it's a good idea to add two > more arguments to make-network-process just so that process-contact is > easier to document. Is there some other point? Instead of :family/:host/:service, you can actually use :local ADDR when setting up a server process (when :server t is specified), and likewise :remote ADDR to setup a client process. I will add a description of the ADDRESS format to the doc string. This means that if you use "clone-process" to clone a network process, i.e. (apply 'make-network-process (process-contact old t)), it will use the same IP addres and PORT number as the original process (taken from :local or :remote), rather than looking up the hostname and service again [which may in some obscure cases result in connecting to another host or port]! BTW, I've found a small bug related to this in my patch: When a server process accepts a connection and creates a new process for it, it must set the :server parameter to nil in the new process' contact list. Here is an updated DOC string for make-network-process: doc: /* Create and return a network server or client process. Input and output work as for subprocesses; `delete-process' closes it. Arguments are specified as keyword/argument pairs. The following arguments are defined: :name NAME -- NAME is name for process. It is modified if necessary to make it unique. :buffer BUFFER -- BUFFER is the buffer (or buffer-name) to associate with the process. Process output goes at end of that buffer, unless you specify an output stream or filter function to handle the output. BUFFER may be also nil, meaning that this process is not associated with any buffer. :host HOST -- HOST is name of the host to connect to, or its IP address. The symbol `local' specifies the local host. If specified for a server process, it must be a valid name or address for the local host, and only clients connecting to that address will be accepted. :service SERVICE -- SERVICE is name of the service desired, or an integer specifying a port number to connect to. If SERVICE is t, a random port number is selected for the server. :family FAMILY -- FAMILY is the address (and protocol) family for the service specified by HOST and SERVICE. The default address family is Inet (or IPv4) for the host and port number specified by HOST and SERVICE. Other address families supported are: local -- for a local (i.e. UNIX) address specified by SERVICE. :local ADDRESS -- ADDRESS is the local address used for the connection. This parameter is ignored when opening a client process. When specified for a server process, the FAMILY, HOST and SERVICE args are ignored. :remote ADDRESS -- ADDRESS is the remote partner's address for the connection. This parameter is ignored when opening a server process. When specified for a client process, the FAMILY, HOST, and SERVICE are ignored. The format of ADDRESS depends on the address family: - An IPv4 address is represented as an vector of integers [A B C D P] corresponding to numeric IP address A.B.C.D and port number P. - A local address is represented as a string with the address in the local address space. - An "unsupported family" address is represented by a cons (F . AV) where F is the family number and AV is a vector containing the socket address data with one element per address data byte. Do not rely on this format in portable code, as it may depend on implementation defined constants, data sizes, and data structure alignment. :datagram BOOL -- Create a datagram type connection if BOOL is non-nil. Default is a stream type connection. :nowait BOOL -- If BOOL is non-nil for a stream type client process, return without waiting for the connection to complete; instead, the sentinel function will be called with second arg matching "open" (if successful) or "failed" when the connect completes. Default is to use a blocking connect (i.e. wait) for stream type connections. :noquery BOOL -- Query the user unless BOOL is non-nil, and process is running when emacs is exited. :stop BOOL -- Start process in the `stopped' state if BOOL non-nil. In the stopped state, a server process does not accept new connections, and a client process does not handle incoming traffic. The stopped state is cleared by `continue-process' and set by `stop-process'. :filter FILTER -- Install FILTER as the process filter. :sentinel SENTINEL -- Install SENTINEL as the process sentinel. :log LOG -- Install LOG as the server process log function. This function is called as when the server accepts a network connection from a client. The arguments are SERVER, CLIENT, and MESSAGE, where SERVER is the server process, CLIENT is the new process for the connection, and MESSAGE is a string. :server BOOL -- if BOOL is non-nil, create a server process for the specified FAMILY, SERVICE, and connection type (stream or datagram). Default is a client process. A server process will listen for and accept connections from clients. When a client connection is accepted, a new network process is created for the connection with the following parameters: - The client's process name is constructed by concatenating the server process' NAME and a client identification string. - If the FILTER argument is non-nil, the client process will not get a separate process buffer; otherwise, the client's process buffer is a newly created buffer named after the server process' BUFFER name or process NAME concatenated with the client identification string. - The connection type and the process filter and sentinel parameters are inherited from the server process' TYPE, FILTER and SENTINEL. - The client process' contact info is set according to the client's addressing information (typically an IP address and a port number). Notice that the FILTER and SENTINEL args are never used directly by the server process. Also, the BUFFER argument is not used directly by the server process, but via `network-server-log-function' hook, a log of the accepted (and failed) connections may be recorded in the server process' buffer. The following special call returns t iff a given KEY VALUE pair is supported on this system: (make-network-process :feature KEY VALUE) */) -- Kim F. Storm <storm@cua.dk> http://www.cua.dk _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Final(?) patch for server sockets and datagram (UDP) support. 2002-03-14 0:50 ` Al Petrofsky 2002-03-14 9:30 ` Kim F. Storm @ 2002-03-14 12:42 ` Richard Stallman 2002-03-14 13:35 ` Kim F. Storm 1 sibling, 1 reply; 47+ messages in thread From: Richard Stallman @ 2002-03-14 12:42 UTC (permalink / raw) Cc: storm, emacs-devel, helmut It's always been a bit confusing that emacs uses the term process for sockets that have no associated process. That confusion will get a little worse now that emacs processes will include server and datagram sockets, which don't even share processes' stream-like nature. In principle, you are right, but practically speaking it would be a lot of work to change this. I see no simple name word that does clearly fit process-or-stream-or-network-port, so any alternative would have its own drawbacks, even if they are lesser drawbacks. Meanwhile, working with process objects is a fairly advanced part of Emacs Lisp programming. I think the people who write such programs can cope with a suboptimal name. I'm not saying we should rename everything now to fix this, but I think it would help if the start of the "make-network-process" doc string immediately disclaimed any relationship to a unix process. That would be a good idea. _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Final(?) patch for server sockets and datagram (UDP) support. 2002-03-14 12:42 ` Richard Stallman @ 2002-03-14 13:35 ` Kim F. Storm 0 siblings, 0 replies; 47+ messages in thread From: Kim F. Storm @ 2002-03-14 13:35 UTC (permalink / raw) Cc: emacs-devel, helmut Richard Stallman <rms@gnu.org> writes: > > I'm not saying we should rename everything now to fix this, but I > think it would help if the start of the "make-network-process" doc > string immediately disclaimed any relationship to a unix process. > > That would be a good idea. > I've added this to the doc string: In emacs, network connections are represented by process objects, so input and output work as for subprocesses and `delete-process' closes a network connection. However, a network process has no process id, it cannot be signalled, and the status codes are different from normal processes. -- Kim F. Storm <storm@cua.dk> http://www.cua.dk _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* I have installed the patch for server sockets and datagram (UDP) support. 2002-03-13 23:19 ` Final(?) " Kim F. Storm 2002-03-14 0:50 ` Al Petrofsky @ 2002-03-17 22:02 ` Kim F. Storm 1 sibling, 0 replies; 47+ messages in thread From: Kim F. Storm @ 2002-03-17 22:02 UTC (permalink / raw) I installed my changes in CVS. See etc/NEWS and doc for make-network-process for details. -- Kim F. Storm <storm@cua.dk> http://www.cua.dk _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: New patch for server sockets and datagram (UDP) support. 2002-03-07 10:56 ` Kim F. Storm 2002-03-07 11:39 ` Alex Schroeder @ 2002-03-07 15:18 ` Helmut Eller 2002-03-07 16:09 ` Kim F. Storm 1 sibling, 1 reply; 47+ messages in thread From: Helmut Eller @ 2002-03-07 15:18 UTC (permalink / raw) Cc: emacs-devel storm@cua.dk (Kim F. Storm) writes: > What do you think? You are adding many features at the same time; it's a bit hard to see what your problem actually is :-) I think it's a _very good_ idea to rename open-network-stream to something different, because this frees you from being backward compatible. Have you considered to use keyword arguments, e.g., in the spirit of the make-socket function found in Allegro CL? <http://www.franz.com/support/documentation/5.0.1/doc/cl/socket.htm> It's probably a pain to parse keyword arguments in C, but it frees you from overloading positional arguments in an unnatural way. It would also be quite nice to use from Lisp. Another advantage is that you could add new arguments without much backward compatibility restrictions. Another point: if someone wants to bind a socket to a specific interface he must be able to specify the IP address, the hostname is IMHO not sufficient for this. Any ideas for this problem? I propose to make gethostbyname and related functions available to Lisp. IP addresses could be represented by vectors of 4 bytes (it's a pity that 32bit don't fit into a ELisp fixnum). Yet another point: please, please, please make accept-connection a separate function. Then one could make a _blocking_ accept; also accept with a timeout argument would be possible. Would this add any implementation complexity? Helmut. _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: New patch for server sockets and datagram (UDP) support. 2002-03-07 15:18 ` New " Helmut Eller @ 2002-03-07 16:09 ` Kim F. Storm 2002-03-07 17:32 ` Helmut Eller 0 siblings, 1 reply; 47+ messages in thread From: Kim F. Storm @ 2002-03-07 16:09 UTC (permalink / raw) Cc: emacs-devel Helmut Eller <helmut@xaital.km4u.net> writes: Thanks Helmut, I appreciate your comments. See below. > storm@cua.dk (Kim F. Storm) writes: > > > What do you think? > > You are adding many features at the same time; it's a bit hard to see > what your problem actually is :-) I agree -- but doing it all in one big step does reveal more of the API related problems than doing it step by step (as I did with the non-blocking argument to open-network-stream). > I think it's a _very good_ idea to rename open-network-stream to > something different, because this frees you from being backward > compatible. That's right. It definitely has advantages to do that. > > Have you considered to use keyword arguments, e.g., in the spirit of > the make-socket function found in Allegro CL? > <http://www.franz.com/support/documentation/5.0.1/doc/cl/socket.htm> > I have considered doing that, but I don't think there is any tradition for doing that in emacs lisp built-in functions. But I agree that it gives more flexibility in combining various options, so if that is ok with the rest of the developers, I will look into that path. > It's probably a pain to parse keyword arguments in C, but it frees you > from overloading positional arguments in an unnatural way. It would > also be quite nice to use from Lisp. Another advantage is that you > could add new arguments without much backward compatibility > restrictions. It still has the problem of what to do with unknown arguments -- but at least it will not trigger the debugger due to an incorrect number of arguments. > > Another point: if someone wants to bind a socket to a specific > interface he must be able to specify the IP address, the hostname is > IMHO not sufficient for this. Any ideas for this problem? I'll have to think about that, but again keyword arguments could be helpful here. > I propose > to make gethostbyname and related functions available to Lisp. IP > addresses could be represented by vectors of 4 bytes (it's a pity that > 32bit don't fit into a ELisp fixnum). > Not that I object to this in general, but for what purpose? > Yet another point: please, please, please make accept-connection a > separate function. Then one could make a _blocking_ accept; also > accept with a timeout argument would be possible. I understand that you want to serialize the connections. Why is it necessary to do that? Each connection gets its own process (and buffer etc), so they should be able to co-exist. If it is a matter of resources, then I think there are other ways we could consider to handle this (e.g. a `:max-conn 5' parameter) to limit the number of concurrent servers running. Or we could make the `stop-process' function applicable to server sockets to stop/start listening. AFAICS, your accept-connection call doesn't really accept anything; it just allows the server to accept a (one) connection if one is received. If you don't want to accept more connections, you can delete the server process in the sentinel. And you can start a timer to cancel the accept (by calling delete-process) if a connection hasn't been received. The sentinel can cancel (or restart) that timer if a connection is accepted. > Would this add any > implementation complexity? Yes. (I'll elaborate more on this when I understand what you are trying to accomplish). > > Helmut. > -- Kim F. Storm <storm@cua.dk> http://www.cua.dk _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: New patch for server sockets and datagram (UDP) support. 2002-03-07 16:09 ` Kim F. Storm @ 2002-03-07 17:32 ` Helmut Eller 2002-03-07 23:58 ` Kim F. Storm 0 siblings, 1 reply; 47+ messages in thread From: Helmut Eller @ 2002-03-07 17:32 UTC (permalink / raw) Cc: emacs-devel no-spam@cua.dk (Kim F. Storm) writes: > > It's probably a pain to parse keyword arguments in C, but it frees you > > from overloading positional arguments in an unnatural way. It would > > also be quite nice to use from Lisp. Another advantage is that you > > could add new arguments without much backward compatibility > > restrictions. > > It still has the problem of what to do with unknown arguments -- > but at least it will not trigger the debugger due to an incorrect > number of arguments. Raise an "invalid keyword" error. That's the pain it was talking about. > > I propose > > to make gethostbyname and related functions available to Lisp. IP > > addresses could be represented by vectors of 4 bytes (it's a pity that > > 32bit don't fit into a ELisp fixnum). > > > > Not that I object to this in general, but for what purpose? Because there is currently no way to get the IP address(es) of the current host. It MAY also simplify the C level implementation, because you could require that e.g. the SERVICE argument is actually a port number and not a string or a number; similar for the HOST argument. > > Yet another point: please, please, please make accept-connection a > > separate function. Then one could make a _blocking_ accept; also > > accept with a timeout argument would be possible. > > I understand that you want to serialize the connections. > Why is it necessary to do that? It's probably not necessary in 95% of all uses, but it may be hard to change later if you hardwire the current behavior. And, about every socket interface I have seen so far has a separate accept function. Here is a somewhat artificial example. I would like to control Emacs from an external Common Lisp program. I would also like to control the Common Lisp program from Emacs. Do to this I implemented a very naive rpc mechanism: both sides send their commands via a socket connection to the peer, the peer evals the command and sends the result back to the client. Now the problem: if the command include a recursive/nested calls to caller, serialization is an issue. The cleanest thing is to accept exactly one connection at a time. Of course, it is not very hard to come up with different solution, but accept-connection makes this particular use elegant and reliable. > And you can start a timer to cancel the accept (by calling > delete-process) if a connection hasn't been received. > The sentinel can cancel (or restart) that timer if a > connection is accepted. Yes I could. But a blocking accept is easier to use and more reliable. If you think a separate function is to much trouble, leave it out. Helmut. _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: New patch for server sockets and datagram (UDP) support. 2002-03-07 17:32 ` Helmut Eller @ 2002-03-07 23:58 ` Kim F. Storm 2002-03-08 7:38 ` Helmut Eller 0 siblings, 1 reply; 47+ messages in thread From: Kim F. Storm @ 2002-03-07 23:58 UTC (permalink / raw) Cc: emacs-devel Helmut Eller <helmut@xaital.km4u.net> writes: > > > It's probably a pain to parse keyword arguments in C, but it frees you > > > from overloading positional arguments in an unnatural way. It would > > > also be quite nice to use from Lisp. Another advantage is that you > > > could add new arguments without much backward compatibility > > > restrictions. Below is the doc-string for make-network-process which is a keyword based replacement for the built-in open-network-stream. I already implemented most of the code handling this, and it looks quite clean compared to the previous version. IMO, this it by far the best proposal for the API so far. The lisp-level wrappers will look like this: (defun open-network-stream (name buffer host service) "..." (make-network-process :name name :buffer buffer :host host :service service)) (defun open-network-stream-nowait (name buffer host service &optional sentinel filter) "..." (if (make-network-process :feature :nowait t) (make-network-process :name name :buffer buffer :nowait t :host host :service service :filter filter :sentinel sentinel))) (defun open-stream-server (name buffer service &optional host sentinel filter) "..." (make-network-process :name name :buffer buffer :server t :host host :service service)) DEFUN ("make-network-process", Fmake_network_process, Smake_network_process, 0, MANY, 0, doc: /* Create and return a network server or client process. Input and output work as for subprocesses; `delete-process' closes it. Arguments are specified as keyword/argument pairs. The following arguments are defined: :name NAME -- NAME is name for process. It is modified if necessary to make it unique. :buffer BUFFER -- BUFFER is the buffer (or buffer-name) to associate with the process. Process output goes at end of that buffer, unless you specify an output stream or filter function to handle the output. BUFFER may be also nil, meaning that this process is not associated with any buffer. :host HOST -- HOST is name of the host to connect to, or its IP address. If specified for a server process, only clients on that host may connect. The symbol `local' specifies the local host. :service SERVICE -- SERVICE is name of the service desired, or an integer specifying a port number to connect to. If port number is 0, a random port number is selected for the :family FAMILY -- FAMILY is the address (and protocol) family for the service specified by HOST and SERVICE. The default address family is Inet (or IPv4) for the host and port number specified by HOST and SERVICE. Other address families supported are: local -- for a local (i.e. UNIX) address specified by SERVICE. :datagram BOOL -- Create a datagram type connection if BOOL is non-nil. Default is a stream type connection. :nowait BOOL -- Don't wait for client process to complete the connection to the server if BOOL is non-nil; instead, the sentinel function will be called with second matching "open" (if successful) or "failed" when the connect completes. Default is to use a blocking connect. :filter FILTER -- Install FILTER as the process filter. :sentinel SENTINEL -- Install SENTINEL as the process sentinel. :server BOOL -- if BOOL is non-nil, create a server process for the specified FAMILY, SERVICE, and connection type (stream or datagram). Default is a client process. A server process will listen for and accept connections from clients. When a client connection is accepted, a new network process is created for the connection with the following parameters: - The client's process name is constructed by concatenating the server process' NAME and a client identification string. - If the FILTER argument is non-nil, the client process will not get a separate process buffer; otherwise, the client's process buffer is a newly created buffer named after the server process' BUFFER name or process NAME concatenated with the client identification string. - The connection type and the process filter and sentinel parameters are inherited from the server process' TYPE, FILTER and SENTINEL. - The client process' contact info is set according to the client's addressing information (typically an IP address and a port number). Notice that the FILTER and SENTINEL args are never used directly by the server process. Also, the BUFFER argument is not used directly by the server process, but via `network-server-log-function' hook, a log of the accepted (and failed) connections may be recorded in the server process' buffer. The following special call returns t iff a given KEY VALUE pair is supported on this system: (make-network-process :feature KEY VALUE) */) (nargs, args) int nargs; Lisp_Object *args; > > > > I propose > > > to make gethostbyname and related functions available to Lisp. IP > > > addresses could be represented by vectors of 4 bytes (it's a pity that > > > 32bit don't fit into a ELisp fixnum). > > > > > > > Not that I object to this in general, but for what purpose? > > Because there is currently no way to get the IP address(es) of the > current host. It MAY also simplify the C level implementation, > because you could require that e.g. the SERVICE argument is actually a > port number and not a string or a number; similar for the HOST > argument. > I don't think this is necessary, so I'll leave that for a future enhancement. To restrict connections to the local host, I suggest using either "localhost" for the HOST or as local (UNIX) socket. > > > Yet another point: please, please, please make accept-connection a > > > separate function. Then one could make a _blocking_ accept; also > > > accept with a timeout argument would be possible. > > > > I understand that you want to serialize the connections. > > Why is it necessary to do that? > > It's probably not necessary in 95% of all uses, but it may be hard to > change later if you hardwire the current behavior. And, about every > socket interface I have seen so far has a separate accept function. > > Here is a somewhat artificial example. I would like to control Emacs > from an external Common Lisp program. I would also like to control > the Common Lisp program from Emacs. Do to this I implemented a very > naive rpc mechanism: both sides send their commands via a socket > connection to the peer, the peer evals the command and sends the > result back to the client. Now the problem: if the command include a > recursive/nested calls to caller, serialization is an issue. The > cleanest thing is to accept exactly one connection at a time. Of > course, it is not very hard to come up with different solution, but > accept-connection makes this particular use elegant and reliable. > Ok, but as you mention yourself, this is the exception, so it shouldn't be the standard behaviour. I will take a look at using stop-process and start-process to temporarily inhibit a server socket from accepting connections. If you can use stop-process in the sentinel, this seems to be a cleaner solution than having to (re-)enable the server by calling accept-connection. -- Kim F. Storm <storm@cua.dk> http://www.cua.dk _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: New patch for server sockets and datagram (UDP) support. 2002-03-07 23:58 ` Kim F. Storm @ 2002-03-08 7:38 ` Helmut Eller 2002-03-08 9:13 ` Kim F. Storm 2002-03-08 21:07 ` Richard Stallman 0 siblings, 2 replies; 47+ messages in thread From: Helmut Eller @ 2002-03-08 7:38 UTC (permalink / raw) Cc: emacs-devel storm@cua.dk (Kim F. Storm) writes: > Below is the doc-string for make-network-process which is a keyword > based replacement for the built-in open-network-stream. I already > implemented most of the code handling this, and it looks quite clean > compared to the previous version. > > IMO, this it by far the best proposal for the API so far. Yes. This looks very good now. > :nowait BOOL -- Don't wait for client process to complete the > connection to the server if BOOL is non-nil; instead, the sentinel > function will be called with second matching "open" (if successful) or > "failed" when the connect completes. Default is to use a blocking > connect. I would prefer :wait that defaults to t. Is shorter and avoids the negation. > Notice that the FILTER and SENTINEL args are never used directly by > the server process. Also, the BUFFER argument is not used directly by > the server process, but via `network-server-log-function' hook, a log > of the accepted (and failed) connections may be recorded in the server > process' buffer. network-server-log-function is a global variable. This may cause problems when used by independent packages. Is it a problem to make this an attribute of the server process? E.g. as :log-function argument? > The following special call returns t iff a given KEY VALUE > pair is supported on this system: > (make-network-process :feature KEY VALUE) */) Hmm... this looks a bit strange. Can you give some examples? Especially, what do you supply as VALUE? > > Because there is currently no way to get the IP address(es) of the > > current host. It MAY also simplify the C level implementation, > > because you could require that e.g. the SERVICE argument is actually a > > port number and not a string or a number; similar for the HOST > > argument. > > > I don't think this is necessary, so I'll leave that for a future > enhancement. To restrict connections to the local host, I suggest > using either "localhost" for the HOST or as local (UNIX) socket. It was indented to implement protocols like DCC. But it is better to leave this for the feature. > Ok, but as you mention yourself, this is the exception, so it > shouldn't be the standard behaviour. I will take a look at using > stop-process and start-process to temporarily inhibit a server socket > from accepting connections. If you can use stop-process in > the sentinel, this seems to be a cleaner solution than > having to (re-)enable the server by calling accept-connection. OK. I will not bother you again :-) Thanks for all your work, Kim. Helmut. _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: New patch for server sockets and datagram (UDP) support. 2002-03-08 7:38 ` Helmut Eller @ 2002-03-08 9:13 ` Kim F. Storm 2002-03-08 11:16 ` Helmut Eller 2002-03-08 16:36 ` Stefan Monnier 2002-03-08 21:07 ` Richard Stallman 1 sibling, 2 replies; 47+ messages in thread From: Kim F. Storm @ 2002-03-08 9:13 UTC (permalink / raw) Cc: emacs-devel Helmut Eller <helmut@xaital.km4u.net> writes: > storm@cua.dk (Kim F. Storm) writes: > > > > :nowait BOOL -- Don't wait for client process to complete the > > connection to the server if BOOL is non-nil; instead, the sentinel > > function will be called with second matching "open" (if successful) or > > "failed" when the connect completes. Default is to use a blocking > > connect. > > I would prefer :wait that defaults to t. Is shorter and avoids the > negation. Since we will normally use this through wrappers -- and there is really only one usage which does actually wait (corresponsing to the old, blocking open-network-stream), it makes more sense to have a :wait key with default nil. I'll change that. > > > Notice that the FILTER and SENTINEL args are never used directly by > > the server process. Also, the BUFFER argument is not used directly by > > the server process, but via `network-server-log-function' hook, a log > > of the accepted (and failed) connections may be recorded in the server > > process' buffer. > > network-server-log-function is a global variable. This may cause > problems when used by independent packages. Is it a problem to make > this an attribute of the server process? E.g. as :log-function > argument? > I actually didn't expect any `packages' as such to use this, as I just intended it to be a way to trace/debug connectivity problems. But I agree that it would be cleaner to add it per-connection via a :log argument. I'll think about that... > > The following special call returns t iff a given KEY VALUE > > pair is supported on this system: > > (make-network-process :feature KEY VALUE) */) > > Hmm... this looks a bit strange. Can you give some examples? > Especially, what do you supply as VALUE? > It simply tests whether using KEY VALUE as arguments to make-network-stream is supported, e.g. :datagram t, :nowait t, :family 'local. > > > Because there is currently no way to get the IP address(es) of the > > > current host. It MAY also simplify the C level implementation, > > > because you could require that e.g. the SERVICE argument is actually a > > > port number and not a string or a number; similar for the HOST > > > argument. > > > > > I don't think this is necessary, so I'll leave that for a future > > enhancement. To restrict connections to the local host, I suggest > > using either "localhost" for the HOST or as local (UNIX) socket. > > It was indented to implement protocols like DCC. But it is better to > leave this for the feature. Yes! I think I'm trying to cope with enough new functionality here :-) > > > Ok, but as you mention yourself, this is the exception, so it > > shouldn't be the standard behaviour. I will take a look at using > > stop-process and start-process to temporarily inhibit a server socket > > from accepting connections. If you can use stop-process in > > the sentinel, this seems to be a cleaner solution than > > having to (re-)enable the server by calling accept-connection. > > OK. I will not bother you again :-) Actually, I will make `:wait t' work for a server socket -- that will allow you to start the server in the stopped state. > > Thanks for all your work, Kim. Thank you for your initial work on this, as well as your valuable feedback, Helmut! -- Kim F. Storm <storm@cua.dk> http://www.cua.dk _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: New patch for server sockets and datagram (UDP) support. 2002-03-08 9:13 ` Kim F. Storm @ 2002-03-08 11:16 ` Helmut Eller 2002-03-08 16:36 ` Stefan Monnier 1 sibling, 0 replies; 47+ messages in thread From: Helmut Eller @ 2002-03-08 11:16 UTC (permalink / raw) Cc: emacs-devel storm@cua.dk (Kim F. Storm) writes: > It simply tests whether using KEY VALUE as arguments to make-network-stream > is supported, e.g. :datagram t, :nowait t, :family 'local. I would prefer the approach with featurep, e.g. (featurep 'datagram-sockets), (featurep 'unix-sockets). It's not so clear how the VALUE argument should be compared (eq, or equal, etc). The user would have to know how to test for a specific feature anyway. _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: New patch for server sockets and datagram (UDP) support. 2002-03-08 9:13 ` Kim F. Storm 2002-03-08 11:16 ` Helmut Eller @ 2002-03-08 16:36 ` Stefan Monnier 2002-03-08 20:57 ` Kim F. Storm 1 sibling, 1 reply; 47+ messages in thread From: Stefan Monnier @ 2002-03-08 16:36 UTC (permalink / raw) Cc: Helmut Eller, emacs-devel > > > The following special call returns t iff a given KEY VALUE > > > pair is supported on this system: > > > (make-network-process :feature KEY VALUE) */) > > > > Hmm... this looks a bit strange. Can you give some examples? > > Especially, what do you supply as VALUE? > > > > It simply tests whether using KEY VALUE as arguments to make-network-stream > is supported, e.g. :datagram t, :nowait t, :family 'local. I suggest to use `subfeatures' for that: (provide 'network '(:datagram :nowait :family ...)) so you can then check (featurep 'network :datagram) -- Stefan _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: New patch for server sockets and datagram (UDP) support. 2002-03-08 16:36 ` Stefan Monnier @ 2002-03-08 20:57 ` Kim F. Storm 2002-03-08 21:03 ` Stefan Monnier 0 siblings, 1 reply; 47+ messages in thread From: Kim F. Storm @ 2002-03-08 20:57 UTC (permalink / raw) Cc: Helmut Eller, emacs-devel "Stefan Monnier" <monnier+gnu/emacs@rum.cs.yale.edu> writes: > > > > The following special call returns t iff a given KEY VALUE > > > > pair is supported on this system: > > > > (make-network-process :feature KEY VALUE) */) > > > > > > Hmm... this looks a bit strange. Can you give some examples? > > > Especially, what do you supply as VALUE? > > > > > > > It simply tests whether using KEY VALUE as arguments to make-network-stream > > is supported, e.g. :datagram t, :nowait t, :family 'local. > > I suggest to use `subfeatures' for that: > > (provide 'network '(:datagram :nowait :family ...)) I don't want to invent something new here if it isn't needed, but the problem here is that the proper setings here are determined at compile-time, so it's not just a question of putting a (provide...) line somewhere in a lisp file -- since that information is not available to lisp [that's why I provided the :feature interface]. > > so you can then check > > (featurep 'network :datagram) > But how do you test for availability of :family 'local ? -- Kim F. Storm <storm@cua.dk> http://www.cua.dk _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: New patch for server sockets and datagram (UDP) support. 2002-03-08 20:57 ` Kim F. Storm @ 2002-03-08 21:03 ` Stefan Monnier 0 siblings, 0 replies; 47+ messages in thread From: Stefan Monnier @ 2002-03-08 21:03 UTC (permalink / raw) Cc: Stefan Monnier, Helmut Eller, emacs-devel > "Stefan Monnier" <monnier+gnu/emacs@rum.cs.yale.edu> writes: > > > > > > The following special call returns t iff a given KEY VALUE > > > > > pair is supported on this system: > > > > > (make-network-process :feature KEY VALUE) */) > > > > > > > > Hmm... this looks a bit strange. Can you give some examples? > > > > Especially, what do you supply as VALUE? > > > > > > > > > > It simply tests whether using KEY VALUE as arguments to make-network-stream > > > is supported, e.g. :datagram t, :nowait t, :family 'local. > > > > I suggest to use `subfeatures' for that: > > > > (provide 'network '(:datagram :nowait :family ...)) > > I don't want to invent something new here if it isn't needed, but the > problem here is that the proper setings here are determined at > compile-time, so it's not just a question of putting a (provide...) > line somewhere in a lisp file -- since that information is not > available to lisp [that's why I provided the :feature interface]. Then put the call to `feature' in the C code. > > so you can then check > > > > (featurep 'network :datagram) > > > But how do you test for availability of :family 'local ? Whichever way you feel like, e.g. (featurep 'network :family:local). I guess I just don't understand the question. Also, it's often good enough to just call make-network-process and see if it succeeds. You only really need to provide/use `featurep' in order to know whether something would be ignored or not. Stefan _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: New patch for server sockets and datagram (UDP) support. 2002-03-08 7:38 ` Helmut Eller 2002-03-08 9:13 ` Kim F. Storm @ 2002-03-08 21:07 ` Richard Stallman 2002-03-13 15:12 ` Kim F. Storm 1 sibling, 1 reply; 47+ messages in thread From: Richard Stallman @ 2002-03-08 21:07 UTC (permalink / raw) Cc: storm, emacs-devel > :nowait BOOL -- Don't wait for client process to complete the > connection to the server if BOOL is non-nil; instead, the sentinel > function will be called with second matching "open" (if successful) or > "failed" when the connect completes. Default is to use a blocking > connect. I would prefer :wait that defaults to t. Is shorter and avoids the negation. A default that isn't nil, when nil cannot stand for use of the default, is problematical. > The following special call returns t iff a given KEY VALUE > pair is supported on this system: > (make-network-process :feature KEY VALUE) */) Hmm... this looks a bit strange. Can you give some examples? Especially, what do you supply as VALUE? That usage seems like a kludge to me. What exactly is it trying to solve? Would it work to simply try this combination of args, catch the error that you get if it isn't supported, and try another method? _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: New patch for server sockets and datagram (UDP) support. 2002-03-08 21:07 ` Richard Stallman @ 2002-03-13 15:12 ` Kim F. Storm 0 siblings, 0 replies; 47+ messages in thread From: Kim F. Storm @ 2002-03-13 15:12 UTC (permalink / raw) Richard Stallman <rms@gnu.org> writes: > > > The following special call returns t iff a given KEY VALUE > > pair is supported on this system: > > (make-network-process :feature KEY VALUE) */) > > Hmm... this looks a bit strange. Can you give some examples? > Especially, what do you supply as VALUE? > > That usage seems like a kludge to me. What exactly is it trying to > solve? Would it work to simply try this combination of args, catch > the error that you get if it isn't supported, and try another method? Well, there are many combinations of things that may or may not work. This provides a simple way to check in advance whether a given feature is _supposed_ to work on this system. If it then fails, something more fundamental is wrong. -- Kim F. Storm <storm@cua.dk> http://www.cua.dk _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: New patch for server sockets and datagram (UDP) support. 2002-03-07 0:08 ` New patch for server sockets and datagram (UDP) support Kim F. Storm 2002-03-07 10:56 ` Kim F. Storm @ 2002-03-07 12:54 ` Mario Lang 2002-03-07 12:58 ` Kim F. Storm 2002-03-08 9:09 ` Richard Stallman 2 siblings, 1 reply; 47+ messages in thread From: Mario Lang @ 2002-03-07 12:54 UTC (permalink / raw) storm@cua.dk (Kim F. Storm) writes: > Richard Stallman <rms@gnu.org> writes: > > > If the HOST argument is nil, a server socket is opened which > > accepts connections. The sentinel is called - with a newly > > created process - whenever a connections is accepted. > > > > That sounds good to me in principle, if the details work ok. > > The following patch adds server socket support via open-network-stream. > If the HOST is nil, a server socket for SERVICE is opened in listening > state. OK, I compiled my emacs and played a bit with it. Here come my issues: 1. How do I bind to a random port? Normally, port 0 is used for that. I tried, it only partially works. I had Emacs listen on 42266 then, but: (setq my-process (open-network-stream "dcc" nil nil 0)) (process-contact my-process) => (nil 0 nil nil nil) Is it possible that process-contact would return the real port where Emacs is listening on? (BTW, the docstring of process-contact is wrong) Here is what netstat -lp told me after the code above was executed: tcp 0 0 *:42266 *:* LISTEN 25203/emacs 2. How am I supposed to bind to localhost only e.g. with this implementation??? -- Regards, Mario <mlang@delysid.org> _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: New patch for server sockets and datagram (UDP) support. 2002-03-07 12:54 ` Mario Lang @ 2002-03-07 12:58 ` Kim F. Storm 0 siblings, 0 replies; 47+ messages in thread From: Kim F. Storm @ 2002-03-07 12:58 UTC (permalink / raw) Cc: emacs-devel Mario Lang <mlang@delysid.org> writes: > storm@cua.dk (Kim F. Storm) writes: > > > Richard Stallman <rms@gnu.org> writes: > > > > > If the HOST argument is nil, a server socket is opened which > > > accepts connections. The sentinel is called - with a newly > > > created process - whenever a connections is accepted. > > > > > > That sounds good to me in principle, if the details work ok. > > > > The following patch adds server socket support via open-network-stream. > > If the HOST is nil, a server socket for SERVICE is opened in listening > > state. > OK, I compiled my emacs and played a bit with it. > > Here come my issues: > > 1. How do I bind to a random port? Normally, port 0 is used > for that. I tried, it only partially works. I had Emacs listen on 42266 then, but: > > (setq my-process (open-network-stream "dcc" nil nil 0)) > (process-contact my-process) > => (nil 0 nil nil nil) > > Is it possible that process-contact would return the real port where > Emacs is listening on? Yes, I'll add a call to getsockname after bind if the port number is 0 and patch the actual port number into the process-contact list. > (BTW, the docstring of process-contact is wrong) I know :-) > > 2. How am I supposed to bind to localhost only e.g. > with this implementation??? Hmm, yes, that is a good question. Referring to my recent answer to Alex on emacs-devel, I guess the "clean" approach to this problem is to just add a 9th argument, SERVER, to open-network-connection rather than overloading the HOST argument ... -- Kim F. Storm <storm@cua.dk> http://www.cua.dk _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: New patch for server sockets and datagram (UDP) support. 2002-03-07 0:08 ` New patch for server sockets and datagram (UDP) support Kim F. Storm 2002-03-07 10:56 ` Kim F. Storm 2002-03-07 12:54 ` Mario Lang @ 2002-03-08 9:09 ` Richard Stallman 2002-03-08 9:35 ` Kim F. Storm 2002-03-08 11:04 ` Helmut Eller 2 siblings, 2 replies; 47+ messages in thread From: Richard Stallman @ 2002-03-08 9:09 UTC (permalink / raw) Cc: emacs-devel, helmut Helmut, what do you think of this approach? Does it solve the problems you were concerned about? Given that datagram streams are handled so differently at the user level, would it be better to have a different function to open one? + DEFVAR_LISP ("network-server-log-function", &Vnetwork_server_log_function, + doc: /* Function called when accepting a network connecting. + Arguments are SERVER, PROCESS, and MESSAGE, where SERVER is the server process, + PROCESS is the new process for the connection, and MESSAGE is a string. */); Could you please explain more about this? I am wondering whether having a single function to be called for all connections is the right interface for the job. For consistency, please have two separate functions process-datagram-address and set-process-datagram-address. The former should just return the current status; the latter should set it, like the other set-process-... functions. _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: New patch for server sockets and datagram (UDP) support. 2002-03-08 9:09 ` Richard Stallman @ 2002-03-08 9:35 ` Kim F. Storm 2002-03-08 11:04 ` Helmut Eller 1 sibling, 0 replies; 47+ messages in thread From: Kim F. Storm @ 2002-03-08 9:35 UTC (permalink / raw) Cc: helmut Richard Stallman <rms@gnu.org> writes: > > Given that datagram streams are handled so differently at the user > level, would it be better to have a different function to open one? > I think my latest proposal for `make-network-process' with wrappers functions like open-datagram-client addresses this concern. > > + DEFVAR_LISP ("network-server-log-function", &Vnetwork_server_log_function, > + doc: /* Function called when accepting a network connecting. > + Arguments are SERVER, PROCESS, and MESSAGE, where SERVER is the server process, > + PROCESS is the new process for the connection, and MESSAGE is a string. */); > > Could you please explain more about this? I am wondering whether > having a single function to be called for all connections is the right > interface for the job. > Helmut suggested that I do this on a per-server basis; I'll look into that. > > For consistency, please have two separate functions > process-datagram-address and set-process-datagram-address. > The former should just return the current status; the latter > should set it, like the other set-process-... functions. > Ok, I will do that. The reason I made just one function is that I was concerned whether doing (setq old-address (process-datagram-address P)) (set-process-datagram-address P new-address) in two steps would be atomic. But since we don't accept input between those two, they will be atomic. -- Kim F. Storm <storm@cua.dk> http://www.cua.dk _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: New patch for server sockets and datagram (UDP) support. 2002-03-08 9:09 ` Richard Stallman 2002-03-08 9:35 ` Kim F. Storm @ 2002-03-08 11:04 ` Helmut Eller 1 sibling, 0 replies; 47+ messages in thread From: Helmut Eller @ 2002-03-08 11:04 UTC (permalink / raw) Cc: storm, emacs-devel Richard Stallman <rms@gnu.org> writes: > Helmut, what do you think of this approach? Does it solve the problems > you were concerned about? I'm happy with the new `make-network-process' function. _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Non-blocking open-network-stream 2002-03-01 0:21 ` Kim F. Storm 2002-03-01 8:01 ` Juanma Barranquero 2002-03-01 21:23 ` Richard Stallman @ 2002-03-02 7:59 ` Helmut Eller 2002-03-03 0:12 ` Kim F. Storm 2002-03-03 14:39 ` Richard Stallman 2 siblings, 2 replies; 47+ messages in thread From: Helmut Eller @ 2002-03-02 7:59 UTC (permalink / raw) Cc: emacs-devel storm@cua.dk (Kim F. Storm) writes: > (do we have papers for that?) No. If needed, I will sign papers. > However, I think it can be done via open-network-stream: > > If the HOST argument is nil, a server socket is opened which > accepts connections. The sentinel is called - with a newly > created process - whenever a connections is accepted. I see several problems with this approach: - What is the process-name for the connections? The same as for the server socket? All connections with the same name? - How do you open a Unix server socket? - It's possible to open Unix server sockets. It would be reasonable to support Unix client sockets too. How can this be done? - What should NON-BLOCKING mean for server sockets? - open-network-stream takes already 7 arguments. Putting even more functionality in makes it hard to document. Server sockets are IMHO different enough to merit a separate function. What is the advantage of merging those concepts? _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Non-blocking open-network-stream 2002-03-02 7:59 ` Non-blocking open-network-stream Helmut Eller @ 2002-03-03 0:12 ` Kim F. Storm 2002-03-03 10:46 ` Helmut Eller 2002-03-03 16:44 ` Mario Lang 2002-03-03 14:39 ` Richard Stallman 1 sibling, 2 replies; 47+ messages in thread From: Kim F. Storm @ 2002-03-03 0:12 UTC (permalink / raw) Cc: emacs-devel Helmut Eller <helmut@xaital.km4u.net> writes: > storm@cua.dk (Kim F. Storm) writes: > > > (do we have papers for that?) > > No. If needed, I will sign papers. > > > However, I think it can be done via open-network-stream: > > > > If the HOST argument is nil, a server socket is opened which > > accepts connections. The sentinel is called - with a newly > > created process - whenever a connections is accepted. > > I see several problems with this approach: > > - What is the process-name for the connections? The same as for the > server socket? All connections with the same name? My idea is to name it as the server process concatenated with the ip address and source port of the client, i.e. something like "service <10.20.30.40:3248>" I prefer this to manually having to give each connection a unique name in advance (as would be necessary with your accept-connection API). > > - How do you open a Unix server socket? > I intent do rename the NON-BLOCKING argument to a more generic TYPE argument. Eventually, it could be things like: If HOST specified - connect to that host: nil - blocking connect (tcp) to SERVICE on HOST t - non-blocking connect (tcp) to SERVICE on HOST udp - open udp socket with target SERVICE on HOST unix - connect to unix socket on address SERVICE If HOST is nil - open a server socket: nil - open tcp socket listning on SERVICE port t - same as nil udp - open udp socket bound to SERVICE port unix - open unix socket bound to address SERVICE > - It's possible to open Unix server sockets. It would be reasonable > to support Unix client sockets too. How can this be done? > See above. (IIRC, Your patch didn't cover unix sockets) > - What should NON-BLOCKING mean for server sockets? See above. > > - open-network-stream takes already 7 arguments. Putting even more > functionality in makes it hard to document. I don't agree. Renaming NON-BLOCKING to TYPE and switching on whether HOST is non-nil (connect) or nil (start server) seems fairly clean and simple to me. > > Server sockets are IMHO different enough to merit a separate > function. What is the advantage of merging those concepts? > Most of the necessary functionality is already in open-network-stream, so adding the server part there is pretty orthogonal I think. -- Kim F. Storm <storm@cua.dk> http://www.cua.dk _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Non-blocking open-network-stream 2002-03-03 0:12 ` Kim F. Storm @ 2002-03-03 10:46 ` Helmut Eller 2002-03-03 16:44 ` Mario Lang 1 sibling, 0 replies; 47+ messages in thread From: Helmut Eller @ 2002-03-03 10:46 UTC (permalink / raw) Cc: emacs-devel storm@cua.dk (Kim F. Storm) writes: > I intent do rename the NON-BLOCKING argument to a more generic TYPE > argument. Eventually, it could be things like: > > If HOST specified - connect to that host: > > nil - blocking connect (tcp) to SERVICE on HOST > t - non-blocking connect (tcp) to SERVICE on HOST > udp - open udp socket with target SERVICE on HOST > unix - connect to unix socket on address SERVICE > > If HOST is nil - open a server socket: > > nil - open tcp socket listning on SERVICE port > t - same as nil > udp - open udp socket bound to SERVICE port > unix - open unix socket bound to address SERVICE If you overload NON-BLOCKING in this way, you lose the ability to make a non-blocking connects to Unix sockets. The distinction between INET or Unix sockets should be made with an optional PROTOCOL-FAMILY argument. Also, mixing up UDP with Unix sockets is not a good idea. The main difference between TCP and UPD is that UDP is packet oriented and TCP is stream oriented. You can also have packet oriented Unix sockets. Packet or stream orientedness should probably be specified with a separate STYLE argument. > See above. (IIRC, Your patch didn't cover unix sockets) I added Unix sockets when RMS said that Unix sockets are required to replace emacsserver. > > - open-network-stream takes already 7 arguments. Putting even more > > functionality in makes it hard to document. > > I don't agree. Renaming NON-BLOCKING to TYPE and switching on > whether HOST is non-nil (connect) or nil (start server) seems > fairly clean and simple to me. I cannot help, but your TYPE argument looks like a kludge to me. The HOST, BUFFER, and NON-BLOCKING arguments are not even used for server sockets. > > Server sockets are IMHO different enough to merit a separate > > function. What is the advantage of merging those concepts? > > > > Most of the necessary functionality is already in open-network-stream, > so adding the server part there is pretty orthogonal I think. I think there is not much shared functionality. open-network-stream does basically this: gethostbyname/socket/connect/coding-system-magic For the server socket case it should do: socket/bind/listen Only the call to socket could be shared. Below is my updated patch that supports Unix sockets. Perhaps you can reuse some parts. Index: process.c =================================================================== RCS file: /cvsroot/emacs/emacs/src/process.c,v retrieving revision 1.353 diff -c -r1.353 process.c *** process.c 28 Feb 2002 23:59:19 -0000 1.353 --- process.c 3 Mar 2002 10:36:17 -0000 *************** *** 51,59 **** --- 51,69 ---- #ifdef HAVE_SOCKETS /* TCP connection support, if kernel can do it */ #include <sys/socket.h> + #include <sys/un.h> #include <netdb.h> #include <netinet/in.h> #include <arpa/inet.h> + + /* Union of all socket address types we support. */ + union sockaddr_union + { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_un sun; + }; + #ifdef NEED_NET_ERRNO_H #include <net/errno.h> #endif /* NEED_NET_ERRNO_H */ *************** *** 114,119 **** --- 124,130 ---- Lisp_Object Qprocessp; Lisp_Object Qrun, Qstop, Qsignal; Lisp_Object Qopen, Qclosed, Qconnect, Qfailed; + Lisp_Object Qserver_socket, Qlisten, Qinet, Qunix; Lisp_Object Qlast_nonmenu_event; /* Qexit is declared and initialized in eval.c. */ *************** *** 122,129 **** --- 133,142 ---- #ifdef HAVE_SOCKETS #define NETCONN_P(p) (GC_CONSP (XPROCESS (p)->childp)) + #define SERVER_SOCKET_P(p) ((XPROCESS (p)->childp) == Qserver_socket) #else #define NETCONN_P(p) 0 + #define SERVER_SOCKET_P(p) 0 #endif /* HAVE_SOCKETS */ /* Define first descriptor number available for subprocesses. */ *************** *** 258,264 **** static struct coding_system *proc_encode_coding_system[MAXDESC]; static Lisp_Object get_process (); ! static void exec_sentinel (); extern EMACS_TIME timer_check (); extern int timers_run; --- 271,282 ---- static struct coding_system *proc_encode_coding_system[MAXDESC]; static Lisp_Object get_process (); ! static void exec_sentinel (Lisp_Object proc, Lisp_Object reason); ! static Lisp_Object read_process_output_error_handler (Lisp_Object error); ! static Lisp_Object read_process_output_call (Lisp_Object fun_and_args); ! #ifdef HAVE_SOCKETS ! static Lisp_Object decode_sockaddr_union (union sockaddr_union *u); ! #endif extern EMACS_TIME timer_check (); extern int timers_run; *************** *** 614,619 **** --- 632,642 ---- XPROCESS (process)->status = Fcons (Qexit, Fcons (make_number (0), Qnil)); XSETINT (XPROCESS (process)->tick, ++process_tick); } + else if (SERVER_SOCKET_P (process)) + { + XPROCESS (process)->status = list2 (Qexit, make_number (0)); + XSETINT (XPROCESS (process)->tick, ++process_tick); + } else if (XINT (XPROCESS (process)->infd) >= 0) { Fkill_process (process, Qnil); *************** *** 638,643 **** --- 661,667 ---- closed -- for a network stream connection that is closed. connect -- when waiting for a non-blocking connection to complete. failed -- when a non-blocking connection has failed. + listen -- for a server socket that is listening. nil -- if arg is a process name and no such process exists. PROCESS may be a process, a buffer, the name of a process, or nil, indicating the current buffer's process. */) *************** *** 668,673 **** --- 692,706 ---- else if (EQ (status, Qexit)) status = Qclosed; } + else if (SERVER_SOCKET_P (process)) + { + if (EQ (status, Qrun)) + status = Qlisten; + else if (EQ (status, Qexit)) + status = Qclosed; + else + abort (); + } return status; } *************** *** 919,930 **** DEFUN ("process-contact", Fprocess_contact, Sprocess_contact, 1, 1, 0, doc: /* Return the contact info of PROCESS; t for a real child. ! For a net connection, the value is a cons cell of the form (HOST SERVICE). */) (process) register Lisp_Object process; { CHECK_PROCESS (process); ! return XPROCESS (process)->childp; } #if 0 /* Turned off because we don't currently record this info --- 952,974 ---- DEFUN ("process-contact", Fprocess_contact, Sprocess_contact, 1, 1, 0, doc: /* Return the contact info of PROCESS; t for a real child. ! For a net connection, the value is a cons cell of the form (HOST SERVICE). ! For a server socket a cons cell of the form (server-socket . ADDRESS). */) (process) register Lisp_Object process; { CHECK_PROCESS (process); ! #ifdef HAVE_SOCKETS ! if (SERVER_SOCKET_P (process)) ! { ! union sockaddr_union u; ! socklen_t length = sizeof u; ! getsockname (XPROCESS (process)->infd, &u.sa, &length); ! return Fcons (Qserver_socket, decode_sockaddr_union (&u)); ! } ! else ! #endif ! return XPROCESS (process)->childp; } #if 0 /* Turned off because we don't currently record this info *************** *** 998,1003 **** --- 1042,1056 ---- else Fprinc (symbol, Qnil); } + else if (SERVER_SOCKET_P (proc)) + { + if (EQ (symbol, Qrun)) + write_string ("listen", -1); + else if (EQ (symbol, Qexit)) + write_string ("closed", -1); + else + Fprinc (symbol, Qnil); + } else Fprinc (symbol, Qnil); *************** *** 1038,1043 **** --- 1091,1115 ---- XSTRING (XCAR (p->childp))->data); insert_string (tembuf); } + else if (SERVER_SOCKET_P (proc)) + { + union sockaddr_union u; + socklen_t length = sizeof u; + getsockname (p->infd, &u.sa, &length); + switch (u.sa.sa_family) + { + case AF_INET: + sprintf (tembuf, "(inet socket on port %d)\n", + ntohs (u.sin.sin_port)); + break; + case AF_LOCAL: + sprintf (tembuf, "(unix socket %s)\n", u.sun.sun_path); + break; + default: + abort (); + } + insert_string (tembuf); + } else { tem = p->command; *************** *** 1778,1783 **** --- 1850,1986 ---- #ifdef HAVE_SOCKETS + /* Setup coding systems for communicating with the network stream. */ + static void + select_coding_system (proc) + Lisp_Object proc; + { + Lisp_Object buffer = XPROCESS (proc)->buffer; + Lisp_Object name = XPROCESS (proc)->name; + Lisp_Object host = XCAR (XPROCESS (proc)->childp); + Lisp_Object service = XCAR (XCDR (XPROCESS (proc)->childp)); + int inch = XINT (XPROCESS (proc)->infd); + int outch = XINT (XPROCESS (proc)->outfd); + { + struct gcpro gcpro1; + /* Qt denotes we have not yet called Ffind_operation_coding_system. */ + Lisp_Object coding_systems = Qt; + Lisp_Object args[5], val; + + if (!NILP (Vcoding_system_for_read)) + val = Vcoding_system_for_read; + else if ((!NILP (buffer) && NILP (XBUFFER (buffer)->enable_multibyte_characters)) + || (NILP (buffer) && NILP (buffer_defaults.enable_multibyte_characters))) + /* We dare not decode end-of-line format by setting VAL to + Qraw_text, because the existing Emacs Lisp libraries + assume that they receive bare code including a sequene of + CR LF. */ + val = Qnil; + else + { + args[0] = Qopen_network_stream, args[1] = name, + args[2] = buffer, args[3] = host, args[4] = service; + GCPRO1 (proc); + coding_systems = Ffind_operation_coding_system (5, args); + UNGCPRO; + if (CONSP (coding_systems)) + val = XCAR (coding_systems); + else if (CONSP (Vdefault_process_coding_system)) + val = XCAR (Vdefault_process_coding_system); + else + val = Qnil; + } + XPROCESS (proc)->decode_coding_system = val; + + if (!NILP (Vcoding_system_for_write)) + val = Vcoding_system_for_write; + else if (NILP (current_buffer->enable_multibyte_characters)) + val = Qnil; + else + { + if (EQ (coding_systems, Qt)) + { + args[0] = Qopen_network_stream, args[1] = name, + args[2] = buffer, args[3] = host, args[4] = service; + GCPRO1 (proc); + coding_systems = Ffind_operation_coding_system (5, args); + UNGCPRO; + } + if (CONSP (coding_systems)) + val = XCDR (coding_systems); + else if (CONSP (Vdefault_process_coding_system)) + val = XCDR (Vdefault_process_coding_system); + else + val = Qnil; + } + XPROCESS (proc)->encode_coding_system = val; + } + + if (!proc_decode_coding_system[inch]) + proc_decode_coding_system[inch] + = (struct coding_system *) xmalloc (sizeof (struct coding_system)); + setup_coding_system (XPROCESS (proc)->decode_coding_system, + proc_decode_coding_system[inch]); + if (!proc_encode_coding_system[outch]) + proc_encode_coding_system[outch] + = (struct coding_system *) xmalloc (sizeof (struct coding_system)); + setup_coding_system (XPROCESS (proc)->encode_coding_system, + proc_encode_coding_system[outch]); + + XPROCESS (proc)->decoding_buf = make_uninit_string (0); + XPROCESS (proc)->decoding_carryover = make_number (0); + XPROCESS (proc)->encoding_buf = make_uninit_string (0); + XPROCESS (proc)->encoding_carryover = make_number (0); + + XPROCESS (proc)->inherit_coding_system_flag + = (NILP (buffer) || !inherit_process_coding_system + ? Qnil : Qt); + + } + + /* create and initialize a process representing a socket. */ + static Lisp_Object + make_socket_process (fd, name, childp, status, buffer) + int fd; + Lisp_Object name, childp, status, buffer; + { + struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; + GCPRO4 (name, childp, status, buffer); + { + Lisp_Object proc = make_process (name); + chan_process[fd] = proc; + XPROCESS (proc)->childp = childp; + XPROCESS (proc)->command_channel_p = Qnil; + XPROCESS (proc)->buffer = buffer; + XPROCESS (proc)->sentinel = Qnil; + XPROCESS (proc)->filter = Qnil; + XPROCESS (proc)->command = Qnil; + XPROCESS (proc)->pid = Qnil; + XSETINT (XPROCESS (proc)->infd, fd); + XSETINT (XPROCESS (proc)->outfd, fd); + XPROCESS (proc)->status = status; + if (fd > max_process_desc) + max_process_desc = fd; + return proc; + } + } + + /* Make FD non-blocking and add it to the input fd-sets. */ + static void + register_fd_for_input (fd) + int fd; + { + #ifdef O_NONBLOCK + fcntl (fd, F_SETFL, O_NONBLOCK); + #else + #ifdef O_NDELAY + fcntl (fd, F_SETFL, O_NDELAY); + #endif + #endif + FD_SET (fd, &input_wait_mask); + FD_SET (fd, &non_keyboard_wait_mask); + } + /* open a TCP network connection to a given HOST/SERVICE. Treated exactly like a normal process when reading and writing. Only differences are in status display and process deletion. A network *************** *** 1836,1842 **** #endif /* HAVE_GETADDRINFO */ int ret = 0; int xerrno = 0; ! int s = -1, outch, inch; struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5; int retry = 0; int count = specpdl_ptr - specpdl; --- 2039,2045 ---- #endif /* HAVE_GETADDRINFO */ int ret = 0; int xerrno = 0; ! int s = -1; struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5; int retry = 0; int count = specpdl_ptr - specpdl; *************** *** 2135,2167 **** send_command (s, C_DUMB, 1, 0); #endif /* TERM */ - inch = s; - outch = s; - if (!NILP (buffer)) buffer = Fget_buffer_create (buffer); - proc = make_process (name); - - chan_process[inch] = proc; - - #ifdef O_NONBLOCK - fcntl (inch, F_SETFL, O_NONBLOCK); - #else - #ifdef O_NDELAY - fcntl (inch, F_SETFL, O_NDELAY); - #endif - #endif ! XPROCESS (proc)->childp = Fcons (host, Fcons (service, Qnil)); ! XPROCESS (proc)->command_channel_p = Qnil; ! XPROCESS (proc)->buffer = buffer; XPROCESS (proc)->sentinel = XCAR (sentinel); XPROCESS (proc)->filter = XCDR (sentinel); - XPROCESS (proc)->command = Qnil; - XPROCESS (proc)->pid = Qnil; - XSETINT (XPROCESS (proc)->infd, inch); - XSETINT (XPROCESS (proc)->outfd, outch); - XPROCESS (proc)->status = Qrun; #ifdef NON_BLOCKING_CONNECT if (!NILP (non_blocking)) --- 2338,2349 ---- send_command (s, C_DUMB, 1, 0); #endif /* TERM */ if (!NILP (buffer)) buffer = Fget_buffer_create (buffer); ! proc = make_socket_process (s, name, list2 (host, service), Qrun, buffer); XPROCESS (proc)->sentinel = XCAR (sentinel); XPROCESS (proc)->filter = XCDR (sentinel); #ifdef NON_BLOCKING_CONNECT if (!NILP (non_blocking)) *************** *** 2170,2271 **** in that case, we still need to signal this like a non-blocking connection. */ XPROCESS (proc)->status = Qconnect; ! if (!FD_ISSET (inch, &connect_wait_mask)) ! { ! FD_SET (inch, &connect_wait_mask); num_pending_connects++; } } else #endif ! if (!EQ (XPROCESS (proc)->filter, Qt)) { ! FD_SET (inch, &input_wait_mask); ! FD_SET (inch, &non_keyboard_wait_mask); } ! if (inch > max_process_desc) ! max_process_desc = inch; { ! /* Setup coding systems for communicating with the network stream. */ ! struct gcpro gcpro1; ! /* Qt denotes we have not yet called Ffind_operation_coding_system. */ ! Lisp_Object coding_systems = Qt; ! Lisp_Object args[5], val; ! if (!NILP (Vcoding_system_for_read)) ! val = Vcoding_system_for_read; ! else if ((!NILP (buffer) && NILP (XBUFFER (buffer)->enable_multibyte_characters)) ! || (NILP (buffer) && NILP (buffer_defaults.enable_multibyte_characters))) ! /* We dare not decode end-of-line format by setting VAL to ! Qraw_text, because the existing Emacs Lisp libraries ! assume that they receive bare code including a sequene of ! CR LF. */ ! val = Qnil; ! else ! { ! args[0] = Qopen_network_stream, args[1] = name, ! args[2] = buffer, args[3] = host, args[4] = service; ! GCPRO1 (proc); ! coding_systems = Ffind_operation_coding_system (5, args); ! UNGCPRO; ! if (CONSP (coding_systems)) ! val = XCAR (coding_systems); ! else if (CONSP (Vdefault_process_coding_system)) ! val = XCAR (Vdefault_process_coding_system); ! else ! val = Qnil; ! } ! XPROCESS (proc)->decode_coding_system = val; ! if (!NILP (Vcoding_system_for_write)) ! val = Vcoding_system_for_write; ! else if (NILP (current_buffer->enable_multibyte_characters)) ! val = Qnil; ! else { ! if (EQ (coding_systems, Qt)) { ! args[0] = Qopen_network_stream, args[1] = name, ! args[2] = buffer, args[3] = host, args[4] = service; ! GCPRO1 (proc); ! coding_systems = Ffind_operation_coding_system (5, args); ! UNGCPRO; } - if (CONSP (coding_systems)) - val = XCDR (coding_systems); - else if (CONSP (Vdefault_process_coding_system)) - val = XCDR (Vdefault_process_coding_system); - else - val = Qnil; } ! XPROCESS (proc)->encode_coding_system = val; ! } ! ! if (!proc_decode_coding_system[inch]) ! proc_decode_coding_system[inch] ! = (struct coding_system *) xmalloc (sizeof (struct coding_system)); ! setup_coding_system (XPROCESS (proc)->decode_coding_system, ! proc_decode_coding_system[inch]); ! if (!proc_encode_coding_system[outch]) ! proc_encode_coding_system[outch] ! = (struct coding_system *) xmalloc (sizeof (struct coding_system)); ! setup_coding_system (XPROCESS (proc)->encode_coding_system, ! proc_encode_coding_system[outch]); ! ! XPROCESS (proc)->decoding_buf = make_uninit_string (0); ! XPROCESS (proc)->decoding_carryover = make_number (0); ! XPROCESS (proc)->encoding_buf = make_uninit_string (0); ! XPROCESS (proc)->encoding_carryover = make_number (0); ! ! XPROCESS (proc)->inherit_coding_system_flag ! = (NILP (buffer) || !inherit_process_coding_system ! ? Qnil : Qt); ! ! UNGCPRO; ! return proc; } #endif /* HAVE_SOCKETS */ void --- 2352,2618 ---- in that case, we still need to signal this like a non-blocking connection. */ XPROCESS (proc)->status = Qconnect; ! if (!FD_ISSET (s, &connect_wait_mask)) ! { ! FD_SET (s, &connect_wait_mask); num_pending_connects++; } } else #endif ! register_fd_for_input (s); ! ! select_coding_system (proc); ! ! UNGCPRO; ! return proc; ! } ! ! #define BACKLOG 5 ! ! /* Open a listening socket on PORT and return the socket descriptor. */ ! static int ! open_inet_socket (int port) ! { ! int fd = socket (PF_INET, SOCK_STREAM, 0); ! if (fd <= 0) goto error; ! { ! int err; ! int optval = 1; ! struct sockaddr_in servaddr; ! bzero (&servaddr, sizeof servaddr); ! servaddr.sin_family = AF_INET; ! servaddr.sin_port = htons (port); ! servaddr.sin_addr.s_addr = htonl (INADDR_ANY); ! err = setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval); ! if (err != 0) goto error; ! err = bind (fd, (struct sockaddr *)&servaddr, sizeof servaddr); ! if (err != 0) goto error; ! err = listen (fd, BACKLOG); ! if (err != 0) goto error; ! return fd; ! } ! error: ! if (fd > 0) ! close (fd); ! report_file_error ("open-server-socket", Qnil); ! abort (); ! } ! ! static int ! open_unix_socket (char *filename) ! { ! int fd = socket (PF_LOCAL, SOCK_STREAM, 0); ! if (fd <= 0) goto error; ! { ! int err; ! struct sockaddr_un servaddr; ! bzero (&servaddr, sizeof servaddr); ! servaddr.sun_family = AF_LOCAL; ! strncpy (servaddr.sun_path, filename, sizeof servaddr.sun_path); ! err = bind (fd, (struct sockaddr *)&servaddr, sizeof servaddr); ! if (err != 0) goto error; ! err = listen (fd, BACKLOG); ! if (err != 0) goto error; ! return fd; ! } ! error: ! if (fd > 0) ! close (fd); ! report_file_error ("open-server-socket", Qnil); ! abort (); ! } ! ! /* Open a server socket on a given port. The filter function must be ! set before accepting connections with `accept-connection'. The ! process-buffer is not used. */ ! ! DEFUN ("open-server-socket", Fopen_server_socket, Sopen_server_socket, ! 3, 3, 0, ! doc: /* Open a server socket on a port. ! ! Returns a process object to represent the socket. The filter function ! can be used to accept connections. See `accept-connection'. ! `delete-process' closes the server socket. ! ! NAME is the name for the process. ! PROTOCOL either 'inet or 'unix. ! PORT is the port number resp. filename for the socket. */ ) ! (name, protocol, port) ! Lisp_Object name, protocol, port; ! { ! int fd; ! CHECK_STRING (name); ! CHECK_SYMBOL (protocol); ! if (protocol == Qinet) ! { ! CHECK_NATNUM (port); ! if ((XINT (port) < 0) || ((1 << 16) <= XINT (port))) ! error ("Port number out of range"); ! fd = open_inet_socket (XINT (port)); ! } ! else if (protocol == Qunix) ! { ! CHECK_STRING (port); ! fd = open_unix_socket (XSTRING (port)->data); ! } ! else ! error ("Unsupported protocol %s", XSYMBOL (protocol)->name->data); ! return make_socket_process (fd, name, Qserver_socket, Qrun, Qnil); ! } ! ! /* return a Lisp representation for U: ! (HOST PORT) for AF_INET ! (unix FILENAME) for AF_UNIX. */ ! static Lisp_Object ! decode_sockaddr_union (union sockaddr_union *u) ! { ! switch (u->sa.sa_family) ! { ! case AF_INET: { ! char string[INET_ADDRSTRLEN]; ! inet_ntop (AF_INET, &u->sin.sin_addr, string, sizeof string); ! return list2 (build_string (string), ! make_number (ntohs (u->sin.sin_port))); } + case AF_LOCAL: + return list2 (Qunix, build_string (u->sun.sun_path)); + default: + abort (); + } + } + + /* Accept a connection on SERVER_SOCKET. Remove SERVER_SOCKET from + the input fd-set so that only the first waiting client is accepted. + Then create a process representing the new connection and pass it + to PROCESS's filter function. */ + static void + accept_client (process, server_socket) + Lisp_Object process; + int server_socket; + { + struct gcpro gcpro1, gcpro2; + union sockaddr_union u; + socklen_t length = sizeof u; + int fd = accept (server_socket, (struct sockaddr*)&u, &length); + FD_CLR (server_socket, &input_wait_mask); + FD_CLR (server_socket, &non_keyboard_wait_mask); + if (fd == -1) + { + if (NILP (XPROCESS (process)->sentinel)) + report_file_error ("accept", process); + else + exec_sentinel (process, build_string (emacs_strerror (errno))); + } + else + { + Lisp_Object childp = decode_sockaddr_union (&u); + Lisp_Object proc = make_socket_process (fd, XPROCESS (process)->mark, + childp, Qrun, Qnil); + GCPRO2 (process, proc); + select_coding_system (proc); + register_fd_for_input (fd); + internal_condition_case_1 (read_process_output_call, + list3 (XPROCESS (process)->filter, + process, + proc), + NILP (Vdebug_on_error) ? Qerror : Qnil, + read_process_output_error_handler); + UNGCPRO; + } + } ! DEFUN ("accept-connection", Faccept_connection, Saccept_connection, ! 2, 2, 0, ! doc: /* Accept a connection on the server socket. + This function is non-blocking and returns nil. The next incoming + connection will be accepted and passed to the process filter. The + process filter function receives 2 arguments: the server socket and a + process representing the new connection. The new process is treated + exactly like a network connection opened with `open-network-stream'. + + PROCESS a process representing a server socket. + NAME is the name for the new connection. */ ) + (process, name) + Lisp_Object process, name; + { + CHECK_PROCESS (process); + if (! (SERVER_SOCKET_P (process))) + error ("Process %s is not a server socket process", + XSTRING (XPROCESS (process)->name)->data); + CHECK_STRING (name); { ! int fd = XINT (XPROCESS (process)->infd); ! XPROCESS (process)->mark = name; /* kludge: field is otherwise unused */ ! register_fd_for_input (fd); ! return Qnil; ! } ! } ! DEFUN ("gethostbyname", Fgethostbyname, Sgethostbyname, ! 1, 1, 0, ! doc: /* Look up a host by name. ! Return the IP address and aliases for NAME. The result is a vector of ! this form: [hostent CANONICAL-NAME ALIASES ADDRTYPE ADDRLIST] ! ! CANONICAL-NAME is a string. ! ALIASES is a list of strings. ! ADDRTYPE is the symbol inet. ! ADDRLIST is a list of this form ((INT INT INT INT)* ) ! each INT correspond to 1 byte of the 32 bit address. */) ! (name) ! Lisp_Object name; ! { ! int gcpro1; ! struct hostent *hostent; ! CHECK_STRING (name); ! hostent = gethostbyname (XSTRING (name)->data); ! if (hostent == 0) ! error ("%s", hstrerror (h_errno)); ! else ! { ! Lisp_Object host_info = Fmake_vector (make_number (5), Qnil); ! Lisp_Object *vector = XVECTOR (host_info)->contents; ! GCPRO1 (host_info); ! vector[0] = intern ("hostent"); ! vector[1] = build_string (hostent->h_name); { ! char **p = hostent->h_aliases; ! while (*p != 0) { ! vector[2] = Fcons (build_string (*p), vector[2]); ! p++; } } ! switch (hostent->h_addrtype) ! { ! case AF_INET: ! { ! struct in_addr **p = (struct in_addr **)hostent->h_addr_list; ! vector[3] = Qinet; ! while (*p != 0) ! { ! uint32_t a = ntohl ((**p).s_addr); ! vector[4] = Fcons (list4 (make_number ((a >> 24) & 0xff), ! make_number ((a >> 16) & 0xff), ! make_number ((a >> 8) & 0xff), ! make_number ((a >> 0) & 0xff)), ! vector[4]); ! p++; ! } ! break; ! } ! default: ! abort (); ! } ! UNGCPRO; ! return host_info; ! } } + #endif /* HAVE_SOCKETS */ void *************** *** 2911,2916 **** --- 3258,3268 ---- proc = chan_process[channel]; if (NILP (proc)) continue; + if (SERVER_SOCKET_P (proc)) + { + accept_client (proc, channel); + continue; + } /* Read data from the process, starting with our buffered-ahead character if we have one. */ *************** *** 3469,3474 **** --- 3821,3828 ---- if (XINT (XPROCESS (proc)->outfd) < 0) error ("Output file descriptor of %s is closed", XSTRING (XPROCESS (proc)->name)->data); + if (SERVER_SOCKET_P (proc)) + error ("Cannot write to server socket"); coding = proc_encode_coding_system[XINT (XPROCESS (proc)->outfd)]; Vlast_coding_system_used = coding->symbol; *************** *** 4246,4251 **** --- 4600,4607 ---- update_status (XPROCESS (proc)); if (! EQ (XPROCESS (proc)->status, Qrun)) error ("Process %s not running", XSTRING (XPROCESS (proc)->name)->data); + if (SERVER_SOCKET_P (proc)) + error ("Cannot write to server socket", XSTRING (XPROCESS (proc)->name)); if (CODING_REQUIRE_FLUSHING (coding)) { *************** *** 4311,4316 **** --- 4667,4674 ---- { if (NETCONN_P (proc)) Fdelete_process (proc); + else if (SERVER_SOCKET_P (proc)) + Fdelete_process (proc); else if (XINT (XPROCESS (proc)->infd) >= 0) process_send_signal (proc, SIGHUP, Qnil, 1); } *************** *** 4861,4866 **** --- 5219,5234 ---- Qfailed = intern ("failed"); staticpro (&Qfailed); + Qlisten = intern ("listen"); + staticpro (&Qlisten); + + Qserver_socket = intern ("server-socket"); + staticpro (&Qserver_socket); + Qinet = intern ("inet"); + staticpro (&Qinet); + Qunix = intern ("unix"); + staticpro (&Qunix); + Qlast_nonmenu_event = intern ("last-nonmenu-event"); staticpro (&Qlast_nonmenu_event); *************** *** 4907,4912 **** --- 5275,5283 ---- defsubr (&Sstart_process); #ifdef HAVE_SOCKETS defsubr (&Sopen_network_stream); + defsubr (&Sopen_server_socket); + defsubr (&Saccept_connection); + defsubr (&Sgethostbyname); #endif /* HAVE_SOCKETS */ defsubr (&Saccept_process_output); defsubr (&Sprocess_send_region); _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Non-blocking open-network-stream 2002-03-03 0:12 ` Kim F. Storm 2002-03-03 10:46 ` Helmut Eller @ 2002-03-03 16:44 ` Mario Lang 1 sibling, 0 replies; 47+ messages in thread From: Mario Lang @ 2002-03-03 16:44 UTC (permalink / raw) storm@cua.dk (Kim F. Storm) writes: > I intent do rename the NON-BLOCKING argument to a more generic TYPE > argument. Eventually, it could be things like: > > If HOST specified - connect to that host: > > nil - blocking connect (tcp) to SERVICE on HOST > t - non-blocking connect (tcp) to SERVICE on HOST > udp - open udp socket with target SERVICE on HOST > unix - connect to unix socket on address SERVICE > > If HOST is nil - open a server socket: > > nil - open tcp socket listning on SERVICE port > t - same as nil > udp - open udp socket bound to SERVICE port > unix - open unix socket bound to address SERVICE Isn't this quite limiting? What comes into mind first is that binding a server socket to a specific interface is not possible then. Also, as I discussed privately with Helmut, it should be possible to specify either 0 or nil (not sure) for the PORT to be able to assign a arbitrary free port (dynamic server socket for e.g. DCC). I didn't look very closely at both approaches yet, but it seems to me from first impression that merging non-blocking connects and server sockets into one single function is quite messy. -- CYa, Mario <mlang@delysid.org> _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Non-blocking open-network-stream 2002-03-02 7:59 ` Non-blocking open-network-stream Helmut Eller 2002-03-03 0:12 ` Kim F. Storm @ 2002-03-03 14:39 ` Richard Stallman 1 sibling, 0 replies; 47+ messages in thread From: Richard Stallman @ 2002-03-03 14:39 UTC (permalink / raw) Cc: storm, emacs-devel Server sockets are IMHO different enough to merit a separate function. What is the advantage of merging those concepts? A priori it is a simplification to merge them. But if merging them is painful in the details, then we are better off not merging them. _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel ^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: Non-blocking open-network-stream
@ 2002-02-27 17:49 Helmut Eller
0 siblings, 0 replies; 47+ messages in thread
From: Helmut Eller @ 2002-02-27 17:49 UTC (permalink / raw)
Cc: emacs-devel
storm@cua.dk (Kim F. Storm) writes:
> Does this sound acceptable (since there is currently no code supporting
> non-blocking connect, this cannot break anything :-)
This sounds much cleaner, at least to me.
Helmut
_______________________________________________
Emacs-devel mailing list
Emacs-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/emacs-devel
^ permalink raw reply [flat|nested] 47+ messages in thread
end of thread, other threads:[~2002-03-17 22:02 UTC | newest] Thread overview: 47+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- [not found] <m2u1sa7819.fsf@xaital.online-marketwatch.com> 2002-02-21 23:45 ` Non-blocking open-network-stream Kim F. Storm 2002-02-22 16:04 ` Stefan Monnier 2002-02-25 22:38 ` Kim F. Storm 2002-02-26 22:46 ` Helmut Eller 2002-02-27 11:59 ` Kim F. Storm 2002-02-28 4:08 ` Richard Stallman 2002-03-01 0:21 ` Kim F. Storm 2002-03-01 8:01 ` Juanma Barranquero 2002-03-01 10:50 ` Kim F. Storm 2002-03-01 17:10 ` Pavel Janík 2002-03-01 21:23 ` Richard Stallman 2002-03-07 0:08 ` New patch for server sockets and datagram (UDP) support Kim F. Storm 2002-03-07 10:56 ` Kim F. Storm 2002-03-07 11:39 ` Alex Schroeder 2002-03-07 12:39 ` Kim F. Storm 2002-03-07 14:51 ` Alex Schroeder 2002-03-08 21:06 ` Richard Stallman 2002-03-13 15:56 ` Kim F. Storm 2002-03-13 23:19 ` Final(?) " Kim F. Storm 2002-03-14 0:50 ` Al Petrofsky 2002-03-14 9:30 ` Kim F. Storm 2002-03-14 12:42 ` Richard Stallman 2002-03-14 13:35 ` Kim F. Storm 2002-03-17 22:02 ` I have installed the " Kim F. Storm 2002-03-07 15:18 ` New " Helmut Eller 2002-03-07 16:09 ` Kim F. Storm 2002-03-07 17:32 ` Helmut Eller 2002-03-07 23:58 ` Kim F. Storm 2002-03-08 7:38 ` Helmut Eller 2002-03-08 9:13 ` Kim F. Storm 2002-03-08 11:16 ` Helmut Eller 2002-03-08 16:36 ` Stefan Monnier 2002-03-08 20:57 ` Kim F. Storm 2002-03-08 21:03 ` Stefan Monnier 2002-03-08 21:07 ` Richard Stallman 2002-03-13 15:12 ` Kim F. Storm 2002-03-07 12:54 ` Mario Lang 2002-03-07 12:58 ` Kim F. Storm 2002-03-08 9:09 ` Richard Stallman 2002-03-08 9:35 ` Kim F. Storm 2002-03-08 11:04 ` Helmut Eller 2002-03-02 7:59 ` Non-blocking open-network-stream Helmut Eller 2002-03-03 0:12 ` Kim F. Storm 2002-03-03 10:46 ` Helmut Eller 2002-03-03 16:44 ` Mario Lang 2002-03-03 14:39 ` Richard Stallman 2002-02-27 17:49 Helmut Eller
Code repositories for project(s) associated with this external index https://git.savannah.gnu.org/cgit/emacs.git https://git.savannah.gnu.org/cgit/emacs/org-mode.git This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.