4e6b0bc074b391dff02bb68a1c72ed073e972314
[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 *node)
64 {
65         FILE *fp;
66         int minor;
67         char name[64];
68
69         fp = fopen("/proc/misc", "r");
70         if (fp == NULL) {
71                 perror("/proc/misc");
72                 return -1;
73         }
74
75         while (fscanf(fp, "%d %64s", &minor, name) == 2) {
76                 if (strcmp(name, node) == 0) {
77                         fclose(fp);
78                         return minor;
79                 }
80         }
81         fclose(fp);
82
83         return -1;
84 }
85
86 static int mkmisc(char *device)
87 {
88         char *path;
89         char *node;
90         int major;
91         int minor;
92         dev_t dev;
93         int ret;
94
95         major = misc_get_major();
96         if (major < 0) {
97                 fprintf(stderr, "Cannot get misc major number\n");
98                 return major;
99         }
100
101         path = strdup(device);
102         node = basename(path);
103
104         minor = misc_get_minor(node);
105         if (minor < 0) {
106                 fprintf(stderr, "Cannot get misc minor for %s\n", device);
107                 free(path);
108                 return minor;
109         }
110         free(path);
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 }