Update FSF postal mail address.
[platform/upstream/coreutils.git] / src / mknod.c
1 /* mknod -- make special files
2    Copyright (C) 90, 91, 1995-2005 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., 51 Franklin Street, Fifth Floor, 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 != EXIT_SUCCESS)
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   const char *specified_mode;
92   int optc;
93   int expected_operands;
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 'm':
111           specified_mode = optarg;
112           break;
113         case_GETOPT_HELP_CHAR;
114         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
115         default:
116           usage (EXIT_FAILURE);
117         }
118     }
119
120   newmode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
121   if (specified_mode)
122     {
123       struct mode_change *change = mode_compile (specified_mode);
124       if (!change)
125         error (EXIT_FAILURE, 0, _("invalid mode"));
126       newmode = mode_adjust (newmode, change, umask (0));
127       free (change);
128     }
129
130   /* If the number of arguments is 0 or 1,
131      or (if it's 2 or more and the second one starts with `p'), then there
132      must be exactly two operands.  Otherwise, there must be four.  */
133   expected_operands = (argc <= optind
134                        || (optind + 1 < argc && argv[optind + 1][0] == 'p')
135                        ? 2 : 4);
136
137   if (argc - optind < expected_operands)
138     {
139       if (argc <= optind)
140         error (0, 0, _("missing operand"));
141       else
142         error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
143       if (expected_operands == 4 && argc - optind == 2)
144         fprintf (stderr, "%s\n",
145                  _("Special files require major and minor device numbers."));
146       usage (EXIT_FAILURE);
147     }
148
149   if (expected_operands < argc - optind)
150     {
151       error (0, 0, _("extra operand %s"),
152              quote (argv[optind + expected_operands]));
153       if (expected_operands == 2 && argc - optind == 4)
154         fprintf (stderr, "%s\n",
155                  _("Fifos do not have major and minor device numbers."));
156       usage (EXIT_FAILURE);
157     }
158
159   /* Only check the first character, to allow mnemonic usage like
160      `mknod /dev/rst0 character 18 0'. */
161
162   switch (argv[optind + 1][0])
163     {
164     case 'b':                   /* `block' or `buffered' */
165 #ifndef S_IFBLK
166       error (EXIT_FAILURE, 0, _("block special files not supported"));
167 #else
168       node_type = S_IFBLK;
169 #endif
170       goto block_or_character;
171
172     case 'c':                   /* `character' */
173     case 'u':                   /* `unbuffered' */
174 #ifndef S_IFCHR
175       error (EXIT_FAILURE, 0, _("character special files not supported"));
176 #else
177       node_type = S_IFCHR;
178 #endif
179       goto block_or_character;
180
181     block_or_character:
182       {
183         char const *s_major = argv[optind + 2];
184         char const *s_minor = argv[optind + 3];
185         uintmax_t i_major, i_minor;
186         dev_t device;
187
188         if (xstrtoumax (s_major, NULL, 0, &i_major, NULL) != LONGINT_OK
189             || i_major != (major_t) i_major)
190           error (EXIT_FAILURE, 0,
191                  _("invalid major device number %s"), quote (s_major));
192
193         if (xstrtoumax (s_minor, NULL, 0, &i_minor, NULL) != LONGINT_OK
194             || i_minor != (minor_t) i_minor)
195           error (EXIT_FAILURE, 0,
196                  _("invalid minor device number %s"), quote (s_minor));
197
198         device = makedev (i_major, i_minor);
199 #ifdef NODEV
200         if (device == NODEV)
201           error (EXIT_FAILURE, 0, _("invalid device %s %s"), s_major, s_minor);
202 #endif
203
204         if (mknod (argv[optind], newmode | node_type, device) != 0)
205           error (EXIT_FAILURE, errno, "%s", quote (argv[optind]));
206       }
207       break;
208
209     case 'p':                   /* `pipe' */
210 #ifndef S_ISFIFO
211       error (EXIT_FAILURE, 0, _("fifo files not supported"));
212 #else
213       if (mkfifo (argv[optind], newmode))
214         error (EXIT_FAILURE, errno, "%s", quote (argv[optind]));
215 #endif
216       break;
217
218     default:
219       error (0, 0, _("invalid device type %s"), quote (argv[optind + 1]));
220       usage (EXIT_FAILURE);
221     }
222
223   /* Perform an explicit chmod to ensure the file mode permission bits
224      are set as specified.  This extra step is necessary in some cases
225      when the containing directory has a default ACL.  */
226
227   if (specified_mode)
228     {
229       if (chmod (argv[optind], newmode))
230         error (0, errno, _("cannot set permissions of %s"),
231                quote (argv[optind]));
232     }
233
234   exit (EXIT_SUCCESS);
235 }