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 */
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. */
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) {
0008089 return;
0008090 }
0008091 if (unlink(new) < 0) fatal(new);
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;
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
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
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 entry 1st entry 2nd entry 3rd entry
interface type NETTYPE_ETH NETTYPE_PSIP 0 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);
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);
0008229 token(1);
0008230 ecp->ec_port= number(word, IP_PORT_MAX-1);
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) {
0008236 pcp->pc_ifno= ifno= number(word+4, IP_PORT_MAX-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 {
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) {
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 }
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
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 */
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.
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