Move call to basename() into misc_get_minor()
[mkmisc.git] / mkmisc.c
1 /* mkmisc - make misc device nodes with dynamic minor number
2  * 
3  * Copyright (C) 2011  Antonio Ospite <ospite@studenti.unina.it>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 /* A misc device is a character device node which can have a dynamic minor
20  * number, see:
21  * http://www.linux.it/~rubini/docs/misc/misc.html
22  */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <libgen.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <ctype.h>
33
34 static int verbose = 0;
35 static int force = 0;
36 static unsigned int mode = 0664;
37
38 static int misc_get_major(void)
39 {
40         FILE *fp;
41         char line[256];
42         int major;
43         char name[64];
44
45         fp = fopen("/proc/devices", "r");
46         if (fp == NULL) {
47                 perror("/proc/devices");
48                 return -1;
49         }
50
51         while (fgets(line, sizeof(line) - 1, fp)) {
52                 if(sscanf(line, "%d %64s", &major, name) == 2)
53                         if (strcmp(name, "misc") == 0) {
54                                 fclose(fp);
55                                 return major;
56                         }
57         }
58         fclose(fp);
59
60         return -1;
61 }
62
63 static int misc_get_minor(char *device)
64 {
65         FILE *fp;
66         int minor;
67         char name[64];
68         char *path;
69         char *node;
70
71         fp = fopen("/proc/misc", "r");
72         if (fp == NULL) {
73                 perror("/proc/misc");
74                 return -1;
75         }
76
77         path = strdup(device);
78         node = basename(path);
79
80         while (fscanf(fp, "%d %64s", &minor, name) == 2) {
81                 if (strcmp(name, node) == 0) {
82                         free(path);
83                         fclose(fp);
84                         return minor;
85                 }
86         }
87         free(path);
88         fclose(fp);
89
90         return -1;
91 }
92
93 static int mkmisc(char *device)
94 {
95         int major;
96         int minor;
97         dev_t dev;
98         int ret;
99
100         major = misc_get_major();
101         if (major < 0) {
102                 fprintf(stderr, "Cannot get misc major number\n");
103                 return major;
104         }
105
106         minor = misc_get_minor(device);
107         if (minor < 0) {
108                 fprintf(stderr, "Cannot get misc minor for %s\n", device);
109                 return minor;
110         }
111
112         if (verbose)
113                 printf("Creating device: %s, major: %d, minor: %d\n",
114                        device, major, minor);
115
116         if (force) {
117                 ret = unlink(device);
118                 if (ret < 0) {
119                         perror("unlink");
120                         /*
121                         return ret;
122                         */
123                 }
124         }
125
126         dev = makedev(major, minor);
127         mode |= S_IFCHR;
128         ret = mknod(device, mode, dev);
129         if (ret < 0) {
130                 perror("mknod");
131                 return ret;
132         }
133
134         return 0;
135 }
136
137 static void usage(void)
138 {
139         printf("Usage: mkmisc [OPTION]... NAME\n");
140         printf("Create the special file NAME as a misc device with a dynamic minor number.\n");
141         printf("\n");
142         printf("OPTION can be any of:\n");
143         printf("  -f       force creation even if the file already exists\n");
144         printf("  -m MODE  set file permission bits to MODE, not a=rw - umask\n");
145         printf("  -v       verbose output\n");
146         printf("  -h       display this help and exit\n");
147 }
148
149 static void parse_options(int argc, char *argv[])
150 {
151         int c;
152         int ret;
153
154         while ((c = getopt (argc, argv, "fm:vh")) != -1)
155                 switch (c) {
156                 case 'f':
157                         force = 1;
158                         break;
159                 case 'm':
160                         ret = sscanf(optarg, "0%o", &mode);
161                         if (ret != 1) {
162                                 fprintf(stderr, "mode must be octal (e.g 0664)\n");
163                                 exit(EXIT_FAILURE);
164                         }
165                         break;
166                 case 'v':
167                         verbose = 1;
168                         break;
169                 case 'h':
170                         usage();
171                         exit(0);
172                 case '?':
173                         /* skip known options with missing arguments */
174                         if (optopt != 'm') {
175                                 if(isprint(optopt))
176                                         fprintf (stderr,
177                                                  "Unknown option `-%c'.\n",
178                                                  optopt);
179                                 else
180                                         fprintf (stderr,
181                                                  "Unknown option character `\\x%x'.\n",
182                                                  optopt);
183                         }
184                 default:
185                         usage();
186                         exit(EXIT_FAILURE);
187                 }
188 }
189
190 int main(int argc, char *argv[])
191 {
192         int ret;
193         char *device = NULL;
194
195         parse_options(argc, argv);
196
197         if (optind >= argc) {
198                 fprintf(stderr, "Missing argument NAME\n\n");
199                 usage();
200                 exit(EXIT_FAILURE);
201         } else if ((argc - optind) > 1) {
202                 fprintf(stderr, "Too many arguments\n\n");
203                 usage();
204                 exit(EXIT_FAILURE);
205         }
206
207         device = strdup(argv[optind]);
208
209         ret = mkmisc(device);
210         if (ret < 0)
211                 fprintf(stderr, "Cannot create %s\n", device);
212
213         free(device);
214
215         return ret;
216 }