chiark / gitweb /
Extract Subversion ignore data.
[unet] / unetcfg.c
1 /* -*-c-*-
2  *
3  * $Id: unetcfg.c,v 1.3 2001/02/19 19:10:28 mdw Exp $
4  *
5  * User-space network device support.
6  *
7  * (c) 1998 Mark Wooding
8  */
9
10 /*----- Licensing notice --------------------------------------------------*
11  *
12  * This file is part of Usernet.
13  *
14  * Usernet is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  *
19  * Usernet is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with Usernet; if not, write to the Free Software Foundation,
26  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27  */
28
29 /*----- Revision history --------------------------------------------------*
30  *
31  * $Log: unetcfg.c,v $
32  * Revision 1.3  2001/02/19 19:10:28  mdw
33  * New option to allow changing interface flags.
34  *
35  * Revision 1.2  2001/02/03 18:39:59  mdw
36  * Setting the maximum interface count now does the right thing.
37  *
38  * Revision 1.1  2001/01/25 22:03:39  mdw
39  * Initial check-in (somewhat belated).
40  *
41  */
42
43 /*----- Include files -----------------------------------------------------*/
44
45 #include <errno.h>
46 #include <getopt.h>
47 #include <stdarg.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51
52 #include <sys/types.h>
53 #include <sys/time.h>
54 #include <sys/ioctl.h>
55
56 #include <fcntl.h>
57 #include <unistd.h>
58
59 #include <sys/socket.h>
60 #include <netinet/in.h>
61
62 #include <linux/if.h>
63 #include <linux/if_ether.h>
64
65 #include "unet.h"
66
67 /*----- Static variables --------------------------------------------------*/
68
69 #define VERSION "1.00"
70 #define ETH_P_HELP 0xffffu
71
72 static const char *quis = "unetcfg";    /* My program's name */
73 static int flags = 0;                   /* Various useful status flags */
74 static const char *current = "<stdin>"; /* Current Usernet device */
75 static int fd = 0;                      /* Default to @stdin@ */
76
77 #define f_verbose 1u
78 #define f_duff 2u
79 #define f_check 4u
80 #define f_close 8u
81
82 /*----- Common routines ---------------------------------------------------*/
83
84 /* --- @die@ --- *
85  *
86  * Arguments:   @const char *format@ = format string to print
87  *              @...@ = values for the placeholders
88  *
89  * Returns:     Doesn't
90  *
91  * Use:         Reports a fatal error message.
92  */
93
94 static void die(const char *format, ...)
95 {
96   va_list ap;
97   va_start(ap, format);
98   fputs(quis, stderr);
99   fputs(": ", stderr);
100   vfprintf(stderr, format, ap);
101   fputc('\n', stderr);
102   va_end(ap);
103   exit(EXIT_FAILURE);
104 }
105
106 /* --- @whiiter@ --- *
107  *
108  * Arguments:   @const char *format@ = format string to print
109  *              @...@ = values for the placeholders
110  *
111  * Returns:     Doesn't
112  *
113  * Use:         Reports a non-fatal error message.
114  */
115
116 static void whitter(const char *format, ...)
117 {
118   va_list ap;
119
120   if ((flags & f_verbose) == 0)
121     return;
122
123   va_start(ap, format);
124   fputs(quis, stderr);
125   fputs(": ", stderr);
126   vfprintf(stderr, format, ap);
127   fputc('\n', stderr);
128   va_end(ap);
129 }
130
131 /* --- @lookup@ --- *
132  *
133  * Arguments:   @void *p@ = pointer to a table
134  *              @size_t sz@ = size of the table items
135  *              @const char *s@ = pointer to string to match
136  *
137  * Returns:     Pointer to the matching block.
138  *
139  * Use:         Finds an item in a table.
140  */
141
142 static void *lookup(const void *p, size_t sz, const char *s)
143 {
144   const char **q = (const char **)p;
145   const void *e = 0;
146
147   while (*q) {
148     const char *x = *q, *y = s;
149
150     for (;;) {
151       if (!*y) {
152         if (!*x)
153           return ((void *)q);
154         if (e)
155           die("ambiguous name `%s'", s);
156         e = q;
157         break;
158       } else if (*x == *y) {
159         x++;
160         y++;
161         continue;
162       } else
163         break;
164     }
165     q = (const char **)((const char *)q + sz);
166   }
167   return ((void *)e);
168 }
169
170 #define LOOKUP(tbl, key) lookup((tbl), sizeof((tbl)[0]), (key))
171
172 /* --- @check@ --- *
173  *
174  * Arguments:   ---
175  *
176  * Returns:     ---
177  *
178  * Use:         Ensures that the current device really is a Usernet device.
179  */
180
181 static void check(void)
182 {
183   if (flags & f_check)
184     return;
185   if (ioctl(fd, UNIOCGINFO, 0) < 0)
186     die("file `%s' is not a Usernet device", current);
187   flags |= f_check;
188 }
189
190 /*----- Command implementations -------------------------------------------*/
191
192 /* --- Forward declarations --- */
193
194 struct command {
195   const char *cmd;                      /* Command name */
196   int minarg;                           /* Minimum number of args */
197   int (*func)(char **av);               /* Command handler function */
198   const char *syn, *shelp, *vhelp;      /* Short and verbose help */
199 };
200
201 static struct command cmdtab[];
202
203 struct eth_proto {
204   const char *name;                     /* User-level protocol name */
205   unsigned short proto;                 /* Protocol number */
206   const char *desc;                     /* Readable description */
207 };
208
209 static struct eth_proto eth_prototab[];
210
211 #define YNQ_YES 1
212 #define YNQ_NO 0
213 #define YNQ_QUERY (-1)
214
215 static struct ynq {
216   const char *s;
217   int val;
218 } ynqtab[] = {
219   { "query",    YNQ_QUERY },
220   { "show",     YNQ_QUERY },
221   { "?",        YNQ_QUERY },
222   { "yes",      YNQ_YES },
223   { "on",       YNQ_YES },
224   { "no",       YNQ_NO },
225   { "off",      YNQ_NO },
226   { 0,          0 }
227 };
228
229 static int run(char **av);
230
231 /* --- @ynq@ --- *
232  *
233  * Arguments:   @const char *p@ = pointer to string to decode
234  *
235  * Returns:     One of the @YNQ@ codes.
236  *
237  * Use:         Decodes a `yes/no/query' response.
238  */
239
240 static int ynq(const char *p)
241 {
242   struct ynq *ynq = LOOKUP(ynqtab, p);
243   if (!ynq)
244     die("unknown setting `%s': I understand `on', `off' and `query'", p);
245   return (ynq->val);
246 }
247
248 /* --- @cmd_help@ --- */
249
250 static int cmd_help(char **av)
251 {
252   struct command *c;
253
254   if (!*av) {
255     puts("Commands provided:\n");
256     for (c = cmdtab; c->cmd; c++)
257       printf("%-30s%s\n", c->syn, c->shelp);
258   } else if ((c = LOOKUP(cmdtab, *av)) == 0)
259     die("unknown command: `%s'", *av);
260   else {
261     fputs(c->syn, stdout);
262     fputs("\n\n", stdout);
263     fputs(c->vhelp, stdout);
264     av++;
265   }
266   return (run(av));
267 }
268
269 /* --- @cmd_select@ --- */
270
271 static int cmd_select(char **av)
272 {
273   const char *fn = *av++;
274   if (flags & f_close) {
275     close(fd);
276     whitter("closed previous device `%s'", current);
277   }
278   fd = open(fn, O_RDWR);
279   if (fd < 0)
280     die("couldn't open Usernet device `%s': %s", fn, strerror(errno));
281   current = fn;
282   flags &= ~f_check;
283   check();
284   whitter("opened new device: `%s'", fn);
285   flags |= f_close;
286   return (run(av));
287 }
288
289 /* --- @cmd_fd@ --- */
290
291 static int cmd_fd(char **av)
292 {
293   const char *p;
294   static char namebuf[20]; /* XXX Big enough? */
295
296   static struct {
297     const char *name;
298     int fd;
299     const char *desc;
300   } *fdp, fdtab[] = {
301     { "stdin",  0, "<stdin>" },
302     { "stdout", 1, "<stdout>" },
303     { "stderr", 2, "<stderr>" },
304     { 0,        0, 0 }
305   };
306
307   if (flags & f_close) {
308     close(fd);
309     whitter("closed previous device `%s'", current);
310   }
311
312   p = *av++;
313   if ((fdp = LOOKUP(fdtab, p)) != 0)
314     fd = fdp->fd;
315   else if (sscanf(p, "%i", &fd) < 1 || fd < 0)
316     die("bad file descriptor name: `%s'", p);
317
318   flags &= ~f_check | f_close;
319   if (fd < 3)
320     current = fdtab[fd].desc;
321   else
322     current = namebuf, sprintf(namebuf, "<fd %i>", fd);
323
324   check();
325   whitter("opened device on `%s'", current);
326
327   return (run(av));
328 }
329
330 /* --- @cmd_show@ --- */
331
332 static int cmd_show(char **av)
333 {
334   struct unet_info uni;
335   const char *af, *proto;
336
337   /* --- Name table for address families --- */
338
339   static const char *aftab[] = {
340     "Unspecified",
341     "Unix file domain (!)",
342     "Internet (IPv4)",
343     "Amateur radio AX.25",
344     "Novell IPX",
345     "Appletalk",
346     "Amateur radio NetROM",
347     "Multiprotocol bridge (!)",
348     "Reserved (for ATM)",
349     "Reserved (for X.25)",
350     "Internet (IPv6)",
351   };
352
353   /* --- Name table for Usernet flags --- */
354
355   static struct {
356     const char *name;
357     unsigned int f;
358   } *flp, fltab[] = {
359     { "trans", UNIF_TRANS },
360     /* @{ "open", UNIF_OPEN }@ -- ignore: this flag always appears on */
361     { "debug", UNIF_DEBUG },
362     { 0, 0 }
363   };
364
365   /* --- Read the attachment information --- */
366
367   check();
368   if (ioctl(fd, UNIOCGINFO, &uni) < 0)
369     die("couldn't read information about attachment: %s", strerror(errno));
370
371   /* --- Look the address family up --- */
372
373   if (uni.uni_family < sizeof(aftab) / sizeof(aftab[0]))
374     af = aftab[uni.uni_family];
375   else
376     af = "Unknown family";
377
378   /* --- Look the protocol up --- */
379
380   {
381     struct eth_proto *ep;
382
383     proto = "unknown";
384     for (ep = eth_prototab; ep->name; ep++) {
385       if (ep->proto == uni.uni_proto) {
386         proto = ep->desc;
387         break;
388       }
389     }
390   }
391
392   /* --- Display appropriate information --- */
393
394   printf("Interface name: %s\n", uni.uni_ifname);
395   printf("MTU: %u\n", uni.uni_mtu);
396   printf("Address family: %s\n", af);
397   printf("Protocol: %s\n", proto);
398   printf("Flags:");
399
400   for (flp = fltab; flp->name; flp++) {
401     if (uni.uni_flags & flp->f) {
402       putchar(' ');
403       fputs(flp->name, stdout);
404     }
405   }
406   putchar('\n');
407
408   /* --- Done --- */
409
410   return (run(av));
411 }
412
413 /* --- @cmd_ifname@ --- */
414
415 static int cmd_ifname(char **av)
416 {
417   struct unet_info uni;
418
419   check();
420   if (ioctl(fd, UNIOCGINFO, &uni) < 0)
421     die("couldn't read attachment information: %s", strerror(errno));
422   puts(uni.uni_ifname);
423   return (run(av));
424 }
425
426 /* --- @cmd_protocol@ --- */
427
428 static int cmd_protocol(char **av)
429 {
430   const char *pn = *av++;
431   const struct eth_proto *ep;
432
433   if ((ep = LOOKUP(eth_prototab, pn)) == 0)
434     die("unknown protocol name `%s'", pn);
435
436   if (ep->proto == ETH_P_HELP) {
437     for (ep = eth_prototab; ep->name; ep++) {
438       if (ep->proto != ETH_P_HELP)
439         printf("%s -- %s\n", ep->name, ep->desc);
440     }
441   } else {
442     check();
443     if (ioctl(fd, UNIOCSPROTO, ep->proto) < 0)
444       die("couldn't set protocol `%s': %s", ep->name, strerror(errno));
445     whitter("set protocol `%s' for `%s'", ep->name, current);
446   }
447
448   return (run(av));
449 }
450
451 /* --- @cmd_debug@ --- */
452
453 static int cmd_debug(char **av)
454 {
455   check();
456   switch (ynq(*av++)) {
457
458     case YNQ_QUERY: {
459       struct unet_info uni;
460
461       if (ioctl(fd, UNIOCGINFO, &uni))
462         die("error reading debug state: %s", strerror(errno));
463       printf("debugging for `%s' is %s\n", current,
464              uni.uni_flags & UNIF_DEBUG ? "enabled" : "disabled");
465     } break;
466
467     case YNQ_YES:
468       if (ioctl(fd, UNIOCSDEBUG, 1) < 0)
469         die("error setting debug state: %s", strerror(errno));
470       whitter("set debugging for `%s'", current);
471       break;
472
473     case YNQ_NO:
474       if (ioctl(fd, UNIOCSDEBUG, 0) < 0)
475         die("error clearing debug state: %s", strerror(errno));
476       whitter("cleared debugging for `%s'", current);
477       break;
478   }
479
480   return (run(av));
481 }
482       
483 /* --- @cmd_gdebug@ --- */
484
485 static int cmd_gdebug(char **av)
486 {
487   check();
488   switch (ynq(*av++)) {
489
490     case YNQ_QUERY: {
491       int i = ioctl(fd, UNIOCGGDEBUG);
492       if (i < 0)
493         die("error reading global debug state: %s", strerror(errno));
494       else
495         printf("global debugging is %s\n", i ? "enabled" : "disabled");
496     } break;
497
498     case YNQ_YES:
499       if (ioctl(fd, UNIOCSGDEBUG, 1) < 0)
500         die("error setting global debug state: %s", strerror(errno));
501       whitter("set global debugging");
502       break;
503
504     case YNQ_NO:
505       if (ioctl(fd, UNIOCSGDEBUG, 0) < 0)
506         die("error clearing global debug state: %s", strerror(errno));
507       whitter("cleared global debugging");
508       break;
509   }
510
511   return (run(av));
512 }
513
514 /* --- @cmd_maxif@ --- */
515
516 static int cmd_maxif(char **av)
517 {
518   check();
519   if (!*av) {
520     int i = ioctl(fd, UNIOCGMAXIF);
521     if (i < 0)
522       die("error reading maxif: %s", strerror(errno));
523     else
524       printf("interface maximum is %d\n", i);
525   } else {
526     char *p = *av++, *q;
527     long i = strtol(p, &q, 0);
528     if (*q)
529       die("malformed integer: %s", p);
530     if (ioctl(fd, UNIOCSMAXIF, i))
531       die("error setting maxif: %s", strerror(errno));
532   }
533
534   return (run(av));
535 }
536
537 /* --- @cmd_ifflags@ --- */
538
539 static int cmd_ifflags(char **av)
540 {
541   static const struct iftab {
542     const char *name;
543     unsigned f;
544   } iftab[] = {
545     { "broadcast",      IFF_BROADCAST },
546     { "loopback",       IFF_LOOPBACK },
547     { "pointopoint",    IFF_POINTOPOINT },
548     { 0,                0 }
549   };
550
551   char *p;
552   int sense;
553   struct iftab *ift;
554   int f;
555
556   check();
557   if ((f = ioctl(fd, UNIOCGIFFLAGS)) < 0)
558     die("error reading interface flags: %s", strerror(errno));
559
560   for (p = strtok(*av++, ","); p; p = strtok(0, ",")) {
561     sense = 1;
562     switch (*p) {
563       case '-': sense = 0;
564       case '+': p++;
565         break;
566     }
567     ift = LOOKUP(iftab, p);
568     if (!ift)
569       die("unknown interface flag `%s'", p);
570     if (sense)
571       f |= ift->f;
572     else
573       f &= ~ift->f;
574   }
575   if (ioctl(fd, UNIOCSIFFLAGS, f))
576     die("error setting interface flags: %s", strerror(errno));
577   return (run(av));
578 }
579
580 /* --- @run@ --- *
581  *
582  * Arguments:   @char **av@ = array of command line arguments
583  *
584  * Returns:     Zero for success, nonzero for failure
585  *
586  * Use:         Handles a sequence of commands.
587  */
588
589 static int run(char **av)
590 {
591   struct command *c;
592   int i;
593
594   if (!*av)
595     return (EXIT_SUCCESS);
596   if ((c = LOOKUP(cmdtab, *av)) == 0)
597     c = &cmdtab[0];
598   else
599     av++;
600   if (!c->func)
601     die("command `%s' not implemented", c->cmd);
602   for (i = 0; i < c->minarg; i++) {
603     if (!av[i])
604       die("Usage: %s", c->syn);
605   }
606   return (c->func(av));
607 }
608
609 /* --- The command definition block --- */
610
611 static struct command cmdtab[] = {
612
613   { "select", 1, cmd_select,
614     "[select] FILE", "select a Usernet device", "\
615 Selects FILE as the current device.  Each command acts only on the current\n\
616 device.  The word `select' may be omitted if desired.\n\
617 " },
618
619   {
620     "fd", 1, cmd_fd,
621     "fd NUMBER", "select Usernet device from a file descriptor", "\
622 Selects the file descriptor NUMBER as the current device.  This allows\n\
623 configuration of already existing transient attachments which would\n\
624 otherwise be unnecessarily awkward.\n\
625 " },
626
627   { "show", 0, cmd_show,
628     "show", "show status of device", "\
629 Displays status information about the current device.\n\
630 " },
631
632   { "ifname", 0, cmd_ifname,
633     "ifname", "print attached network interface name", "\
634 Displays the name of the network interface attached to the current device.\n\
635 This is useful in configuration scripts, for example.\n\
636 " },
637
638   { "protocol", 1, cmd_protocol,
639     "protocol PROT", "selects PROT as the device's protocol", "\
640 Sets PROT as the current device's protocol.  All packets received by the\n\
641 device are stamped with this protocol tag.  To see a list of currently\n\
642 known protocols, use the `help' protocol.\n\
643 " },
644
645   { "debug", 1, cmd_debug,
646     "debug on|off|query", "set attachment debugging state", "\
647 Set the current debugging state for the attachment.  When debugging is\n\
648 enabled, all packets flowing through the attachment are logged, along with\n\
649 a large number of other informative messages.  Note: the logs generated\n\
650 tend to be very large, so don't flood the interface with data while\n\
651 debugging is enabled!\n\
652 " },
653
654   { "gdebug", 1, cmd_gdebug,
655     "gdebug on|off|query", "set global debugging state", "\
656 Set the global debugging state for Usernet.  This controls the emission\n\
657 emission of various non-attachment-specific message.  Also, new\n\
658 attachments inherit their debug flags from the global flag.\n\
659 " },
660
661   { "maxif", 0, cmd_maxif,
662     "maxif [MAX]", "set maximum number of interfaces allowed", "\
663 Configures the maximum number of interfaces allowed.\n\
664 " },
665
666   { "ifflags", 1, cmd_ifflags,
667     "ifflags [-]FLAG,[-]FLAG,...", "set interface flags", "\
668 Configures the network interface flags, because `ifconfig' is prevented\n\
669 from doing the job properly.\n\
670 " },
671
672   { "help", 0, cmd_help,
673     "help [COMMAND]", "display help about COMMAND", "\
674 If COMMAND is given, display help about it.  If no COMAMND is specified,\n\
675 provide general help.\n\
676 " },
677
678   { 0, 0, 0, 0 }
679 };
680
681 /* --- Protocol description table --- */
682
683 static struct eth_proto eth_prototab[] = {
684   { "loop",             ETH_P_LOOP,     "Ethernet loopback" },
685   { "echo",             ETH_P_ECHO,     "Ethernet echo" },
686   { "pup",              ETH_P_PUP,      "Xerox PUP" },
687   { "ip",               ETH_P_IP,       "Internet IP" },
688   { "x25",              ETH_P_X25,      "CCITT X.25" },
689   { "arp",              ETH_P_ARP,      "Address resolution protocol" },
690   { "bpq",              ETH_P_BPQ,      "G8BPQ AX.25" },
691   { "dec",              ETH_P_DEC,      "DEC assigned" },
692   { "dna-dl",           ETH_P_DNA_DL,   "DEC DNA dump/load" },
693   { "dna-rcon",         ETH_P_DNA_RC,   "DEC DNA remote console" },
694   { "dna-route",        ETH_P_DNA_RT,   "DEC DNA routing" },
695   { "lat",              ETH_P_LAT,      "DEC LAT" },
696   { "diag",             ETH_P_DIAG,     "DEC diagnostics" },
697   { "cust",             ETH_P_CUST,     "DEC customer user" },
698   { "sca",              ETH_P_SCA,      "DEC Systems Comminications "
699                                            "Architecture" },
700   { "rarp",             ETH_P_RARP,     "Reverse address resolution" },
701   { "atalk",            ETH_P_ATALK,    "Appletalk DDP" },
702   { "aarp",             ETH_P_AARP,     "Appletalk AARP" },
703   { "ipx",              ETH_P_IPX,      "Novell IPX" },
704   { "ipv6",             ETH_P_IPV6,     "Internet IPv6" },
705   { "help",             ETH_P_HELP,     "<magical help token>" },
706   { 0,                  0,              0 }
707 };
708
709 /*----- Informative messages ----------------------------------------------*/
710
711 /* --- @usage@ --- *
712  *
713  * Arguments:   @FILE *fp@ = stream to write on
714  *
715  * Returns:     ---
716  *
717  * Use:         Displays usage information for the program.
718  */
719
720 static void usage(FILE *fp)
721 {
722   fprintf(fp, "Usage: %s [-v] command ...\n", quis);
723 }
724
725 /* --- @version@ --- *
726  *
727  * Arguments:   ---
728  *
729  * Returns:     ---
730  *
731  * Use:         Displays the system's version number.
732  */
733
734 static void version(void)
735 {
736   printf("%s version %s\n", quis, VERSION);
737 }
738
739 /* --- @help@ --- *
740  *
741  * Arguments:   ---
742  *
743  * Returns:     Doesn't
744  *
745  * Use:         Displays some help and quits.
746  */
747
748 static void help(void)
749 {
750   struct command *c;
751   version();
752   putchar('\n');
753   usage(stdout);
754   putchar('\n');
755   puts("Commands provided:\n");
756   for (c = cmdtab; c->cmd; c++)
757     printf("%-30s%s\n", c->syn, c->shelp);
758   exit(0);
759 }
760
761 /* --- @main@ --- *
762  *
763  * Arguments:   @int argc@ = number of command line arguments received
764  *              @char *argv[]@ = pointers to the command line arguments
765  *
766  * Returns:     Zero for success, nonzero for failure
767  *
768  * Use:         Dumps and manipulates Usernet attachments.
769  */
770
771 int main(int argc, char *argv[])
772 {
773   /* --- Set the program name properly --- */
774
775   if (argc >= 1) {
776     if ((quis = strrchr(argv[0], '/')) == 0)
777       quis = argv[0];
778     else
779       quis++;
780   }
781
782   /* --- Now start parsing options --- */
783
784   for (;;) {
785     int i;
786
787     static struct option opt[] = {
788       { "help",                 0,      0,      'h' },
789       { "usage",                0,      0,      'U' },
790       { "version",              0,      0,      'V' },
791       { "verbose",              0,      0,      'v' },
792       { 0,                      0,      0,      0 }
793     };
794
795     if ((i = getopt_long(argc, argv, "+hVv", opt, 0)) < 0)
796       break;
797     switch (i) {
798       case 'h':
799         help();
800         break;
801       case 'U':
802         usage(stdout);
803         exit(0);
804         break;
805       case 'V':
806         version();
807         break;
808       case 'v':
809         flags |= f_verbose;
810         break;
811       default:
812         flags |= f_duff;
813         break;
814     }
815   }
816
817   if (flags & f_duff) {
818     usage(stderr);
819     printf("(Type `%s --help' for more information.)\n", quis);
820     exit(EXIT_FAILURE);
821   }
822
823   return (run(argv + optind));
824 }
825
826 /*----- That's all, folks -------------------------------------------------*/