1 | /* |
2 | * Copyright 2015 Red Hat, Inc. |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2.1 of the License, or (at your option) any later version. |
8 | * |
9 | * This library 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 GNU |
12 | * Lesser General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Lesser General Public |
15 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
16 | * |
17 | * Author: Matthias Clasen <mclasen@redhat.com> |
18 | */ |
19 | |
20 | #include "config.h" |
21 | |
22 | #include <gio/gio.h> |
23 | #include <gi18n.h> |
24 | |
25 | #include "gio-tool.h" |
26 | |
27 | static gchar **watch_dirs; |
28 | static gchar **watch_files; |
29 | static gchar **watch_direct; |
30 | static gchar **watch_silent; |
31 | static gchar **watch_default; |
32 | static gboolean no_moves; |
33 | static gboolean mounts; |
34 | |
35 | static const GOptionEntry entries[] = { |
36 | { "dir" , 'd', 0, G_OPTION_ARG_FILENAME_ARRAY, &watch_dirs, |
37 | N_("Monitor a directory (default: depends on type)" ), N_("LOCATION" ) }, |
38 | { "file" , 'f', 0, G_OPTION_ARG_FILENAME_ARRAY, &watch_files, |
39 | N_("Monitor a file (default: depends on type)" ), N_("LOCATION" ) }, |
40 | { "direct" , 'D', 0, G_OPTION_ARG_FILENAME_ARRAY, &watch_direct, |
41 | N_("Monitor a file directly (notices changes made via hardlinks)" ), N_("LOCATION" ) }, |
42 | { "silent" , 's', 0, G_OPTION_ARG_FILENAME_ARRAY, &watch_silent, |
43 | N_("Monitors a file directly, but doesn’t report changes" ), N_("LOCATION" ) }, |
44 | { "no-moves" , 'n', 0, G_OPTION_ARG_NONE, &no_moves, |
45 | N_("Report moves and renames as simple deleted/created events" ), NULL }, |
46 | { "mounts" , 'm', 0, G_OPTION_ARG_NONE, &mounts, |
47 | N_("Watch for mount events" ), NULL }, |
48 | { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &watch_default, |
49 | NULL, NULL }, |
50 | { NULL } |
51 | }; |
52 | |
53 | static void |
54 | watch_callback (GFileMonitor *monitor, |
55 | GFile *child, |
56 | GFile *other, |
57 | GFileMonitorEvent event_type, |
58 | gpointer user_data) |
59 | { |
60 | gchar *child_str; |
61 | gchar *other_str; |
62 | |
63 | g_assert (child); |
64 | |
65 | if (g_file_is_native (file: child)) |
66 | child_str = g_file_get_path (file: child); |
67 | else |
68 | child_str = g_file_get_uri (file: child); |
69 | |
70 | if (other) |
71 | { |
72 | if (g_file_is_native (file: other)) |
73 | other_str = g_file_get_path (file: other); |
74 | else |
75 | other_str = g_file_get_uri (file: other); |
76 | } |
77 | else |
78 | other_str = g_strdup (str: "(none)" ); |
79 | |
80 | g_print (format: "%s: " , (gchar *) user_data); |
81 | switch (event_type) |
82 | { |
83 | case G_FILE_MONITOR_EVENT_CHANGED: |
84 | g_assert (!other); |
85 | g_print (format: "%s: changed" , child_str); |
86 | break; |
87 | case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: |
88 | g_assert (!other); |
89 | g_print (format: "%s: changes done" , child_str); |
90 | break; |
91 | case G_FILE_MONITOR_EVENT_DELETED: |
92 | g_assert (!other); |
93 | g_print (format: "%s: deleted" , child_str); |
94 | break; |
95 | case G_FILE_MONITOR_EVENT_CREATED: |
96 | g_assert (!other); |
97 | g_print (format: "%s: created" , child_str); |
98 | break; |
99 | case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED: |
100 | g_assert (!other); |
101 | g_print (format: "%s: attributes changed" , child_str); |
102 | break; |
103 | case G_FILE_MONITOR_EVENT_PRE_UNMOUNT: |
104 | g_assert (!other); |
105 | g_print (format: "%s: pre-unmount" , child_str); |
106 | break; |
107 | case G_FILE_MONITOR_EVENT_UNMOUNTED: |
108 | g_assert (!other); |
109 | g_print (format: "%s: unmounted" , child_str); |
110 | break; |
111 | case G_FILE_MONITOR_EVENT_MOVED_IN: |
112 | g_print (format: "%s: moved in" , child_str); |
113 | if (other) |
114 | g_print (format: " (from %s)" , other_str); |
115 | break; |
116 | case G_FILE_MONITOR_EVENT_MOVED_OUT: |
117 | g_print (format: "%s: moved out" , child_str); |
118 | if (other) |
119 | g_print (format: " (to %s)" , other_str); |
120 | break; |
121 | case G_FILE_MONITOR_EVENT_RENAMED: |
122 | g_assert (other); |
123 | g_print (format: "%s: renamed to %s\n" , child_str, other_str); |
124 | break; |
125 | |
126 | case G_FILE_MONITOR_EVENT_MOVED: |
127 | default: |
128 | g_assert_not_reached (); |
129 | } |
130 | |
131 | g_free (mem: child_str); |
132 | g_free (mem: other_str); |
133 | g_print (format: "\n" ); |
134 | } |
135 | |
136 | typedef enum |
137 | { |
138 | WATCH_DIR, |
139 | WATCH_FILE, |
140 | WATCH_AUTO |
141 | } WatchType; |
142 | |
143 | static gboolean |
144 | add_watch (const gchar *cmdline, |
145 | WatchType watch_type, |
146 | GFileMonitorFlags flags, |
147 | gboolean connect_handler) |
148 | { |
149 | GFileMonitor *monitor = NULL; |
150 | GError *error = NULL; |
151 | GFile *file; |
152 | |
153 | file = g_file_new_for_commandline_arg (arg: cmdline); |
154 | |
155 | if (watch_type == WATCH_AUTO) |
156 | { |
157 | GFileInfo *info; |
158 | guint32 type; |
159 | |
160 | info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_TYPE, flags: G_FILE_QUERY_INFO_NONE, NULL, error: &error); |
161 | if (!info) |
162 | goto err; |
163 | |
164 | type = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_STANDARD_TYPE); |
165 | watch_type = (type == G_FILE_TYPE_DIRECTORY) ? WATCH_DIR : WATCH_FILE; |
166 | } |
167 | |
168 | if (watch_type == WATCH_DIR) |
169 | monitor = g_file_monitor_directory (file, flags, NULL, error: &error); |
170 | else |
171 | monitor = g_file_monitor (file, flags, NULL, error: &error); |
172 | |
173 | if (!monitor) |
174 | goto err; |
175 | |
176 | if (connect_handler) |
177 | g_signal_connect (monitor, "changed" , G_CALLBACK (watch_callback), g_strdup (cmdline)); |
178 | |
179 | monitor = NULL; /* leak */ |
180 | g_object_unref (object: file); |
181 | |
182 | return TRUE; |
183 | |
184 | err: |
185 | print_file_error (file, message: error->message); |
186 | g_error_free (error); |
187 | g_object_unref (object: file); |
188 | |
189 | return FALSE; |
190 | } |
191 | |
192 | int |
193 | handle_monitor (int argc, gchar *argv[], gboolean do_help) |
194 | { |
195 | GOptionContext *context; |
196 | gchar *param; |
197 | GError *error = NULL; |
198 | GFileMonitorFlags flags; |
199 | guint i; |
200 | |
201 | g_set_prgname (prgname: "gio monitor" ); |
202 | |
203 | /* Translators: commandline placeholder */ |
204 | param = g_strdup_printf (format: "%s…" , _("LOCATION" )); |
205 | context = g_option_context_new (parameter_string: param); |
206 | g_free (mem: param); |
207 | g_option_context_set_help_enabled (context, FALSE); |
208 | g_option_context_set_summary (context, |
209 | _("Monitor files or directories for changes." )); |
210 | g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); |
211 | |
212 | if (do_help) |
213 | { |
214 | show_help (context, NULL); |
215 | g_option_context_free (context); |
216 | return 0; |
217 | } |
218 | |
219 | if (!g_option_context_parse (context, argc: &argc, argv: &argv, error: &error)) |
220 | { |
221 | show_help (context, message: error->message); |
222 | g_error_free (error); |
223 | g_option_context_free (context); |
224 | return 1; |
225 | } |
226 | |
227 | if (!watch_dirs && !watch_files && !watch_direct && !watch_silent && !watch_default) |
228 | { |
229 | show_help (context, _("No locations given" )); |
230 | g_option_context_free (context); |
231 | return 1; |
232 | } |
233 | |
234 | g_option_context_free (context); |
235 | |
236 | flags = (no_moves ? 0 : G_FILE_MONITOR_WATCH_MOVES) | |
237 | (mounts ? G_FILE_MONITOR_WATCH_MOUNTS : 0); |
238 | |
239 | if (watch_dirs) |
240 | { |
241 | for (i = 0; watch_dirs[i]; i++) |
242 | if (!add_watch (cmdline: watch_dirs[i], watch_type: WATCH_DIR, flags, TRUE)) |
243 | return 1; |
244 | } |
245 | |
246 | if (watch_files) |
247 | { |
248 | for (i = 0; watch_files[i]; i++) |
249 | if (!add_watch (cmdline: watch_files[i], watch_type: WATCH_FILE, flags, TRUE)) |
250 | return 1; |
251 | } |
252 | |
253 | if (watch_direct) |
254 | { |
255 | for (i = 0; watch_direct[i]; i++) |
256 | if (!add_watch (cmdline: watch_direct[i], watch_type: WATCH_FILE, flags: flags | G_FILE_MONITOR_WATCH_HARD_LINKS, TRUE)) |
257 | return 1; |
258 | } |
259 | |
260 | if (watch_silent) |
261 | { |
262 | for (i = 0; watch_silent[i]; i++) |
263 | if (!add_watch (cmdline: watch_silent[i], watch_type: WATCH_FILE, flags: flags | G_FILE_MONITOR_WATCH_HARD_LINKS, FALSE)) |
264 | return 1; |
265 | } |
266 | |
267 | if (watch_default) |
268 | { |
269 | for (i = 0; watch_default[i]; i++) |
270 | if (!add_watch (cmdline: watch_default[i], watch_type: WATCH_AUTO, flags, TRUE)) |
271 | return 1; |
272 | } |
273 | |
274 | while (TRUE) |
275 | g_main_context_iteration (NULL, TRUE); |
276 | |
277 | return 0; |
278 | } |
279 | |