Please wait until the page is fully downloaded and then press the "Expand" button or the blue line numbers.

0008001 /*
0008002 inet/inet_config.c
0008003 
0008004 Created:       Nov 11, 1992 by Philip Homburg
0008005 
0008006 Modified:       Apr 07, 2001 by Kees J. Bot
0008007                   Read the configuration file and fill in the xx_conf[] arrays.
0008008 
0008009 Copyright 1995 Philip Homburg
0008010 */
eth_conf[] / psip_conf[] / ip_conf[] / iftype[]

read_conf(), called by nw_init(), reads the configuration file /etc/inet.conf and populates the arrays eth_conf[], psip_conf[], ip_conf[] and iftype[].

An example inet.conf file and the resulting arrays are shown in the following figure:



Note that "psip" stands for "pseudo ip", which is generally used over serial lines (as opposed to ethernet).

A fourth array, iftype[], correlates the interface type with the interface number (the index of the array). Since the same information in if_type[] can be found in ip_conf[], the code could have been rewritten using ip_conf[] where if_type[] was used. However, this would have limited the flexibility of the code since a network protocol other than ip could be added later.


iftype[]
0th entry1st entry2nd entry3rd entry
interface typeNETTYPE_ETH NETTYPE_PSIP0 0


In addition to populating the four arrays, read_conf() also creates the necessary device files. For example, for the inet.conf file above, the following files (and their minor device numbers in parentheses) are created:

/dev/eth0 (1), /dev/ip0 (2), /dev/tcp0 (3), /dev/udp0 (4)
/dev/psip1 (17), /dev/ip1 (18), /dev/tcp1 (19), /dev/udp1 (20)

Note that the major device number for all of the network device files is set to the major device number of the /dev/ip file, which must be created before the network service can be started. The value of this major device number is 7 for the default configuration.

Links are also created for the default entry. For example, for the inet.conf file above, the following links are created:

/dev/eth -> /dev/eth0
/dev/ip -> /dev/ip0
/dev/tcp -> /dev/tcp0
/dev/udp -> /dev/udp0


From inet(8):


Inet starts as a normal process, reads the configuration file
/etc/inet.conf to see what it should do, and uses a few special low level
system calls to turn itself into a server. The format of the
configuration file is as follows:

Configuration
The inet configuration file is fairly simple, here is an example:

eth0 DP8390 0 { default; };
psip1;

It tells that network 0 (the one containing devices eth0, ip0, tcp0 and
udp0) uses the ethernet device driver handled by task "DP8390" at port 0.
This network is marked as the default network, so most programs use it
through the unnumbered devices like /dev/tcp or /dev/udp. Network 1 is a
Pseudo IP network that can be used for a serial IP over a modem for
instance.

ethN task port {options};
This sets up an ethernet with device name /dev/ethN, built on the
given ethernet device driver at the given port at that driver. (If
a network driver manages two network cards then they are at port 0
and 1.)


psipN {options};
Creates pseudo IP network /dev/psipN, usable for IP over serial
lines, tunnels and whatnot.



0008011 
0008012 #define _MINIX       1
_MINIX

_MINIX is a macro that can affect whether certain lines within include files are actually included by the preprocessor. For example, if a file #include's <string.h> (as inet_config.c does), then the following lines containing prototypes that enable backward compatibility are included by the preprocessor:

#ifdef _MINIX

/* For backward compatibility. */
_PROTOTYPE( char *index, (const char *_s, int _charwanted) );
_PROTOTYPE( char *rindex, (const char *_s, int _charwanted) );
_PROTOTYPE( void bcopy, (const void *_src, void *_dst, size_t _length) );
_PROTOTYPE( int bcmp, (const void *_s1, const void *_s2, size_t _length));
_PROTOTYPE( void bzero, (void *_dst, size_t _length) );
_PROTOTYPE( void *memccpy, (char *_dst, const char *_src, int _ucharstop,
size_t _size) );
/* Misc. extra functions */
_PROTOTYPE( int strcasecmp, (const char *_s1, const char *_s2) );
_PROTOTYPE( int strncasecmp, (const char *_s1, const char *_s2,
size_t _len) );
_PROTOTYPE( size_t strnlen, (const char *_s, size_t _n) );
#endif
Note that nearly all macros that begin with an underscore (_) are specific to library routines.

The 12th paragraph of section 2.6.2, lines 01184-011200, and lines 01241-01245 in the Minix code in Operating Systems, Design and Implementation describe the _MINIX macro.


0008013 
0008014 #include <stdlib.h>
0008015 #include <unistd.h>
0008016 #include <fcntl.h>
0008017 #include <string.h>
0008018 #include <errno.h>
0008019 #include <sys/types.h>
0008020 #include <sys/stat.h>
0008021 #include <minix/config.h>
0008022 #include <minix/type.h>
0008023 #include <minix/syslib.h>
0008024 #include "inet_config.h"
0008025 
0008026 #define CRAMPED (_EM_WSIZE==2)       /* 64K code and data is quite cramped. */
CRAMPED

CRAMPED is #define'd in inet.h as 0 for an 80836 processor or better and 1 for anything less (e.g., 8086). In the figures and discussions in this documentation, it is assumed that the system is an 80836 or better.

If the system is not even an 80386, it is a fair assumption that there is not much memory to work with (i.e., the memory is "cramped").


0008027 #if CRAMPED
0008028 #endif
0008029 
eth_conf[], psip_conf[], and ip_conf[] are described above on line 8001.

IP_PORT_MAX is #define'd as 4 for the 80386 (and 2 for the 8086). In other words, there can be at most 4 network interfaces (2 ethernet interfaces and 2 psip interfaces) for an 80386 Minix system.


0008030 struct eth_conf eth_conf[IP_PORT_MAX];
0008031 struct psip_conf psip_conf[IP_PORT_MAX];
0008032 struct ip_conf ip_conf[IP_PORT_MAX];
0008033 dev_t ip_dev;
0008034 
The following are described in inet/inet_config.h as:

eth_conf_nr; /* Number of ethernets */
psip_conf_nr; /* Number of Pseudo IP networks */
ip_conf_nr; /* Number of configured TCP/IP layers */

For the following inet.conf file:

eth0 DP8390 0 { default; };
psip1;

eth_conf_nr will be 1, psip_conf_nr will be 1 and ip_conf_nr will be 2.


0008035 int eth_conf_nr;
0008036 #if ENABLE_PSIP
0008037 int psip_conf_nr;
0008038 #endif
0008039 int ip_conf_nr;
0008040 
iftype[] is set on lines 8284 and 8285.

Each element will be either NETTYPE_ETH or NETTYPE_PSIP, depending on whether the interface is an ethernet device or a pseudo ip (psip) device.


0008041 static u8_t iftype[IP_PORT_MAX];       /* Interface in use as? */
0008042 static int ifdefault= -1;              /* Default network interface. */
ifdefault, initially -1 (an invalid number), eventually takes the number of the default. For the following example inet.conf file:



ifdefault will be 0 (which corresponds to the red 0).


0008043 
0008044 static void fatal(char *label)
fatal()

If fatal() is called, the network service is terminated (i.e., fatal() calls exit(); see src/lib/posix/__exit.c).


0008045 {
0008046          printf("init: %s: Error %d\n", label, errno);
0008047          exit(1);
0008048 }
0008049 
0008050 static void check_rm(char *device)
0008051 /* Check if a device is not among the living. */
check_rm()

check_rm() removes the specified file (if it exists) by calling unlink().


0008052 {
0008053          if (unlink(device) < 0) {
0008054                   if (errno == ENOENT) return;
If the file does not exist (i.e., ulink() returns ENOENT), it's not a problem. All other errors are unacceptable.

ENOENT (Error NO ENTry) is #define'd in include/errno.h.


0008055                   fatal(device);
fatal()

If fatal() is called, the network service is terminated (i.e., fatal() calls exit(); see src/lib/posix/__exit.c).


0008056          }
0008057          printf("rm %s\n", device);
0008058 }
0008059 
0008060 static void check_mknod(char *device, mode_t mode, int minor)
0008061 /* Check if a device exists with the proper device number. */
check_mknod()

check_mknod() checks whether a device exists with

1) the name device, check_mknod()'s first parameter (e.g., /dev/ip0)
2) the same major device number as /dev/ip (typically 7)
3) a minor device number of minor, check_mknod()'s third parameter

If such a device exists, check_mknod() simply returns. If the device does not exist, check_mknod() creates the character device with mknod().


0008062 {
0008063          struct stat st;
0008064          dev_t dev;
dev_t is defined in as:

typedef short dev_t; /* holds (major|minor) device pair */

A short is 2 bytes on both the 8086 and 80386. The first byte will hold the major device number and the second byte will hold the minor device number.

ip_dev is a global variable that is defined on line 8033 and is set in read_conf() on line 8285. ip_dev is set to the major device number of "/dev/ip", which must exist when the service is brought up. The high byte of dev is set to ip_dev and the low byte set to minor (check_mknod()'s third paramter).



0008065 
0008066          dev= (ip_dev & 0xFF00) | minor;
0008067 
0008068          if (stat(device, &st) < 0) {
0008069                   if (errno != ENOENT) fatal(device);
If the file doesn't exist (i.e., stat() returns ENOENT), it's not a problem. It will be created later.

ENOENT is #define'd in include/errno.h.


0008070          } else {
If the file exists, determine if it is a "character raw device" (i.e., S_ISCHR(st.st_mode) returns true) and if it is a character raw device, determine if the device file has the same major and minor device numbers that were just calculated. If so, simply return since this is the desired file. If not, remove the file with unlink().

S_ISCHR is #define'd in include/sys/stat.h.



0008071                   if (S_ISCHR(st.st_mode) && st.st_rdev == dev) return;
0008072                   if (unlink(device) < 0) fatal(device);
fatal()

If fatal() is called, the network service is terminated (i.e., fatal() calls exit(); see src/lib/posix/__exit.c).


0008073          }
0008074 
0008075          if (mknod(device, S_IFCHR | mode, dev) < 0) fatal(device);
0008076          printf("mknod %s c %d %d\n", device, (ip_dev >> 8), minor);
The character device file is created by the mknod() system call and the following message is printed:

mknod device c major minor

This is the same syntax as the unix mknod command that creates a character (c) device.

S_IFCHR is #define'd in include/sys/stat.h.



0008077 }
0008078 
0008079 static void check_ln(char *old, char *new)
0008080 /* Check if 'old' and 'new' are still properly linked. */
check_ln()

check_ln(old, new) determines if the file old, check_ln()'s first parameter, and new, check_ln()'s second parameter, share the same inode (i.e., they are linked). If the file new does exist but is not linked to the file old, remove the file new.


0008081 {
0008082          struct stat st_old, st_new;
0008083 
0008084          if (stat(old, &st_old) < 0) fatal(old);
0008085          if (stat(new, &st_new) < 0) {
stat() returns a file's information in the struct stat. Again, it's not a problem if the file doesn't exist (i.e., stat returns ENOENT). Any other error is a problem. (Note that stat returns a negative value if there is an error.)

If the file exists, the following fields are of interest:

st_dev (major/minor device number)
st_ino (this inode's number)


0008086                   if (errno != ENOENT) fatal(new);
0008087          } else {
0008088                   if (st_new.st_dev == st_old.st_dev && st_new.st_ino == st_old.st_ino) {
If the two files are on the same device and have the same inode, there's no need to do anything.


0008089                            return;
0008090                   }
0008091                   if (unlink(new) < 0) fatal(new);
If the two files (old and new) do not have the same inode number, unlink() the new file.


0008092          }
0008093 
0008094          if (link(old, new) < 0) fatal(new);
link() creates a hard link (i.e., they share an inode) between a file that did not previously exist (in this case, new) and an existing file (in this case, old).


0008095          printf("ln %s %s\n", old, new);
0008096 }
0008097 
0008098 static void check_dev(int type, int ifno)
0008099 /* Check if the device group with interface number 'ifno' exists and has the
0008100  * proper device numbers. If 'type' is -1 then the device group must be
0008101  * removed.
0008102  */
check_dev()

check_dev(), called by read_conf(), creates device files and links to the device files. For the following /etc/inet.conf file:

eth0 DP8390 0 { default; };
psip1;

check_dev() is called with the following two pairs of arguments:

NETTYPE_ETH, 0
NETTYPE_PSIP, 1

For the first call, check_dev() creates the files:

/dev/eth0, /dev/ip0, /dev/tcp0, /dev/udp0

and for the second call, check_dev() creates the files:

/dev/psip1, /dev/ip1, /dev/tcp1, /dev/udp1


0008103 {
0008104          static struct devlist {
0008105                   char       *defname;
0008106                   mode_t       mode;
0008107                   u8_t       minor_off;
0008108          } devlist[] = {
0008109                   {       "/dev/eth",       0600,       ETH_DEV_OFF       },
0008110                   {       "/dev/psip",       0600,       PSIP_DEV_OFF       },
0008111                   {       "/dev/ip",       0600,       IP_DEV_OFF       },
0008112                   {       "/dev/tcp",       0666,       TCP_DEV_OFF       },
0008113                   {       "/dev/udp",       0666,       UDP_DEV_OFF       },
0008114          };
The mask 0600 gives read and write (w=4 + r=2) permissions only to the
owner whereas 0666 gives read and write permissions to everyone. In
other words, the /dev/tcp and /dev/udp files are accessible by everyone.

The offsets of the different devices are #define'd in inet/inet_config.h:

#define ETH_DEV_OFF 1
#define PSIP_DEV_OFF 1
#define IP_DEV_OFF 2
#define TCP_DEV_OFF 3
#define UDP_DEV_OFF 4



0008115          struct devlist *dvp;
0008116          int i;
0008117          char device[sizeof("/dev/psip99")];
"/dev/psip99" is a dummy string that gives the maximum possible length of the string array device[].


0008118          char *dp;
0008119 
0008120          for (i= 0; i < sizeof(devlist) / sizeof(devlist[0]); i++) {
sizeof(devlist) / sizeof(devlist[0]) equals 5 for the above array. Therefore, the loop will be executed 5 times.


0008121                   dvp= &devlist[i];
0008122                   strcpy(device, dvp->defname);
Copy the non-numerical portion of the device name. For the example /etc/inet.conf file given above and the first time through this loop, "/dev/eth" will be copied for the ethernet device with an interface number of 0. The second time through this loop, "/dev/psip" will be copied. The third time through this loop, "/dev/ip" will be copied and so on.


0008123                   dp= device + strlen(device);
The next few lines of code append the interface number to the string previously copied (e.g., append "0" to "/dev/eth" for the resulting string "/dev/eth0").



0008124                   if (ifno >= 10) *dp++ = '0' + (ifno / 10);
0008125                   *dp++ = '0' + (ifno % 10);
0008126                   *dp = 0;
Add a null terminating character.


0008127 
0008128                   if (type == 0
0008129                            || (i == 0 && type != NETTYPE_ETH)
0008130                            || (i == 1 && type != NETTYPE_PSIP)
0008131                   ) {
0008132                            check_rm(device);
If type == NETTYPE_ETH, remove the /dev/psipX device and if type==NETTYPE_PSIP, remove the /dev/ethX device (where X is the interface number, ifno).


check_rm()


check_rm() removes the specified file (if it exists) by calling unlink().


0008133                            if (ifno == ifdefault) check_rm(dvp->defname);
ifdefault is set on line 8261. For the example /etc/inet.conf file:

eth0 DP8390 0 { default; };
psip1;

the /dev/psip device is removed (if it exists).


0008134                   } else {
This is where the files are actually created. For the example inet.conf file:

eth0 DP8390 0 { default; };
psip1;

These files will be created:

/dev/eth0, /dev/ip0, /dev/tcp0, /dev/udp0
/dev/psip1, /dev/ip1, /dev/tcp1, /dev/udp1

if2minor() is #define'd in inet_config.h as:

#define if2minor(ifno, dev) ((ifno) * 16 + (dev))

For example, for the example /etc/inet.conf file given above, the minor number for /dev/udp1 will be 1 * 16 + 4 = 20.

As described above, the ethernet, psip, and ip devices will have read and write permissions only for the owner and the udp and tcp devices will have read and write permissions for everyone.



0008135                            check_mknod(device, dvp->mode,
0008136                                     if2minor(ifno, dvp->minor_off));
For dev/ip1, check_mknod() would have the following parameters:

"/dev/ip1", 0600, 19



check_mknod()


check_mknod() checks whether a device exists with

1) the name device, check_mknod()'s first parameter (e.g., /dev/ip0)
2) the same major device number as /dev/ip (typically 7)
3) a minor device number of minor, check_mknod()'s third parameter

If such a device exists, check_mknod() simply returns. If the device does not exist, check_mknod() creates the character device with mknod().


0008137                            if (ifno == ifdefault) check_ln(device, dvp->defname);
For our example /etc/inet.conf file:

eth0 DP8390 0 { default; };
psip1;

a hard link will be made between the following files:

/dev/eth0 /dev/eth
/dev/ip0 /dev/ip
/dev/tcp0 /dev/tcp
/dev/udp0 /dev/udp

since the 0th interface number is the default.


check_ln()


check_ln(old, new) determines if the file old, check_ln()'s first parameter, and new, check_ln()'s second parameter, share the same inode (i.e., they are linked). If the file new does exist but is not linked to the file old, remove the file new.


0008138                   }
0008139          }
0008140 }
0008141 
0008142 static int cfg_fd;
0008143 static char word[16];
0008144 static unsigned line;
line is the line number of the configuration file /etc/inet.conf. line is used only to give error reports (line 8148).


0008145 
0008146 static void error(void)
0008147 {
0008148          printf("inet: error on line %u\n", line);
0008149          exit(1);
When exit() is called with an argument of 1, it indicates failure.

From include/stdlib.h:

#define EXIT_FAILURE 1 / * standard error return using exit() * /


0008150 }
0008151 
0008152 static void token(int need)
0008153 {
0008154          /* Read a word from the configuration file. Return a null string on
0008155           * EOF. Return a punctiation as a one character word. If 'need' is
0008156           * true then an actual word is expected at this point, so err out if
0008157           * not.
0008158           */
token()

token(need) (called repeatedly by read_conf()) grabs a token and places the token in the variable word[].

Examples of tokens include the following:

{
}
,
;
a word (e.g., "DP8390")

If need, token()'s only parameter, is 1, the calling function expects a word before the end of the file.


0008159          unsigned char *wp;
0008160          static unsigned char c= '\n';
0008161 
0008162          wp= (unsigned char *) word;
0008163          *wp = 0;
0008164 
Skip over the initial spaces and control characters.


0008165          while (c <= ' ') {
0008166                   if (c == '\n') line++;
0008167                   if (read(cfg_fd, &c, 1) != 1) {
0008168                            if (need) error();
0008169                            return;
0008170                   }
0008171          }
0008172 
Add characters to the array word[]. Quit if a space or a punctuation mark is encountered.


0008173          do {
0008174                   if (wp < (unsigned char *) word + sizeof(word)-1) *wp++ = c;
0008175                   if (read(cfg_fd, &c, 1) != 1) c= ' ';
0008176                   if (word[0] == ';' || word[0] == '{' || word[0] == '}') {
0008177                            if (need) error();
0008178                            break;
0008179                   }
0008180          } while (c > ' ' && c != ';' && c != '{' && c != '}');
0008181          *wp = 0;
0008182 }
0008183 
0008184 static unsigned number(char *str, unsigned max)
number()

number() parses a string of characters and returns an unsigned number. For example, number() returns the unsigned number 14 for the string "14".


0008185 {
0008186          /* Interpret a string as an unsigned decimal number, no bigger than
0008187           * 'max'. Return this number.
0008188           */
0008189          char *s;
0008190          unsigned n, d;
0008191 
0008192          s= str;
0008193          n= 0;
0008194          while ((d= (*s - '0')) < 10 && n <= max) {
Verify that the character is really a number. Also, ensure that the value isn't larger than the maximum allowed.

Note:

'5' - '0' = 5

is the same as

53 - 48 = 5

since the ascii value for zero (0) is 48 and the ascii value for five (5) is 53.

For strings with two digits (e.g., "25"), multiply the first digit (in the example, 2) by 10.


0008195                   n= n * 10 + d;
0008196                   s++;
0008197          }
Verify that the numberic string ends in a null. For example, "65J" is not acceptable. Also, ensure that the number doesn't exceed the maximum allowed.


0008198          if (*s != 0 || n > max) {
0008199                   printf("inet: '%s' is not a number <= %u\n", str, max);
0008200                   error();
0008201          }
0008202          return n;
0008203 }
0008204 
0008205 void read_conf(void)
eth_conf[] / psip_conf[] / ip_conf[] / iftype[]

read_conf(), called by nw_init(), reads the configuration file /etc/inet.conf and populates the arrays eth_conf[], psip_conf[], ip_conf[] and iftype[].

An example inet.conf file and the resulting arrays are shown in the following figure:



Note that "psip" stands for "pseudo ip", which is generally used over serial lines (as opposed to ethernet).

A fourth array, iftype[], correlates the interface type with the interface number (the index of the array). Since the same information in if_type[] can be found in ip_conf[], the code could have been rewritten using ip_conf[] where if_type[] was used. However, this would have limited the flexibility of the code since a network protocol other than ip could be added later.


iftype[]
0th entry1st entry2nd entry3rd entry
interface typeNETTYPE_ETH NETTYPE_PSIP0 0


In addition to populating the four arrays, read_conf() also creates the necessary device files. For example, for the inet.conf file above, the following files (and their minor device numbers in parentheses) are created:

/dev/eth0 (1), /dev/ip0 (2), /dev/tcp0 (3), /dev/udp0 (4)
/dev/psip1 (17), /dev/ip1 (18), /dev/tcp1 (19), /dev/udp1 (20)

Note that the major device number for all of the network device files is set to the major device number of the /dev/ip file, which must be created before the network service can be started. The value of this major device number is 7 for the default configuration.

Links are also created for the default entry. For example, for the inet.conf file above, the following links are created:

/dev/eth -> /dev/eth0
/dev/ip -> /dev/ip0
/dev/tcp -> /dev/tcp0
/dev/udp -> /dev/udp0


From inet(8):


Inet starts as a normal process, reads the configuration file
/etc/inet.conf to see what it should do, and uses a few special low level
system calls to turn itself into a server. The format of the
configuration file is as follows:

Configuration
The inet configuration file is fairly simple, here is an example:

eth0 DP8390 0 { default; };
psip1;

It tells that network 0 (the one containing devices eth0, ip0, tcp0 and
udp0) uses the ethernet device driver handled by task "DP8390" at port 0.
This network is marked as the default network, so most programs use it
through the unnumbered devices like /dev/tcp or /dev/udp. Network 1 is a
Pseudo IP network that can be used for a serial IP over a modem for
instance.

ethN task port {options};
This sets up an ethernet with device name /dev/ethN, built on the
given ethernet device driver at the given port at that driver. (If
a network driver manages two network cards then they are at port 0
and 1.)


psipN {options};
Creates pseudo IP network /dev/psipN, usable for IP over serial
lines, tunnels and whatnot.



0008206 {
0008207          int i, j, ifno, type, port;
0008208          struct eth_conf *ecp;
0008209          struct psip_conf *pcp;
0008210          struct ip_conf *icp;
0008211          struct stat st;
0008212 
0008213          /* Open the configuration file. */
0008214          if ((cfg_fd= open(PATH_INET_CONF, O_RDONLY)) == -1)
PATH_INET_CONF is #define'd as "/etc/inet.conf" in inet_conf.h on line 6091. Change the #define if you wish to specify another configuration file (i.e., if you are looking for job security).

open() opens a file and returns the file's file descriptor (fd) or returns -1 on error.


0008215                   fatal(PATH_INET_CONF);
fatal()

If fatal() is called, the network service is terminated (i.e., fatal() calls exit(); see src/lib/posix/__exit.c).


0008216 
0008217          ecp= eth_conf;
0008218          pcp= psip_conf;
0008219          icp= ip_conf;
0008220 
0008221          while (token(0), word[0] != 0) {
0008222                   if (strncmp(word, "eth", 3) == 0) {
If read_conf() is parsing the following line in inet.conf:

eth0 DP8390 0 { default; };

ec_ifno will be 0 (which corresponds to the first 0), ec_task will be "DP8390" for this configured interface and ec_port will be 0. The type will be NETTYPE_ETH.

IP_PORT_MAX is #define'd in inet_config.h as 4 for the 80386 in protected mode and 2 for the 8086.


0008223                            ecp->ec_ifno= ifno= number(word+3, IP_PORT_MAX-1);
For the example inet.conf file below, ec_ifno will correspond to the red 0.






0008224                            type= NETTYPE_ETH;
0008225                            port= eth_conf_nr;
eth_conf_nr is 0 for the first ethernet device in the configuration file and 1 for the second ethernet device.

eth_conf_nr is defined on line 8035.


0008226                            token(1);
0008227                            ecp->ec_task= alloc(strlen(word)+1);
Alocate space for the ec_task field (i.e., Kernel ethernet task name) field of struct eth_conf. The '+1' is for the terminating null character.


alloc()


On the Minix system, the memory for a process is divided in the following manner:



Instructions (e.g., MOV AX, BX) are stored in the text segment, initialized global variables are stored in the data segment, and uninitialized global variables are stored in the bss. Dynamically allocated memory is allocated from the heap (generally using malloc()), and automatic variables (among other things) are allocated from the stack.

The "break" is the boundary between the (data + bss + the space previously allocated from the heap) and the unallocated space from the heap. alloc(size_t size) increases the size of the (data + bss + already allocated space from the heap) (by calling sbrk()) and returns a pointer to the newly claimed area. If size, alloc()'s only parameter, is a multiple of 4, size bytes are claimed. If size is not a multiple of 4, the value is rounded up to the next multiple of 4 and this space is claimed.


0008228                            strcpy(ecp->ec_task, word);
For the example inet.conf file below, ec_task will correspond to the blue "DP8390".






0008229                            token(1);
0008230                            ecp->ec_port= number(word, IP_PORT_MAX-1);
For the example inet.conf file below, ec_port will correspond to the green 0.






0008231                            ecp++;
Move pointer ecp so that it's ready for a (possible) additional ethernet device in the configuration file.


0008232                            eth_conf_nr++;
0008233 #if ENABLE_PSIP
0008234                   } else
0008235                   if (strncmp(word, "psip", 4) == 0) {
For the following inet.conf entry:

psip1;

the pc_ifno will be 1.


0008236                            pcp->pc_ifno= ifno= number(word+4, IP_PORT_MAX-1);
For the example inet.conf file below, pc_ifno will correspond to the orange 1.






0008237                            type= NETTYPE_PSIP;
0008238                            port= psip_conf_nr;
psip_conf_nr is 0 for the first pseudo ip (psip) device in the configuration file and 1 for the second psip device.


0008239                            pcp++;
Move pointer pcp so that it's ready for a (possible) additional pseudo ip (psip) device in the configuration file.


0008240                            psip_conf_nr++;
0008241 #endif
0008242                   } else {
Only ethernet and pseudo ip (psip) entries in the inet.conf file are acceptable.


0008243                            printf("inet: Unknown device '%s'\n", word);
0008244                            error();
0008245                   }
Fill in the fields for the arrays iftype[] and ip_conf[]. For a description of these arrays, click here.


0008246                   iftype[ifno]= type;
0008247                   icp->ic_ifno= ifno;
0008248                   icp->ic_devtype= type;
If the current line in the configuration file being processed is an ethernet device, type will be NETTYPE_ETH. If it's a psip device, type will be NETTYPE_PSIP.


0008249                   icp->ic_port= port;
PORT was set on either 8225 or 8237, depending on whether the line currently being processed is an ethernet device or a psip device. PORT will be 0 for the first ethernet device listed in inet.conf and 1 for the second ethernet device. PORT will be 0 for the first psip device listed in inet.conf and 1 for the second psip device.


0008250 
0008251                   token(0);
token()

token(need) (called repeatedly by read_conf()) grabs a token and places the token in the variable word[].

Examples of tokens include the following:

{
}
,
;
a word (e.g., "DP8390")

If need, token()'s only parameter, is 1, the calling function expects a word before the end of the file.


0008252                   if (word[0] == '{') {
0008253                            token(0);
token()

token(need) (called repeatedly by read_conf()) grabs a token and places the token in the variable word[].

Examples of tokens include the following:

{
}
,
;
a word (e.g., "DP8390")

If need, token()'s only parameter, is 1, the calling function expects a word before the end of the file.


0008254                            if (strcmp(word, "default") == 0) {
Here we have reached:

{ default; };

in the configuration file.

In our example, the default device is eth0. Since this is the default device, the following links will be created (see line 8137):

/dev/eth -> /dev/eth0
/dev/ip -> /dev/ip0
/dev/tcp -> /dev/tcp0
/dev/udp -> /dev/udp0


0008255                                     if (ifdefault != -1) {
Ensure that no more than one default device was specified.


0008256                                              printf(
0008257                            "inet: ip%d and ip%d can't both be default\n",
0008258                                                 ifdefault, ifno);
0008259                                              error();
0008260                                     }
0008261                                     ifdefault= ifno;
0008262                                     token(0);
token()

token(need) (called repeatedly by read_conf()) grabs a token and places the token in the variable word[].

Examples of tokens include the following:

{
}
,
;
a word (e.g., "DP8390")

If need, token()'s only parameter, is 1, the calling function expects a word before the end of the file.


0008263                            }
Ensure that no deviations of the following string:

{ default; }

are encountered.


0008264                            if (word[0] == ';') token(0);
0008265                            if (word[0] != '}') error();
0008266                            token(0);
0008267                   }
0008268                   if (word[0] != ';' && word[0] != 0) error();
0008269                   icp++;
0008270                   ip_conf_nr++;
ip_conf_nr will be 0 for the first device listed in the configuration file (regardless whether the device was an ethernet or a psip device) and 1 for the second device listed in the configuration file and so on.


0008271          }
0008272 
Ensure that one of the devices was specified as the default device.


0008273          if (ifdefault == -1) {
0008274                   printf("inet: No networks or no default network defined\n");
0008275                   exit(1);
0008276          }
0008277 
0008278          /* Set umask 0 so we can creat mode 666 devices. */
0008279          (void) umask(0);
umask() is used to prevent system calls (e.g., open()) from creating files with certain permissions. For example, if umask(022) is specified, even if open() tries to create a file with write permissions for the group and others, it won't be allowed.

umask(0) allows full permissions to be given to any file created.


0008280 
0008281          /* See what the device number of /dev/ip is. That's what we
0008282           * used last time for the network devices, so we keep doing so.
0008283           */
It is important to note that the device file "/dev/ip" must be created before the network service can be started. This can be accomplished by using the utility tool MAKEDEV.

stat() returns information about a file. We are concerned here only with its device number.



0008284          if (stat("/dev/ip", &st) < 0) fatal("/dev/ip");
0008285          ip_dev= st.st_rdev;
ip_dev now holds the major and minor numbers of /dev/ip (the higher byte holds the major number, the lower byte holds the minor number).



0008286 
0008287          for (i= 0; i < IP_PORT_MAX; i++) {
For the example /etc/inet.conf file given above, check_dev() will be called with the following two pairs of arguments:

NETTYPE_ETH, 0
NETTYPE_PSIP, 1

check_dev() creates all the special device files. For the inet.conf file given above, these will be:

/dev/eth0, /dev/ip0, /dev/tcp0, /dev/udp0
/dev/psip1, /dev/ip1, /dev/tcp1, /dev/udp1

check_dev() also creates links for the default device. For our example inet.conf file, the following links will be created:

/dev/eth0 /dev/eth
/dev/ip0 /dev/ip
/dev/tcp0 /dev/tcp
/dev/udp0 /dev/udp


0008288                   /* Create network devices. */
0008289                   check_dev(iftype[i], i);
0008290          }
0008291 }
0008292 
0008293 void *alloc(size_t size)
alloc()

On the Minix system, the memory for a process is divided in the following manner:



Instructions (e.g., MOV AX, BX) are stored in the text segment, initialized global variables are stored in the data segment, and uninitialized global variables are stored in the bss. Dynamically allocated memory is allocated from the heap (generally using malloc()), and automatic variables (among other things) are allocated from the stack.

The "break" is the boundary between the (data + bss + the space previously allocated from the heap) and the unallocated space from the heap. alloc(size_t size) increases the size of the (data + bss + already allocated space from the heap) (by calling sbrk()) and returns a pointer to the newly claimed area. If size, alloc()'s only parameter, is a multiple of 4, size bytes are claimed. If size is not a multiple of 4, the value is rounded up to the next multiple of 4 and this space is claimed.


0008294 {
0008295          /* Allocate memory on the heap with sbrk(). */
0008296 
0008297          return sbrk((size + (sizeof(char *) - 1)) & ~(sizeof(char *) - 1));
For the following equation:

value = (size + (sizeof(char *) - 1)) & ~(sizeof(char *) - 1)

value is equal to SIZE rounded up to the next multiple of 4.

If SIZE = 20:

value = (20 + (sizeof(char *) -1)) & ~(sizeof(char *) - 1)
= (20 + (4-1)) & ~(4 - 1)
= 00000000000000000000000000010100 & 11111111111111111111111111111100
= 10100 = 20


If SIZE = 23:

value = (23 + (sizeof(char *) -1)) & ~(sizeof(char *) - 1)
= (23 + (4-1)) & ~(4 - 1)
= 00000000000000000000000000011010 & 11111111111111111111111111111100
= 11000 = 24


Note that this is assuming that a pointer to a char (i.e., char *) is 4 bytes (32 bits). This is true for an 80386 running in protected mode.


0008298 }
0008299 
0008300 /*
0008301  * $PchId: inet_config.c,v 1.6 1998/10/23 20:15:27 philip Exp $
0008302  */