minor fix to return E_USAGE on -V instead of exit(0);
[oss-qm-packages.git] / nameif.c
blob8d79b504d6815b5e7380a95386daf6a7b39ddc28
1 /*
2 * Name Interfaces based on MAC address.
3 * Writen 2000 by Andi Kleen.
4 * Subject to the Gnu Public License, version 2.
5 * TODO: make it support token ring etc.
6 * $Id: nameif.c,v 1.1 2000/10/18 17:26:29 ak Exp $
7 */
8 #ifndef _GNU_SOURCE
9 #define _GNU_SOURCE
10 #endif
11 #include <stdio.h>
12 #include <getopt.h>
13 #include <sys/syslog.h>
14 #include <errno.h>
15 #include <stdlib.h>
16 #include <ctype.h>
17 #include <string.h>
18 #include <stdarg.h>
19 #include <sys/socket.h>
20 #include <sys/ioctl.h>
21 #include <net/if.h>
22 #include <linux/sockios.h>
23 #include <errno.h>
24 #include "intl.h"
26 const char default_conf[] = "/etc/mactab";
27 const char *fname = default_conf;
28 int use_syslog;
29 int ctl_sk = -1;
31 void err(char *msg)
33 if (use_syslog) {
34 syslog(LOG_ERR,"%s: %m", msg);
35 } else {
36 perror(msg);
38 exit(1);
41 void complain(char *fmt, ...)
43 va_list ap;
44 va_start(ap,fmt);
45 if (use_syslog) {
46 vsyslog(LOG_ERR,fmt,ap);
47 } else {
48 vfprintf(stderr,fmt,ap);
49 fputc('\n',stderr);
51 va_end(ap);
52 exit(1);
55 void warning(char *fmt, ...)
57 va_list ap;
58 va_start(ap,fmt);
59 if (use_syslog) {
60 vsyslog(LOG_ERR,fmt,ap);
61 } else {
62 vfprintf(stderr,fmt,ap);
63 fputc('\n',stderr);
65 va_end(ap);
68 int parsemac(char *str, unsigned char *mac)
70 char *s;
71 while ((s = strsep(&str, ":")) != NULL) {
72 unsigned byte;
73 if (sscanf(s,"%x", &byte)!=1 || byte > 0xff)
74 return -1;
75 *mac++ = byte;
77 return 0;
80 void *xmalloc(unsigned sz)
82 void *p = calloc(sz,1);
83 if (!p) errno=ENOMEM, err("xmalloc");
84 return p;
87 void opensock(void)
89 if (ctl_sk < 0)
90 ctl_sk = socket(PF_INET,SOCK_DGRAM,0);
93 #ifndef ifr_newname
94 #define ifr_newname ifr_ifru.ifru_slave
95 #endif
97 int setname(char *oldname, char *newname)
99 struct ifreq ifr;
100 opensock();
101 memset(&ifr,0,sizeof(struct ifreq));
102 strcpy(ifr.ifr_name, oldname);
103 strcpy(ifr.ifr_newname, newname);
104 return ioctl(ctl_sk, SIOCSIFNAME, &ifr);
107 int getmac(char *name, unsigned char *mac)
109 int r;
110 struct ifreq ifr;
111 opensock();
112 memset(&ifr,0,sizeof(struct ifreq));
113 strcpy(ifr.ifr_name, name);
114 r = ioctl(ctl_sk, SIOCGIFHWADDR, &ifr);
115 memcpy(mac, ifr.ifr_hwaddr.sa_data, 6);
116 return r;
119 struct change {
120 struct change *next,**pprev;
121 char ifname[IFNAMSIZ+1];
122 unsigned char mac[6];
124 struct change *clist;
126 struct change *lookupmac(unsigned char *mac)
128 struct change *ch;
129 for (ch = clist;ch;ch = ch->next)
130 if (!memcmp(ch->mac, mac, 6))
131 return ch;
132 return NULL;
135 int addchange(char *p, struct change *ch, char *pos)
137 if (strchr(ch->ifname, ':'))
138 warning(_("alias device %s at %s probably has no mac"),
139 ch->ifname, pos);
140 if (parsemac(p,ch->mac) < 0)
141 complain(_("cannot parse MAC `%s' at %s"), p, pos);
142 if (clist)
143 clist->pprev = &ch->next;
144 ch->next = clist;
145 ch->pprev = &clist;
146 clist = ch;
147 return 0;
150 void readconf(void)
152 char *line;
153 size_t linel;
154 int linenum;
155 FILE *ifh;
156 char *p;
157 int n;
159 ifh = fopen(fname, "r");
160 if (!ifh)
161 complain(_("opening configuration file %s: %s"),fname,strerror(errno));
163 line = NULL;
164 linel = 0;
165 linenum = 1;
166 while (getdelim(&line, &linel, '\n', ifh) > 0) {
167 struct change *ch = xmalloc(sizeof(struct change));
168 char pos[20];
170 sprintf(pos, _("line %d"), linenum);
172 if ((p = strchr(line,'#')) != NULL)
173 *p = '\0';
174 p = line;
175 while (isspace(*p))
176 ++p;
177 if (*p == '\0')
178 continue;
179 n = strcspn(p, " \t");
180 if (n > IFNAMSIZ)
181 complain(_("interface name too long at line %d"), line);
182 memcpy(ch->ifname, p, n);
183 ch->ifname[n] = 0;
184 p += n;
185 p += strspn(p, " \t");
186 n = strspn(p, "0123456789ABCDEFabcdef:");
187 p[n] = 0;
188 addchange(p, ch, pos);
189 linenum++;
191 fclose(ifh);
194 struct option lopt[] = {
195 {"syslog", 0, NULL, 's' },
196 {"config-file", 1, NULL, 'c' },
197 {"help", 0, NULL, '?' },
198 {NULL},
201 void usage(void)
203 fprintf(stderr, _("usage: nameif [-c configurationfile] [-s] {ifname macaddress}"));
204 exit(1);
207 int main(int ac, char **av)
209 FILE *ifh;
210 char *p;
211 int n;
212 int linenum;
213 char *line = NULL;
214 size_t linel = 0;
216 for (;;) {
217 int c = getopt_long(ac,av,"c:s",lopt,NULL);
218 if (c == -1) break;
219 switch (c) {
220 default:
221 case '?':
222 usage();
223 case 'c':
224 fname = optarg;
225 break;
226 case 's':
227 use_syslog = 1;
228 break;
232 if (use_syslog)
233 openlog("nameif",0,LOG_LOCAL0);
235 while (optind < ac) {
236 struct change *ch = xmalloc(sizeof(struct change));
237 char pos[30];
239 if ((ac-optind) & 1)
240 usage();
241 if (strlen(av[optind])+1>IFNAMSIZ)
242 complain(_("interface name `%s' too long"), av[optind]);
243 strcpy(ch->ifname, av[optind]);
244 optind++;
245 sprintf(pos,_("argument %d"),optind);
246 addchange(av[optind], ch, pos);
247 optind++;
250 if (!clist || fname != default_conf)
251 readconf();
253 ifh = fopen("/proc/net/dev", "r");
254 if (!ifh) complain(_("open of /proc/net/dev: %s"), strerror(errno));
257 linenum = 0;
258 while (getdelim(&line, &linel, '\n', ifh) > 0) {
259 struct change *ch;
260 unsigned char mac[6];
262 if (linenum++ < 2)
263 continue;
265 p = line;
266 while (isspace(*p))
267 ++p;
268 n = strcspn(p, ": \t");
269 p[n] = 0;
271 if (n > IFNAMSIZ-1)
272 complain(_("interface name `%s' too long"), p);
274 if (getmac(p, mac) < 0)
275 continue;
277 ch = lookupmac(mac);
278 if (!ch)
279 continue;
281 *ch->pprev = ch->next;
282 if (strcmp(p, ch->ifname)) {
283 if (setname(p, ch->ifname) < 0)
284 complain(_("cannot change name of %s to %s: %s"),
285 p, ch->ifname, strerror(errno));
287 free(ch);
289 fclose(ifh);
291 while (clist) {
292 struct change *ch = clist;
293 clist = clist->next;
294 warning(_("interface '%s' not found"), ch->ifname);
295 free(ch);
298 if (use_syslog)
299 closelog();
300 return 0;