1sk(3) Library Functions Manual sk(3)
2
3
4
6 sk (done) P Client/Server
7 (Rev. 03-May-1996)
8
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
503 a Fox (francois@simbad.u-strasbg.fr)
504
505
506
507 sk(3)