Subversion Repositories configs

Rev

Rev 192 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
192 - 1
#!/bin/sh -x
2
##########################################################
204 - 3
# Copyright (C) 2001-2018, 2021 VMware, Inc. All rights reserved.
192 - 4
#
5
# This program is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU Lesser General Public License as published
7
# by the Free Software Foundation version 2.1 and no later version.
8
#
9
# This program is distributed in the hope that it will be useful, but
10
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11
# or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
12
# License for more details.
13
#
14
# You should have received a copy of the GNU Lesser General Public License
15
# along with this program; if not, write to the Free Software Foundation, Inc.,
16
# 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
17
#
18
##########################################################
19
 
20
 
21
#
22
# network (Linux)
23
#
24
# Using a combination of a system networking script, ifconfig, ifup, ifdown
25
# and the ip command, attempt to release and renew DHCP leases upon receipt
26
# of suspend and resume events, respectively.
27
#
28
 
204 - 29
SOURCE=$0
30
logdir=/var/log
31
logbase=$logdir/vmware-network
192 - 32
logfile=$logbase.log
33
 
204 - 34
# Defines logging mode enabled (1) or disabled (0)
35
logmode=1
36
 
37
# Defines whether to rotate logs (1) or not (0)
38
logrotate=1
39
 
192 - 40
#
204 - 41
# Get log file path
42
#
43
get_logfile() {
44
   file=`vmware-toolbox-cmd config get logging network.data | \
45
        sed -e 's/.*= *//' -e 's/ *$//'`
46
   if [ -n "${file##*"UNSET"*}" ]; then
47
      logfile=$file
48
      logdir=`dirname $logfile`
49
      logbase=`echo $logfile | sed 's/\..*$//'`
50
   fi
51
}
52
 
53
#
54
# Get Network logging config
55
#
56
get_logconfig() {
57
   handler=`vmware-toolbox-cmd config get logging network.handler | \
58
           sed -e 's/.*= *//' -e 's/ *$//'`
59
   case $handler in
60
      "file")
61
         get_logfile
62
         ;;
63
      "file+")
64
         get_logfile
65
         logrotate=0
66
         ;;
67
      "vmx"|"std")
68
         logrotate=0
69
         ;;
70
      "syslog")
71
         logfile=/var/log/syslog
72
         logdir=`dirname $logfile`
73
         logrotate=0
74
         ;;
75
      *)
76
         ;;
77
   esac
78
}
79
 
80
#
192 - 81
# Rotate any logs
82
#
83
rotate_logfile() {
204 - 84
   if [ $logrotate -eq 1 ]; then
85
      max=`vmware-toolbox-cmd config get logging network.maxOldLogFiles | \
86
          sed -e 's/.*= *//' -e 's/ *$//'`
87
      if [ -z "${max##*"UNSET"*}" -o `expr "$max" : '[0-9]\+$'` -eq 0 ]; then
88
         max=9
89
      fi
90
      max=`expr $max - 1`
91
      for s in `seq $max -1 1`; do
92
         d=`expr $s + 1`
93
         mv -f $logbase.$s.log $logbase.$d.log
94
      done
95
      mv -f $logbase.log $logbase.1.log
96
   fi
192 - 97
}
98
 
204 - 99
#
100
# Logging api
101
#
102
log() {
103
   if [ $logmode -eq 1 ]; then
104
      if [ "$handler" = "vmx" ]; then
105
         `vmtoolsd --cmd "log $*"`
106
      elif [ "$handler" = "std" ]; then
107
         echo `date` ": $*"
108
      elif [ -w $logdir ]; then
109
         space=`df -k $logdir | awk 'NR == 2 { print $4 }'`
110
         if [ $space -gt 1024 ]; then
111
            echo `date` ": $*" >> $logfile
112
         else
113
            `vmtoolsd --cmd "log WARNING: [$SOURCE] Logging disabled. No space left in $logdir"`
114
            logmode=0
115
         fi
116
      else
117
         `vmtoolsd --cmd "log WARNING: [$SOURCE] Logging disabled. $logdir is not writable"`
118
         logmode=0
119
      fi
120
   fi
121
}
122
 
123
get_logconfig
192 - 124
rotate_logfile
125
 
204 - 126
log "Executing '$0 $*'"
192 - 127
 
128
. `dirname "$0"`/../../statechange.subr
129
 
130
 
131
#
132
# find_networking_script --
133
#
134
#    Searches common Linux distro init/rc paths to find a singular network
135
#    services script.
136
#
137
# Result:
138
#    Returns a valid networking script path on success or "error" on failure.
139
#
140
# Side effects:
141
#    None.
142
#
143
 
144
find_networking_script() {
145
   local script="error"
146
   for dir in "/etc/init.d" "/sbin/init.d" "/etc" "/etc/rc.d" ; do
147
      if [ -d "$dir/rc0.d" ] &&
148
         [ -d "$dir/rc1.d" ] &&
149
         [ -d "$dir/rc2.d" ] &&
150
         [ -d "$dir/rc3.d" ] &&
151
         [ -d "$dir/rc4.d" ] &&
152
         [ -d "$dir/rc5.d" ] &&
153
         [ -d "$dir/rc6.d" ]; then
154
 
155
         # Now find the appropriate networking script.
156
         if [ -d "$dir/init.d" ]; then
157
            if [ -x "$dir/init.d/network" ]; then
158
               script="$dir/init.d/network"
159
            elif [ -x "$dir/init.d/networking" ]; then
160
               script="$dir/init.d/networking"
161
            fi
162
         else
163
            if [ -x "$dir/network" ]; then
164
               script="$dir/network"
165
            elif [ -x "$dir/networking" ]; then
166
               script="$dir/networking"
167
            fi
168
         fi
169
      fi
170
   done
171
 
204 - 172
   log "$script"
192 - 173
}
174
 
175
 
176
#
177
# exec_networking_script --
178
#
179
#    Execute the networking script to bring network interfaces up or down
180
#    based on the given input action argument.
181
#
182
 
183
exec_networking_script()
184
{
185
   local script=$1
186
   local action=$2
187
 
188
   # Using SysV "service" if it exists, otherwise fall back to run the
189
   # script directly
190
   service=`which service 2>/dev/null`
191
   if [ $? = 0 -a -n "$service" ]; then
192
      serviceName=`basename "$script"`
193
      "$service" "$serviceName" "$action"
194
   else
195
      "$script" "$action"
196
   fi
197
 
198
   return $?
199
}
200
 
201
 
202
#
203
# exec_systemctl_service --
204
#
205
#    Handle linux distributions that use systemd to replace the legacy
206
#    system V startup scripts. The previous network script searching
207
#    approach is no longer viable in these systems. Invoke the systemctl
208
#    command to control the network service instead.
209
#
210
 
211
exec_systemctl_service()
212
{
213
   local rc=1
214
   local action=$1
215
   local ctlcmd=$(which systemctl 2>/dev/null)
216
   local service
217
 
218
   [ -z "$ctlcmd" ] && return $rc
219
 
220
   for svc in systemd-networkd network; do
221
      if ! $ctlcmd status $svc | grep -iq 'not-found'; then
222
         service=$svc && break
223
      fi
224
   done
225
 
226
   [ -z "$service" ] && return $rc
227
 
228
   $ctlcmd $action $service; rc=$?
229
 
230
   # When use the systemd-networkd service to shut down interfaces, interface
231
   # address and state remain unchanged. Need to use ip command to change its
232
   # address and state.
233
   if [ $rc = 0 -a $service = 'systemd-networkd' -a $action = 'stop' ]; then
234
      config_network_intfs $action; rc=$?
235
   fi
236
 
237
   return $rc
238
}
239
 
240
 
241
#
242
# del_intf_ip --
243
#
244
#    Use the ip command to remove all the addresses of an interface.
245
#
246
 
247
del_intf_ip()
248
{
249
   local nic=$1
250
 
251
   $ip_cmd addr flush dev $nic
252
   return $?
253
}
254
 
255
 
256
#
257
# ip_intf_ops --
258
#
259
#    Use the ip command to change the state of an interface to up or down.
260
#
261
 
262
ip_intf_ops()
263
{
264
   local rc=1
265
   local nic=$1
266
   local ops=$2
267
 
268
   [ -z "$ip_cmd" ] && return $rc
269
 
270
   $ip_cmd link set $nic $ops; rc=$?
271
 
272
   # Remove interface addresses when taking an interface down.
273
   if [ $rc = 0 -a $ops = down ]; then
274
      del_intf_ip $nic; rc=$?
275
   fi
276
 
277
   return $rc
278
}
279
 
280
 
281
#
282
# intf_ops --
283
#
284
#    Execute the specified command (ifup or ifdown) if available, otherwise use
285
#    the ip command as fallback. If ifup or ifdown fails, run the ip command to
286
#    retry the intended operation.
287
#
288
 
289
intf_ops()
290
{
291
   local rc=0
292
   local cmd=$1
293
   local ops=$2
294
   local nic=$3
295
   local tmp
296
 
297
   if [ ! -z "$cmd" ]; then
298
      tmp=$($cmd $nic 2>&1); rc=$?
299
 
300
      # Some systems still return a successful status even the command fails
301
      # because the interface is not configured in the configuration file. So
302
      # have to examine the command output to determine the actual status.
303
      if [ $rc = 0 ]; then
304
         echo $tmp | egrep -iq 'not configured|ignoring unknown' && rc=1
305
      fi
306
   fi
307
 
308
   # If ifup/ifdown fails, try the ip fallback.
309
   if [ -z "$cmd" -o $rc != 0 ]; then
310
      ip_intf_ops $nic $ops; rc=$?
311
   fi
312
 
313
   return $rc
314
}
315
 
316
 
317
#
318
# exec_intf_ops --
319
#
320
#    Perform an operation to bring an individual interface up or down.
321
#
322
 
323
exec_intf_ops()
324
{
325
   local rc=0
326
   local action=$1
327
   local nic=$2
328
 
329
   case $action in
330
      start)
331
         intf_ops "$ifup_cmd" up $nic; rc=$?
332
         ;;
333
      stop)
334
         intf_ops "$ifdown_cmd" down $nic; rc=$?
335
         ;;
336
      *)
337
         Panic "Illegal interface action: $action"
338
         ;;
339
   esac
340
 
341
   return $rc
342
}
343
 
344
 
345
#
346
# config_network_intfs --
347
#
348
#    For Linux systems not supporting networking scripts to bring interfaces
349
#    up or down, provide a way to change the interface state individually.
350
#
351
 
352
config_network_intfs()
353
{
354
   local rc=0
355
   local action=$1
356
 
357
   if [ -f "$activeList" ]; then
358
 
359
      while read nic; do
360
         exec_intf_ops $action $nic
361
         rc=$(expr $rc \| $?)
362
      done < $activeList
363
   fi
364
 
365
   return $rc
366
}
367
 
368
 
369
#
370
# run_network_script --
371
#
372
#    Finds out how to run the system's script used to control networking, and
373
#    runs it with the given argument (which should be one of the usual SysV
374
#    init script arguments). If it does not work, tries the other alternatives.
375
#    So far, our alternatives are (a) systemctl (b) network script (c) perform
376
#    an individual interface state change.
377
#
378
 
379
run_network_script()
380
{
381
   local action=$1
382
   local rc=0
383
   local script
384
 
385
   while true; do
386
 
387
      exec_systemctl_service $action
388
      [ $? != 0 ] || break
389
 
390
      script=`find_networking_script`
391
 
392
      if [ $script != "error" ]; then
393
         exec_networking_script $script $action
394
         [ $? != 0 ] || break
395
      fi
396
 
397
      # Since all the other alternatives fail, need to manually change
398
      # individual interface state.
399
      config_network_intfs $action; rc=$?
400
      break
401
   done
402
 
403
   return $rc
404
}
405
 
406
 
407
#
408
# save_active_NIC_list --
409
#
410
#    Records a list of every active NIC to /var/run/vmware-active-nics.
411
#
412
#    XXX What's the story on aliases?  Should they still be included, or will
413
#    they be recreated automatically upon resume?
414
#
415
# Results:
416
#    $activeList has, one per line, a list of all active NICs.
417
#
418
# Side effects:
419
#    None.
420
#
421
 
422
save_active_NIC_list()
423
{
424
   local intf_out
425
 
426
   >$activeList
427
 
428
   # Find out all the non-loopback up interfaces. Use ip if available
429
   # otherwise fall back to the ifconfig command.
430
   # ifconfig is buggy on some platforms and truncates long
431
   # network names
432
   if [ -n "$ip_cmd" ]; then
433
      for nic in $($ip_cmd link show up | egrep '\bUP\b' | awk -F: '{print $2}'); do
434
         $ip_cmd link show ${nic%@*} | grep -iq 'link/ether' && echo ${nic%@*} >> $activeList
435
      done
436
   else
437
      for nic in $($ifconfig_cmd | sed -n 's/^\([^: \t]*\).*$/\1/p'); do
438
         intf_out=$($ifconfig_cmd $nic)
439
         echo $intf_out | grep -iq loopback && continue
440
         echo $intf_out | egrep -q '\bUP\b' && echo $nic >> $activeList
441
      done
442
   fi
443
}
444
 
445
 
446
#
447
# rescue_NIC --
448
#
449
#    For each NIC recorded in $activeList that is not currently "up", run
450
#    "ifup $nic" or "ip link set $nic up" to bring the interface up.
451
#
452
# Results:
453
#    All downed NICs should be active.
454
#
455
 
456
rescue_NIC()
457
{
458
   local rc=0
459
   local intf_out
460
 
461
   if [ -f "$activeList" ]; then
462
      while read nic; do
463
         if [ -n "$ip_cmd" ]; then
464
            intf_out=$($ip_cmd link show $nic up)
465
         else
466
            intf_out=$($ifconfig_cmd $nic)
467
         fi
468
 
469
         if echo $intf_out | grep -q 'UP'; then
204 - 470
            log "[rescue_nic] $nic is already active."
192 - 471
         else
204 - 472
            log "[rescue_nic] activating $nic ..."
192 - 473
 
474
            # Our best effort to activate interfaces, use ifup if available
475
            # otherwise use the ip command as fallback.
476
            intf_ops "$ifup_cmd" up $nic
477
            rc=$(expr $rc \| $?)
478
         fi
479
      done < $activeList
480
 
481
      rm -f $activeList
482
   fi
483
 
484
   return $rc
485
}
486
 
487
 
488
#
489
# TranquilizeNetworkManager --
490
#
491
#    Put the NetworkManager daemon to sleep (maybe).
492
#
493
#    See http://projects.gnome.org/NetworkManager/developers/spec.html .
494
#
495
# Results:
496
#    Sleep(true) request is sent to the NetworkManager D-Bus interface.
497
#
498
# Side effects:
499
#    None.
500
#
501
 
502
TranquilizeNetworkManager()
503
{
504
   # `which' may be a bit noisy, so we'll shush it.
505
   dbusSend=`which dbus-send 2>/dev/null`
506
   rc=$?
507
   if [ $rc -ne 0 ]; then
508
      return $rc
509
   fi
510
 
511
   # Check NetworkManager state before disabling it.
512
   nm_state=`$dbusSend --system --print-reply		\
513
             --dest=org.freedesktop.NetworkManager	\
514
             /org/freedesktop/NetworkManager		\
515
             org.freedesktop.DBus.Properties.Get	\
516
             string:'org.freedesktop.NetworkManager'	\
517
             string:'State'				\
518
             | awk '/variant/ {print $3;}'`
519
   if [ -z "$nm_state" ]; then
520
      return 1
521
   fi
522
   # NetworkManager API     0.7/0.8   0.9
523
   # NM_STATE_ASLEEP           1      10
524
   # NM_STATE_DISCONNECTED     4      20
525
   case $nm_state in
526
      1|4|10|20)
527
         # Nothing needs to be done.
528
         return 0
529
         ;;
530
   esac
531
 
532
   # NetworkManager 0.8.0 and above
533
   $dbusSend --system --print-reply          \
534
      --dest=org.freedesktop.NetworkManager  \
535
      /org/freedesktop/NetworkManager        \
536
      org.freedesktop.NetworkManager.Enable boolean:false
537
   rc=$?
538
   if [ $rc -eq 0 ]; then
539
      return $rc
540
   fi
541
   # NetworkManager 0.7.0
542
   $dbusSend --system --print-reply          \
543
      --dest=org.freedesktop.NetworkManager  \
544
      /org/freedesktop/NetworkManager        \
545
      org.freedesktop.NetworkManager.Sleep boolean:true
546
   rc=$?
547
   if [ $rc -eq 0 ]; then
548
      return $rc
549
   fi
550
   # NetworkManager 0.6
551
   $dbusSend --system --print-reply          \
552
      --dest=org.freedesktop.NetworkManager  \
553
      /org/freedesktop/NetworkManager        \
554
      org.freedesktop.NetworkManager.sleep
555
   rc=$?
556
 
557
   return $rc
558
}
559
 
560
 
561
#
562
# WakeNetworkManager --
563
#
564
#    Wake the NetworkManager daemon (maybe).
565
#
566
#    See http://projects.gnome.org/NetworkManager/developers/spec.html .
567
#
568
# Results:
569
#    Sleep(false)request is sent to the NetworkManager D-Bus interface.
570
#
571
# Side effects:
572
#    None.
573
#
574
 
575
WakeNetworkManager()
576
{
577
   # `which' may be a bit noisy, so we'll shush it.
578
   dbusSend=`which dbus-send 2>/dev/null`
579
   rc=$?
580
   if [ $rc = 0 ]; then
581
      # NetworkManager 0.8.0
582
      $dbusSend --system --print-reply          \
583
         --dest=org.freedesktop.NetworkManager  \
584
         /org/freedesktop/NetworkManager        \
585
         org.freedesktop.NetworkManager.Enable boolean:true
586
      rc=$?
587
      if [ $rc = 0 ]; then
588
         return $rc
589
      fi
590
      # NetworkManager 0.7.0
591
      $dbusSend --system --print-reply          \
592
         --dest=org.freedesktop.NetworkManager  \
593
         /org/freedesktop/NetworkManager        \
594
         org.freedesktop.NetworkManager.Sleep boolean:false
595
      rc=$?
596
      if [ $rc = 0 ]; then
597
         return $rc
598
      fi
599
      # NetworkManager 0.6
600
      $dbusSend --system --print-reply          \
601
         --dest=org.freedesktop.NetworkManager  \
602
         /org/freedesktop/NetworkManager        \
603
         org.freedesktop.NetworkManager.wake
604
      rc=$?
605
   fi
606
   return $rc
607
}
608
 
609
 
610
#
611
# sanity_check --
612
#
613
#    Check if the script has all the commands it needs to carry out the
614
#    request. So far, it requires either ip or ifconfig command to read
615
#    interface configuration. Ifup is not checked here. It is checked at
616
#    the place where we need to do individual interface state change.
617
#
618
 
619
sanity_check()
620
{
621
   ip_cmd=$(which ip 2>/dev/null)
622
   ifconfig_cmd=$(which ifconfig 2>/dev/null)
623
   ifup_cmd=$(which ifup 2>/dev/null)
624
   ifdown_cmd=$(which ifdown 2>/dev/null)
625
 
626
   [ -z "$ifconfig_cmd" -a -z "$ip_cmd" ] && \
627
       Panic "ip and ifconfig not in search path."
628
}
629
 
630
 
631
#
632
# main --
633
#
634
#    Main entry point.  Perform some sanity checking, then map state change
635
#    events to relevant networking operations.
636
#
637
# Results:
638
#    See comment at top of file.
639
#
640
 
641
main() {
642
   exitCode=0
643
   activeList=/var/run/vmware-active-nics
644
 
645
   case "$1" in
646
      poweron-vm)
647
         rm -f $activeList
648
         ;;
649
      suspend-vm)
650
         TranquilizeNetworkManager
651
         exitCode=$?
652
         if [ $exitCode != 0 ]; then
653
            sanity_check suspend-vm
654
            save_active_NIC_list
655
            run_network_script stop
656
            exitCode=$?
657
         fi
658
         ;;
659
      resume-vm)
660
         WakeNetworkManager
661
         exitCode=$?
662
         if [ $exitCode != 0 ]; then
663
            sanity_check resume-vm
664
            # According to hfu, "/etc/init.d/networking restart" on Debian 5.0
665
            # may bring down ethernet interfaces tagged as "allow-hotplug" without
666
            # bringing them back up.
667
            #
668
            # This is especially a problem when reverting to a live, running
669
            # VM snapshot where an active NIC list hadn't yet been generated,
670
            # resulting in sudden loss of an otherwise operational NIC.
671
            #
672
            # So, if the active list doesn't exist, assume we're coming back to
673
            # a live snapshot and capture the current active list now for
674
            # rescue later.
675
            if [ ! -s $activeList ]; then
676
               save_active_NIC_list
677
            fi
678
 
679
            # We shall use start not restart here. Otherwise we may not be able
680
            # to bring back active list on distros like sles11sp2
681
            # -- PR 816791
682
            run_network_script start
683
            rescue_NIC
684
            exitCode=$?
685
         fi
686
         ;;
687
      *)
204 - 688
         log "No argument supplied."
192 - 689
         ;;
690
   esac
691
 
692
   return $exitCode
693
}
694
 
695
main "$@"
204 - 696
log "Finished '$0 $*'"