declare program_name consistently
[platform/upstream/coreutils.git] / src / mknod.c
1 /* mknod -- make special files
2    Copyright (C) 90, 91, 1995-2008 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 3 of the License, or
7    (at your option) 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, see <http://www.gnu.org/licenses/>.  */
16
17 /* Written by David MacKenzie <djm@ai.mit.edu>  */
18
19 #include <config.h>
20 #include <stdio.h>
21 #include <getopt.h>
22 #include <sys/types.h>
23 #include <selinux/selinux.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 proper_name ("David MacKenzie")
35
36 /* The name this program was run with. */
37 char const *program_name;
38
39 static struct option const longopts[] =
40 {
41   {GETOPT_SELINUX_CONTEXT_OPTION_DECL},
42   {"mode", required_argument, NULL, 'm'},
43   {GETOPT_HELP_OPTION_DECL},
44   {GETOPT_VERSION_OPTION_DECL},
45   {NULL, 0, NULL, 0}
46 };
47
48 void
49 usage (int status)
50 {
51   if (status != EXIT_SUCCESS)
52     fprintf (stderr, _("Try `%s --help' for more information.\n"),
53              program_name);
54   else
55     {
56       printf (_("Usage: %s [OPTION]... NAME TYPE [MAJOR MINOR]\n"),
57               program_name);
58       fputs (_("\
59 Create the special file NAME of the given TYPE.\n\
60 \n\
61 "), stdout);
62       fputs(_("\
63   -Z, --context=CTX  set the SELinux security context of NAME to CTX\n\
64 "), stdout);
65       fputs (_("\
66 Mandatory arguments to long options are mandatory for short options too.\n\
67 "), stdout);
68       fputs (_("\
69   -m, --mode=MODE   set file permission bits to MODE, not a=rw - umask\n\
70 "), stdout);
71       fputs (HELP_OPTION_DESCRIPTION, stdout);
72       fputs (VERSION_OPTION_DESCRIPTION, stdout);
73       fputs (_("\
74 \n\
75 Both MAJOR and MINOR must be specified when TYPE is b, c, or u, and they\n\
76 must be omitted when TYPE is p.  If MAJOR or MINOR begins with 0x or 0X,\n\
77 it is interpreted as hexadecimal; otherwise, if it begins with 0, as octal;\n\
78 otherwise, as decimal.  TYPE may be:\n\
79 "), stdout);
80       fputs (_("\
81 \n\
82   b      create a block (buffered) special file\n\
83   c, u   create a character (unbuffered) special file\n\
84   p      create a FIFO\n\
85 "), stdout);
86       printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
87       emit_bug_reporting_address ();
88     }
89   exit (status);
90 }
91
92 int
93 main (int argc, char **argv)
94 {
95   mode_t newmode;
96   char const *specified_mode = NULL;
97   int optc;
98   int expected_operands;
99   mode_t node_type;
100   security_context_t scontext = NULL;
101
102   initialize_main (&argc, &argv);
103   program_name = argv[0];
104   setlocale (LC_ALL, "");
105   bindtextdomain (PACKAGE, LOCALEDIR);
106   textdomain (PACKAGE);
107
108   atexit (close_stdout);
109
110   while ((optc = getopt_long (argc, argv, "m:Z:", longopts, NULL)) != -1)
111     {
112       switch (optc)
113         {
114         case 'm':
115           specified_mode = optarg;
116           break;
117         case 'Z':
118           scontext = optarg;
119           break;
120         case_GETOPT_HELP_CHAR;
121         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
122         default:
123           usage (EXIT_FAILURE);
124         }
125     }
126
127   newmode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
128   if (specified_mode)
129     {
130       struct mode_change *change = mode_compile (specified_mode);
131       if (!change)
132         error (EXIT_FAILURE, 0, _("invalid mode"));
133       newmode = mode_adjust (newmode, false, umask (0), change, NULL);
134       free (change);
135       if (newmode & ~S_IRWXUGO)
136         error (EXIT_FAILURE, 0,
137                _("mode must specify only file permission bits"));
138     }
139
140   /* If the number of arguments is 0 or 1,
141      or (if it's 2 or more and the second one starts with `p'), then there
142      must be exactly two operands.  Otherwise, there must be four.  */
143   expected_operands = (argc <= optind
144                        || (optind + 1 < argc && argv[optind + 1][0] == 'p')
145                        ? 2 : 4);
146
147   if (argc - optind < expected_operands)
148     {
149       if (argc <= optind)
150         error (0, 0, _("missing operand"));
151       else
152         error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
153       if (expected_operands == 4 && argc - optind == 2)
154         fprintf (stderr, "%s\n",
155                  _("Special files require major and minor device numbers."));
156       usage (EXIT_FAILURE);
157     }
158
159   if (expected_operands < argc - optind)
160     {
161       error (0, 0, _("extra operand %s"),
162              quote (argv[optind + expected_operands]));
163       if (expected_operands == 2 && argc - optind == 4)
164         fprintf (stderr, "%s\n",
165                  _("Fifos do not have major and minor device numbers."));
166       usage (EXIT_FAILURE);
167     }
168
169   if (scontext && setfscreatecon (scontext) < 0)
170     error (EXIT_FAILURE, errno,
171            _("failed to set default file creation context to %s"),
172            quote (scontext));
173
174   /* Only check the first character, to allow mnemonic usage like
175      `mknod /dev/rst0 character 18 0'. */
176
177   switch (argv[optind + 1][0])
178     {
179     case 'b':                   /* `block' or `buffered' */
180 #ifndef S_IFBLK
181       error (EXIT_FAILURE, 0, _("block special files not supported"));
182 #else
183       node_type = S_IFBLK;
184 #endif
185       goto block_or_character;
186
187     case 'c':                   /* `character' */
188     case 'u':                   /* `unbuffered' */
189 #ifndef S_IFCHR
190       error (EXIT_FAILURE, 0, _("character special files not supported"));
191 #else
192       node_type = S_IFCHR;
193 #endif
194       goto block_or_character;
195
196     block_or_character:
197       {
198         char const *s_major = argv[optind + 2];
199         char const *s_minor = argv[optind + 3];
200         uintmax_t i_major, i_minor;
201         dev_t device;
202
203         if (xstrtoumax (s_major, NULL, 0, &i_major, NULL) != LONGINT_OK
204             || i_major != (major_t) i_major)
205           error (EXIT_FAILURE, 0,
206                  _("invalid major device number %s"), quote (s_major));
207
208         if (xstrtoumax (s_minor, NULL, 0, &i_minor, NULL) != LONGINT_OK
209             || i_minor != (minor_t) i_minor)
210           error (EXIT_FAILURE, 0,
211                  _("invalid minor device number %s"), quote (s_minor));
212
213         device = makedev (i_major, i_minor);
214 #ifdef NODEV
215         if (device == NODEV)
216           error (EXIT_FAILURE, 0, _("invalid device %s %s"), s_major, s_minor);
217 #endif
218
219         if (mknod (argv[optind], newmode | node_type, device) != 0)
220           error (EXIT_FAILURE, errno, "%s", quote (argv[optind]));
221       }
222       break;
223
224     case 'p':                   /* `pipe' */
225       if (mkfifo (argv[optind], newmode) != 0)
226         error (EXIT_FAILURE, errno, "%s", quote (argv[optind]));
227       break;
228
229     default:
230       error (0, 0, _("invalid device type %s"), quote (argv[optind + 1]));
231       usage (EXIT_FAILURE);
232     }
233
234   exit (EXIT_SUCCESS);
235 }