Most .c files (AUTHORS): Revert the WRITTEN_BY/AUTHORS change
[platform/upstream/coreutils.git] / src / mknod.c
1 /* mknod -- make special files
2    Copyright (C) 90, 91, 1995-2003 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software Foundation,
16    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
17
18 /* Written by David MacKenzie <djm@ai.mit.edu>  */
19
20 #include <config.h>
21 #include <stdio.h>
22 #include <getopt.h>
23 #include <sys/types.h>
24
25 #include "system.h"
26 #include "error.h"
27 #include "modechange.h"
28 #include "quote.h"
29 #include "xstrtol.h"
30
31 /* The official name of this program (e.g., no `g' prefix).  */
32 #define PROGRAM_NAME "mknod"
33
34 #define AUTHORS "David MacKenzie"
35
36 /* The name this program was run with. */
37 char *program_name;
38
39 static struct option const longopts[] =
40 {
41   {"mode", required_argument, NULL, 'm'},
42   {GETOPT_HELP_OPTION_DECL},
43   {GETOPT_VERSION_OPTION_DECL},
44   {NULL, 0, NULL, 0}
45 };
46
47 void
48 usage (int status)
49 {
50   if (status != 0)
51     fprintf (stderr, _("Try `%s --help' for more information.\n"),
52              program_name);
53   else
54     {
55       printf (_("Usage: %s [OPTION]... NAME TYPE [MAJOR MINOR]\n"),
56               program_name);
57       fputs (_("\
58 Create the special file NAME of the given TYPE.\n\
59 \n\
60 "), stdout);
61       fputs (_("\
62 Mandatory arguments to long options are mandatory for short options too.\n\
63 "), stdout);
64       fputs (_("\
65   -m, --mode=MODE   set permission mode (as in chmod), not a=rw - umask\n\
66 "), stdout);
67       fputs (HELP_OPTION_DESCRIPTION, stdout);
68       fputs (VERSION_OPTION_DESCRIPTION, stdout);
69       fputs (_("\
70 \n\
71 Both MAJOR and MINOR must be specified when TYPE is b, c, or u, and they\n\
72 must be omitted when TYPE is p.  If MAJOR or MINOR begins with 0x or 0X,\n\
73 it is interpreted as hexadecimal; otherwise, if it begins with 0, as octal;\n\
74 otherwise, as decimal.  TYPE may be:\n\
75 "), stdout);
76       fputs (_("\
77 \n\
78   b      create a block (buffered) special file\n\
79   c, u   create a character (unbuffered) special file\n\
80   p      create a FIFO\n\
81 "), stdout);
82       printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
83     }
84   exit (status);
85 }
86
87 int
88 main (int argc, char **argv)
89 {
90   mode_t newmode;
91   struct mode_change *change;
92   const char *specified_mode;
93   int optc;
94   mode_t node_type;
95
96   initialize_main (&argc, &argv);
97   program_name = argv[0];
98   setlocale (LC_ALL, "");
99   bindtextdomain (PACKAGE, LOCALEDIR);
100   textdomain (PACKAGE);
101
102   atexit (close_stdout);
103
104   specified_mode = NULL;
105
106   while ((optc = getopt_long (argc, argv, "m:", longopts, NULL)) != -1)
107     {
108       switch (optc)
109         {
110         case 0:
111           break;
112         case 'm':
113           specified_mode = optarg;
114           break;
115         case_GETOPT_HELP_CHAR;
116         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
117         default:
118           usage (EXIT_FAILURE);
119         }
120     }
121
122   newmode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
123   if (specified_mode)
124     {
125       newmode &= ~ umask (0);
126       change = mode_compile (specified_mode, 0);
127       if (change == MODE_INVALID)
128         error (EXIT_FAILURE, 0, _("invalid mode"));
129       else if (change == MODE_MEMORY_EXHAUSTED)
130         xalloc_die ();
131       newmode = mode_adjust (newmode, change);
132     }
133
134   if (argc - optind != 2 && argc - optind != 4)
135     {
136       const char *msg;
137       if (argc - optind < 2)
138         msg = _("too few arguments");
139       else if (argc - optind > 4)
140         msg = _("too many arguments");
141       else
142         msg = _("wrong number of arguments");
143       error (0, 0, "%s", msg);
144       usage (EXIT_FAILURE);
145     }
146
147   /* Only check the first character, to allow mnemonic usage like
148      `mknod /dev/rst0 character 18 0'. */
149
150   switch (argv[optind + 1][0])
151     {
152     case 'b':                   /* `block' or `buffered' */
153 #ifndef S_IFBLK
154       error (4, 0, _("block special files not supported"));
155 #else
156       node_type = S_IFBLK;
157 #endif
158       goto block_or_character;
159
160     case 'c':                   /* `character' */
161     case 'u':                   /* `unbuffered' */
162 #ifndef S_IFCHR
163       error (4, 0, _("character special files not supported"));
164 #else
165       node_type = S_IFCHR;
166 #endif
167       goto block_or_character;
168
169     block_or_character:
170       if (argc - optind != 4)
171         {
172           error (0, 0, _("\
173 when creating special files, major and minor device\n\
174 numbers must be specified"));
175           usage (EXIT_FAILURE);
176         }
177
178       {
179         char const *s_major = argv[optind + 2];
180         char const *s_minor = argv[optind + 3];
181         uintmax_t i_major, i_minor;
182         dev_t device;
183
184         if (xstrtoumax (s_major, NULL, 0, &i_major, NULL) != LONGINT_OK
185             || i_major != (major_t) i_major)
186           error (EXIT_FAILURE, 0,
187                  _("invalid major device number %s"), quote (s_major));
188
189         if (xstrtoumax (s_minor, NULL, 0, &i_minor, NULL) != LONGINT_OK
190             || i_minor != (minor_t) i_minor)
191           error (EXIT_FAILURE, 0,
192                  _("invalid minor device number %s"), quote (s_minor));
193
194         device = makedev (i_major, i_minor);
195 #ifdef NODEV
196         if (device == NODEV)
197           error (EXIT_FAILURE, 0, _("invalid device %s %s"), s_major, s_minor);
198 #endif
199
200         if (mknod (argv[optind], newmode | node_type, device) != 0)
201           error (EXIT_FAILURE, errno, "%s", quote (argv[optind]));
202       }
203       break;
204
205     case 'p':                   /* `pipe' */
206 #ifndef S_ISFIFO
207       error (4, 0, _("fifo files not supported"));
208 #else
209       if (argc - optind != 2)
210         {
211           error (0, 0, _("\
212 major and minor device numbers may not be specified for fifo files"));
213           usage (EXIT_FAILURE);
214         }
215       if (mkfifo (argv[optind], newmode))
216         error (EXIT_FAILURE, errno, "%s", quote (argv[optind]));
217 #endif
218       break;
219
220     default:
221       error (0, 0, _("invalid device type %s"), quote (argv[optind + 1]));
222       usage (EXIT_FAILURE);
223     }
224
225   /* Perform an explicit chmod to ensure the file mode permission bits
226      are set as specified.  This extra step is necessary in some cases
227      when the containing directory has a default ACL.  */
228
229   if (specified_mode)
230     {
231       if (chmod (argv[optind], newmode))
232         error (0, errno, _("cannot set permissions of %s"),
233                quote (argv[optind]));
234     }
235
236   exit (EXIT_SUCCESS);
237 }