device: renew dhcp leases on awake for software devices
[NetworkManager.git] / clients / nm-online.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /*
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2, or (at your option)
6  * any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16  *
17  * Copyright (C) 2006 - 2008 Novell, Inc.
18  * Copyright (C) 2008 - 2014 Red Hat, Inc.
19  *
20  */
21
22 /*
23  * nm-online.c - Are we online?
24  *
25  * Return values:
26  *
27  *      0       : already online or connection established within given timeout
28  *      1       : offline or not online within given timeout
29  *      2       : unspecified error
30  *
31  * Robert Love <rml@novell.com>
32  */
33
34 #include "nm-default.h"
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <getopt.h>
39 #include <locale.h>
40
41 #include "NetworkManager.h"
42
43 #define PROGRESS_STEPS 15
44 #define WAIT_STARTUP_TAG "wait-startup"
45
46 typedef struct
47 {
48         gint64 start_timestamp_ms;
49         gint64 end_timestamp_ms;
50         gint64 progress_step_duration;
51         gboolean quiet;
52 } Timeout;
53
54 static void
55 client_properties_changed (GObject *object,
56                            GParamSpec *pspec,
57                            gpointer loop)
58 {
59         NMClient *client = NM_CLIENT (object);
60         NMState state;
61         gboolean wait_startup = GPOINTER_TO_UINT (g_object_get_data (object, WAIT_STARTUP_TAG));
62
63         if (!nm_client_get_nm_running (client))
64                 return;
65
66         if (wait_startup) {
67                 if (!nm_client_get_startup (client))
68                         g_main_loop_quit (loop);
69         } else {
70                 state = nm_client_get_state (client);
71                 if (   state == NM_STATE_CONNECTED_LOCAL
72                     || state == NM_STATE_CONNECTED_SITE
73                     || state == NM_STATE_CONNECTED_GLOBAL)
74                         g_main_loop_quit (loop);
75         }
76 }
77
78 static gboolean
79 handle_timeout (gpointer data)
80 {
81         const Timeout *timeout = data;
82         const gint64 now = g_get_monotonic_time () / (G_USEC_PER_SEC / 1000);
83         gint64 remaining_ms = timeout->end_timestamp_ms - now;
84         const gint64 elapsed_ms = now - timeout->start_timestamp_ms;
85         int progress_next_step_i = 0;
86
87         if (!timeout->quiet) {
88                 int i;
89
90                 /* calculate the next step (not the current): floor()+1 */
91                 progress_next_step_i = (elapsed_ms / timeout->progress_step_duration) + 1;
92                 progress_next_step_i = MIN (progress_next_step_i, PROGRESS_STEPS);
93
94                 g_print ("\r%s", _("Connecting"));
95                 for (i = 0; i < PROGRESS_STEPS; i++)
96                         putchar (i < progress_next_step_i ? '.' : ' ');
97                 g_print (" %4lds", (long) (MAX (0, remaining_ms) / 1000));
98                 fflush (stdout);
99         }
100
101         if (remaining_ms <= 3) {
102                 if (!timeout->quiet)
103                         g_print ("\n");
104                 exit (1);
105         }
106
107         if (!timeout->quiet) {
108                 gint64 rem;
109
110                 /* synchronize the timeout with the ticking of the seconds. */
111                 rem = remaining_ms % 1000;
112                 if (rem <= 3)
113                         rem = rem + G_USEC_PER_SEC;
114                 rem = rem + 10; /* add small offset to awake a bit after the second ticks */
115                 if (remaining_ms > rem)
116                         remaining_ms = rem;
117
118                 /* synchronize the timeout with the steps of the progress bar. */
119                 rem = (progress_next_step_i * timeout->progress_step_duration) - elapsed_ms;
120                 if (rem <= 3)
121                         rem = rem + timeout->progress_step_duration;
122                 rem = rem + 10; /* add small offset to awake a bit after the time out */
123                 if (remaining_ms > rem)
124                         remaining_ms = rem;
125         }
126
127         g_timeout_add (remaining_ms, handle_timeout, (void *) timeout);
128         return G_SOURCE_REMOVE;
129 }
130
131 int
132 main (int argc, char *argv[])
133 {
134         int t_secs = 30;
135         gboolean exit_no_nm = FALSE;
136         gboolean wait_startup = FALSE;
137         Timeout timeout;
138         GOptionContext *opt_ctx = NULL;
139         gboolean success;
140         NMClient *client;
141         NMState state = NM_STATE_UNKNOWN;
142         GMainLoop *loop;
143         gint64 remaining_ms;
144         GError *error = NULL;
145
146         GOptionEntry options[] = {
147                 {"timeout", 't', 0, G_OPTION_ARG_INT, &t_secs, N_("Time to wait for a connection, in seconds (without the option, default value is 30)"), "<timeout>"},
148                 {"exit", 'x', 0, G_OPTION_ARG_NONE, &exit_no_nm, N_("Exit immediately if NetworkManager is not running or connecting"), NULL},
149                 {"quiet", 'q', 0, G_OPTION_ARG_NONE, &timeout.quiet, N_("Don't print anything"), NULL},
150                 {"wait-for-startup", 's', 0, G_OPTION_ARG_NONE, &wait_startup, N_("Wait for NetworkManager startup instead of a connection"), NULL},
151                 {NULL}
152         };
153
154         timeout.start_timestamp_ms = g_get_monotonic_time () / (G_USEC_PER_SEC / 1000);
155         timeout.quiet = FALSE;
156
157         /* Set locale to be able to use environment variables */
158         setlocale (LC_ALL, "");
159
160         bindtextdomain (GETTEXT_PACKAGE, NMLOCALEDIR);
161         bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
162         textdomain (GETTEXT_PACKAGE);
163
164         opt_ctx = g_option_context_new (NULL);
165         g_option_context_set_translation_domain (opt_ctx, GETTEXT_PACKAGE);
166         g_option_context_set_ignore_unknown_options (opt_ctx, FALSE);
167         g_option_context_set_help_enabled (opt_ctx, TRUE);
168         g_option_context_add_main_entries (opt_ctx, options, NULL);
169
170         g_option_context_set_summary (opt_ctx,
171                                       _("Waits for NetworkManager to finish activating startup network connections."));
172
173         success = g_option_context_parse (opt_ctx, &argc, &argv, NULL);
174         g_option_context_free (opt_ctx);
175
176         if (!success) {
177                 g_printerr ("%s: %s\n", argv[0],
178                             _("Invalid option.  Please use --help to see a list of valid options."));
179                 return 2;
180         }
181
182         if (t_secs < 0 || t_secs > 3600)  {
183                 g_printerr ("%s: %s\n", argv[0],
184                             _("Invalid option.  Please use --help to see a list of valid options."));
185                 return 2;
186         }
187         remaining_ms = t_secs * 1000;
188
189         nm_g_type_init ();
190
191         client = nm_client_new (NULL, &error);
192         if (!client) {
193                 g_printerr (_("Error: Could not create NMClient object: %s."), error->message);
194                 g_error_free (error);
195                 return 2;
196         }
197
198         loop = g_main_loop_new (NULL, FALSE);
199
200         g_object_set_data (G_OBJECT (client), WAIT_STARTUP_TAG, GUINT_TO_POINTER (wait_startup));
201         state = nm_client_get_state (client);
202         if (!nm_client_get_nm_running (client)) {
203                 if (exit_no_nm) {
204                         g_object_unref (client);
205                         return 1;
206                 }
207         } else if (wait_startup) {
208                 if (!nm_client_get_startup (client)) {
209                         g_object_unref (client);
210                         return 0;
211                 }
212         } else {
213                 if (   state == NM_STATE_CONNECTED_LOCAL
214                     || state == NM_STATE_CONNECTED_SITE
215                     || state == NM_STATE_CONNECTED_GLOBAL) {
216                         g_object_unref (client);
217                         return 0;
218                 }
219         }
220         if (exit_no_nm && (state != NM_STATE_CONNECTING)) {
221                 g_object_unref (client);
222                 return 1;
223         }
224
225         if (remaining_ms == 0) {
226                 g_object_unref (client);
227                 return 1;
228         }
229
230         g_signal_connect (client, "notify",
231                           G_CALLBACK (client_properties_changed), loop);
232
233         timeout.end_timestamp_ms = timeout.start_timestamp_ms + remaining_ms;
234         timeout.progress_step_duration = (timeout.end_timestamp_ms - timeout.start_timestamp_ms + PROGRESS_STEPS/2) / PROGRESS_STEPS;
235
236         g_timeout_add (timeout.quiet ? remaining_ms : 0,
237                        handle_timeout, &timeout);
238
239         g_main_loop_run (loop);
240         g_main_loop_unref (loop);
241
242         g_object_unref (client);
243
244         return 0;
245 }