1sk(3)                      Library Functions Manual                      sk(3)
2
3
4

NAME

6       sk  (done) P Client/Server
7                           (Rev. 03-May-1996)
8

SYNTAX

10       =>  Server startup:
11           int sock = sk_start(char *service, char * mode);
12           int  sock  = sk_astart(char *service, char * mode, char *authorisa‐
13           tion_file);
14           int plug = sk_accept(int sock, int queue_slots);
15           int status = sk_ack(int plug);
16           char *caller = sk_caller(int plug);
17           sk_urgent(int plug);
18
19       =>  Client Connection:
20           int plug = sk_open(char *host, char * service);
21           int plug = sk_connect(char *host, char * service,  char  *username,
22           char *password);
23           int stat = sk_close(int plug);
24
25       =>  Client/Server Dialogue:
26           int bytes = sk_read(int plug, char *buf, int len);
27           int bytes = sk_get(int plug, char *buf, int len);
28           int bytes = sk_gets(int plug, char *buf, int len);
29           long value = sk_getl(int plug);
30           int bytes = sk_write(int plug, char *buf, int len);
31           int bytes = sk_put(int plug, char *buf);
32           int bytes = sk_puts(int plug, char *buf);
33           int status = sk_putl(int plug, long value);
34
35       =>  Data Exchange on Server Side
36           int bytes = sk_toclient(int plug, int file_to_send );
37           int bytes = sk_fromclient(int plug, int file_to_receive);
38           int status = sk_errsend(int plug, char * message);
39           int status = sk_perrsend(int plug, char * message);
40
41       =>  Data Exchange on Client Side
42           int  bytes  = sk_obeyserver(int plug, int (* digest_routine)(), int
43           (*more_routine)());
44           int  bytes   =   sk_fromserver(int   plug,   int   file_to_receive,
45           file_to_send);
46           char *message = sk_error();
47           sk_kill(int plug, int signo);
48
49       =>  Logging & Debugging:
50           FILE *old_logfile = sk_setlog(FILE *logfile);
51           FILE *old_debfile = sk_iolog(FILE *debfile);
52           int lev = sk_log(int level, char *format, ...  );
53
54       =>  Server Utilities:
55           int status = sk_umatch(char *remote_id, char * template);
56
57       =>  Socket Configuration:
58           int bytes = sk_setb(int socket_size)
59

DESCRIPTION

61       This set of functions, stored in the libraries
62
63       /usr/local/lib/libsk.a for the client routines, and in
64
65       /usr/local/lib/libskserv.a (server routines),
66
67       allow  to  build  client/server dialogues using socket(2) facilities in
68       stream mode. A client connected to Internet may  reach  a  server  con‐
69       nected  to  Internet  independently  of  the machine architectures. The
70       client/server model is a way of RPC (Remote Process  Call)  implementa‐
71       tion.
72
73       The  service  is a character string representing either a number in the
74       range 1024-2047 or a  symbolic  service  name  appearing  in  the  file
75       /etc/services(5);  it pertains to which kind of service we want to con‐
76       nect to.
77

INCLUDE FILES

79       Function declarations are gathered in the two files:
80
81       =>  /usr/local/include/sk.h for client routines
82
83       =>  /usr/local/include/skserv.h for server routines
84

SERVER STARTUP

86       The server can be launched, either as a daemon by inetd (8),  or  as  a
87       standalone  program  in  background  mode. The choice between these two
88       modes is made via the the mode argument of the sk_start  routine:  when
89       it  contains the character d, the server is started via inetd(8), which
90       requires a specific line in the /etc/inetd.conf(5) file.  The  possible
91       characters in the mode argument are:
92
93       =>  d   to start in daemon mode
94
95       =>  l   to log the server events in the syslog(8)
96
97       =>  v   to add debugging messages in the log
98
99       =>  vv (two v's)   for very verbose (debugging) log.
100
101       sk_start  returns -1 in case of failure, and prints an error message in
102       the log; otherwise sk_start returns a non-negative socket number.
103
104       sk_astart is a version which uses an authorisation file  which  can  be
105       the  /etc/passwd(5)  file,  or  a file similar to it: the client has to
106       provide a username and a password for identification. This client  nor‐
107       mally  uses  the sk_connect routine to identify himself. The authorisa‐
108       tion_file parameter may be
109
110       =>  NULL (i.e. (char *)0); in this case  no  user  authentification  is
111           performed
112
113       =>  void  string  (i.e. ""); in this case the standard /etc/passwd file
114           is used;
115
116       =>  a valid file name which is then used instead of the /etc/passwd
117            file.  See below the description of the authorisation file,
118
119       Once launched, the server will normally listen to the  created  socket,
120       waiting  for  a  client connection. The sk_accept routine provides this
121       functionnality, i.e. a successful return from sk_accept  means  that  a
122       client  asked  for a dialogue with the server. The queue_slots argument
123       specifies the size of the queue of clients  waiting  for  a  connection
124       with  the  server: when the queue is full, the unsuccessful client will
125       get an error message.
126
127       After a successful sk_accept, the server has to identify  himself,  and
128       ask for client's identification. This is achieved with the sk_ack func‐
129       tion.
130
131       In a daemon mode, the  function  sk_accept  is  called  by  the  parent
132       process,  which  forks  ; the sk_ack function is normally called by the
133       child process, while the parent process waits for  another  connection.
134       See the example in the Example section below.
135
136       Once a client has started a dialogue with the server (via sk_accept and
137       sk_ack), the client's name and  address  can  then  be  retrieved  with
138       sk_caller. The syntax of the character string returned by sk_caller is
139
140                   Remote_Username[!]@InterNet_Number(Host_Name):
141       Provided_Username
142
143       where the ! indicates a user with root privileges.
144
145       A  NULL  string  ((char  *)0) is returned by sk_caller in case of a bad
146       plug number or in case of error.
147
148       The sk_urgent routine allows the server to receive signals sent by  the
149       connected client; see below the Signal Communication section.
150

CLIENT CONNECTION

152       A  program  can  start  a  dialogue with a server using the sk_open (no
153       identification) or the sk_connect function. service, as for the server,
154       is  either  a  numeric  string  representing  a  number  in  the  range
155       1024-2047, or is a name normally existing in  the  /etc/services  file.
156       The  username  and  password  provided  by  sk_connect,  as well as the
157       client's node and username, are checked against what is  known  in  the
158       authorisation_file (see below)
159
160       A failure to reach the server specified by its hostname and its service
161       name or number returns a  negative  value,  and  an  error  message  is
162       printed  or  logged  (see the sk_setlog function); otherwise sk_open or
163       sk_connect return a non-negative number to be used as the  first  argu‐
164       ment of dialogue routines.
165
166       In case of failure, the returned codes mean the following:
167
168       =>  -1:  the  connection to the Server can't be established due to net‐
169           work problems, like unknown host or service;
170
171       =>  -2: the Server has been reached, but refused to pursue for  invalid
172           username/password or unauthorized machine.
173

CLIENT/SERVER DIALOGUE

175       When  the  connection between the client and the server is established,
176       both can read or write on the socket. Which of the client or server has
177       to read or write on the socket is defined only by the calling programs;
178       the dialogue has therefore to be carefully designed  to  avoid  endless
179       waits  on  an opened socket. Other functions described in the next sec‐
180       tion use a very simple protocol for exchange of data.
181
182       Four functions are provided available for reading on  the  socket,  and
183       four  other  are  provided for writing on the socket. All these 8 func‐
184       tions return -1 in case of error.
185
186       1.  int bytes = sk_read(int plug, char *buf, int len)
187           is the simplest reading interface, and reads up to len bytes.   The
188           returned  number of bytes is generally smaller than len: no attempt
189           is made to fill buf.
190
191       2.  int bytes = sk_get(int plug, char *buf, int len)
192           fills buf from what comes on the socket.  The  returned  number  of
193           bytes is therefore identical to len, unless an error occured or the
194           partner closed the connection.
195
196       3.  int bytes = sk_gets(int plug, char *buf, int len)
197           fills buf up to len  bytes,  or  until  a  newline  is  found.  The
198           returned  length  includes  the newline. Notice however that buf is
199           not terminated by the NULL character.
200
201       4.  long value = sk_getl(int plug)
202           reads a long integer  (4-byte  integer)  on  the  socket  (normally
203           issued by the partner using sk_putl). Byte swapping is performed if
204           the local machine architecture differs from the network one.
205
206       5.  int bytes = sk_write(int plug, char *buf, int len)
207           writes len bytes onto the socket. Its returned number of  bytes  is
208           identical to len, unless an error occured or the partner closed the
209           connection.
210
211       6.  int bytes = sk_put(int plug, char *buf)
212           writes a null-terminated string on the socket.
213
214       7.  int bytes = sk_puts(int plug, char *buf)
215           writes a null-terminated string as a line on  the  socket,  i.e.  a
216           newline is appended to the string before being sent to the partner.
217
218       8.  int status = sk_putl(int plug, long value)
219           sends  a  4-byte long integer to the partner; byte swapping is per‐
220           formed if necessary.
221
222       Note that plug is a file handle, and it is therefore  possible  to  use
223       standard  i/o routines after the association of a FILE structure gener‐
224       ated by the fdopen(2) routine.
225

DATA EXCHANGE ON SERVER SIDE

227       Four control characters are used for the data exchange functions.  When
228       the server sends to the client the character:
229
230       =>
231            D . It means:
232           ``I've finished to talk. It's now up to you''
233
234       =>
235            B . It means:
236           ``I'll  send  Counted  Buffers,  i.e. data prefixed with its length
237           expressed as a 4-byte integer. I'll continue to send  Counted  Buf‐
238           fers until the prefix specifies a zero length''. This convention is
239           used by sk_toclient.
240
241       =>
242            C . It means:
243           ``I'll send a single counted buffer, i.e. data  prefixed  with  its
244           4-byte length.  It contains normally an error message.
245
246       =>
247            F . It means:
248           ``Please  send  me  a  Counted Buffer''. This convention is used by
249           sk_fromclient.
250
251       The detailed functionnalities are:
252
253       =>  int bytes = sk_toclient(int plug, int file_to_send )
254           is used by the Server to send a file to the connected client, using
255           the  B convention:
256
257              1.  Server sends  B
258
259              2.  Client acknowledges with  F
260
261              3.  Server  sends  counted  buffers  (buffers  preceded by their
262                  4-byte length). The last counted  buffer  has  a  length  of
263                  zero.
264
265       Upon return from sk_toclient, the Server has still to send a
266        D to the client to tell him that it's up to him to talk.
267       file_to_send must have been opened in read mode by open (2).
268
269       =>  int bytes = sk_fromclient(int plug, int file_to_receive)
270           is  used by the Server to get a data set from the connected client,
271           using the  F convention:
272
273              1.  Server sends  F
274
275              2.  Client returns a counted buffer (4-byte  integer  expressing
276                  the buffer length, followed by the actual buffer)
277
278              3.  Server asks for the next buffer with  F if the length of the
279                  received counted buffer is not zero.
280
281       sk_fromclient stops just after the client sent a zero length data  buf‐
282       fer;  Upon return from sk_fromclient, the Server has still to send a  D
283       to the client to tell him that it's up to him to talk.
284       file_to_receive must have been opened in write mode by open(2).
285
286       =>  int status = sk_errsend(int plug, char * message)
287           allows to send a single counted buffer to the client, which  gener‐
288           ally  represents  an error message. If the client uses the sk_obey‐
289           server or the sk_fromserver routine, the server has to send a  D to
290           the client to tell him to return from the sk_obeyserver function.
291
292       =>  int status = sk_perrsend(int plug, char * message)
293           is  similar  to  sk_errsend,  but message is followed by the system
294           error message as in perror(2).
295

DATA EXCHANGE ON CLIENT SIDE

297       =>  int  bytes   =   sk_fromserver(int   plug,   int   file_to_receive,
298           file_to_send)
299           is  used  by  the  Client to follow the above conventions: it reads
300           what comes over the plug socket and writes it onto  file_to_receive
301           which  must  have  been  opened  in write mode by open(2); when the
302           Server  asks  to  send  a  Counted  Buffer,  it   reads   it   from
303           file_to_send.
304           The return from sk_fromserver therfore occurs
305
306              =>  either when the server sends a  D in non-buffered mode;
307
308              =>  or when an error occurs.
309
310       =>  int  bytes  = sk_obeyserver(int plug, int (* digest_routine)(), int
311           (*more_routine)())
312           is similar to sk_fromserver,  but  routines  are  used  instead  of
313           files:
314
315              =>  digest_routine(char  *buf,  int length) collects what's sent
316                  by the Server
317
318              =>  more_routine(char *buf,  int  length)  is  called  when  the
319                  Server asks for more data.
320
321       Both  digest  and  more  routines  must return the number of bytes pro‐
322       cessed, 0 for end-of-file, and -1 for error . As for sk_fromserver, the
323       client  has  normally to send something to the Server, since he got the
324       D telling he has to talk.
325
326       =>  char *message = sk_error()
327           returns the last encountered error message. This function  is  nor‐
328           mally to use when one of the sk routines returns -1.
329           Note  that  errors  are  also written to stderr by default; another
330           file - or no file at all - may be chosen as a logfile.
331
332       The sk_kill routine allows the client to send signals to the sever; see
333       below the Signal Communication section.
334

LOGGING & DEBUGGING

336       The  server  normally  logs  the  occuring events, either to a log file
337       (which can be the stdout terminal), or in the system log. The usage  of
338       the  syslog  facility (in file /var/log/syslog) is recommended when the
339       server is launched in daemon mode by inetd(8).
340
341       At any time, the server can log details in the syslog with  the  sk_log
342       function, which will direct the message to the currently opened logfile
343       or to the syslog.
344
345       The first argument of the sk_log(int level, char * format,  ...)  func‐
346       tion  is  a number defined in the syslog.h file, or -1 to close the log
347       file; this level argument is returned in case  of  success.  The  other
348       arguments of the sk_log
349        are similar to those of the printf(3) function.
350
351       The  sk_setlog  function allows to switch the log file at any time, and
352       returns the previously active logfile. Notice that the syslog is  indi‐
353       cated by a NULL value: to use the syslog as a log file, use the call
354
355       old_logfile = sk_setlog((FILE *)0))
356
357       A very verbose debugging is activated with the sk_iolog function: what‐
358       ever is read or written on the socket is printed on  the  supplied  log
359       file.  As  sk_setlog,  sk_iolog returns the old logfile. A NULL logfile
360       argument asks to stop this debugging feature.
361

SIGNAL COMMUNICATION

363       The client can send a signal to the server via  the  sk_kill(int  plug,
364       int  signo) routine; signo must be a valid signal (see signal(3)). How‐
365       ever, the signals can be received by the server only after  the  server
366       has specified that he would accept signal interruptions. The acceptance
367       of signal interrupts is specified once with the call  to  sk_urgent(int
368       plug).
369

SERVER UTILITIES

371       char  *sk_caller(plug)   allows to retrieve a client identification; it
372       is described above.
373
374       int sk_umatch(char *remote_id, char *template )   is  a  routine  which
375       returns  0  if  remote_identification as returned by sk_caller does not
376       match the template of authorized Remote_Username@Internet separated  by
377       commas;  the  wild  chars  * and ? can be used in this field.  Remember
378       also the ending ! in the username which indicates root privileges. Some
379       examples of template:
380
381       =>  root!@130.79.*.*    matches  the root user from any of the machines
382           which Internet number starts with 130.79
383
384       =>  *@130.79.128.5,root!@130.79.*.*   matches anybody having an account
385           on  the  130.79.128.5  machine,  or the root user on other machines
386           with Internet number starting with 130.79
387

AUTHORISATION FILE

389       An Authorisation File can be used  to  restrict  access  to  authorized
390       users  via  a  Username  and  a Password. The Authorisation File can be
391       identical to the /etc/passwd(5) file when the third  parameter  of  the
392       sk_start routine is a blank string ""
393
394       The three fields which are used are:
395
396       1.  the Username the client has to provide (default is guest)
397
398       2.  the Password (encrypted)
399
400       3.  the  5th  field  (also  called  gecos  which  may contain a list of
401           allowed  Remote_Username@Internet_Number,  templates  separated  by
402           commas or blanks; the wild chars * and ? can be used in this field
403
404       The password in the /etc/passwd file can be modified with the passwd(1)
405       utility, as well as the gecos field with the -f option. An  alternative
406       to the passwd -f is the chfn (1) utility.
407

SOCKET CONFIGURATION

409       The size of blocks transferred onto the network can be changed with the
410       sk_setb function. The standard size is generally 8 blocks (4K). sk_setb
411       returns the current configuration.
412
413       A  negative  or  null  value of socket_size does not modify the size of
414       socket blocks; sk_setb(0) can therefore be used  to  know  the  current
415       socket size.
416

EXAMPLE OF A SERVER

418       The following shows the basic writing of a server which creates a child
419       via fork(2) who has to deal with the client. The server is  assumed  to
420       receive the name of a file and to send its contents to the Client.
421
422       #include <skserv.h>
423       #include <signal.h>
424       #include <sys/wait.h>
425
426       void on_death()         /* Read Zombie status */
427       { while(wait3(NULL, WNOHANG, NULL) > 0) ; }
428
429       static int theplug;
430       void  on_intr()           /*  When  the  Serveur  in Interrupted by the
431       Client */
432       {  sk_puts(theplug, "Bye-Bye"); exit(1); }
433
434               /* A routine to send a file to the Client */
435       display_file(plug, filename) int plug; char *filename;
436       { int file;
437           file = open(filename, 0);
438           if (file < 0) perrsend(filename);
439           else {
440               sk_toclient(plug, file);
441               close(file);
442           }
443       }
444
445       main()
446       {
447         int sock, plug;
448         char buffer[133];
449
450           sock = sk_start("service", "v");    /* Verbose option */
451           if (sock < 0) exit(1);
452           signal(SIGCHLD, on_death);                  /* Handler for  Zombies
453       */
454
455           while(1) {                          /* Loop on incoming connections
456       */
457               plug = sk_accept(sock, 1);
458               if (plug < 0) exit(1);
459               if (fork()) {   /* Father Here. He doesn't need plug */
460                   close(plug);
461                   continue;
462               }
463
464               /* ===Child Here. sock is only for new connections;
465                     therefore close it */
466               close(sock);
467            sk_ack(plug);       /* Acknowledge the Client     */
468               sk_log("%s just called\n", sk_caller(plug));
469
470               sk_urgent(plug);        /* Allow client to send Signal   */
471               signal(SIGINT, on_intr);
472               theplug = plug;         /* To communicate with on_intr   */
473               sk_gets(plug, buffer, sizeof(buffer));          /* Get question
474       */
475               display_file(plug, buffer);
476               sk_put(plug,  "\04");     /*  Tell the Client: I've finished to
477       talk */
478               exit(0);
479           }
480       }
481
482
483       The corresponding client can be aclient(1), or the  following  code  if
484       the program is assumed to have three parameters which are the host, the
485       service, and the filename to list.
486       main(argc, argv) int argc; char *argv[];
487       {
488         int plug;
489
490           plug = sk_open(argv[1], argv[2]);
491           if (plug < 0 ) { perror(argv[1]); exit(1); }
492           if (sk_puts(plug, argv[3]) < 0) exit(1);
493           sk_obeyserver(plug, 1, 0);  /* Digest = file#1 (stdout) */
494       }
495
496

SEE ALSO

498       aclient(1) aserver(1) chfn(1) socket(2) open(2) fdopen(2) fork(2)  per‐
499       ror(2)  printf(3)  passwd(1)  passwd(5)  services(5) inetd(8) signal(3)
500       syslog(8) wait3(2)
501

QUESTIONS & PROBLEMES

503       a Fox (francois@simbad.u-strasbg.fr)
504
505
506
507                                                                         sk(3)
Impressum