Merge "sysmgr: add a gflt300 profile"
diff --git a/taxonomy/dhcp.py b/taxonomy/dhcp.py
index cab824c..7a503bc 100644
--- a/taxonomy/dhcp.py
+++ b/taxonomy/dhcp.py
@@ -65,7 +65,7 @@
     '1,3,6,12,15,28,40,41,42': ['visiotv', 'kindle'],
 
     '1,3,6,15,28,33': ['wii'],
-    '1,3,6,15': ['wii'],
+    '1,3,6,15': ['wii', 'xbox'],
 
     '1,15,3,6,44,46,47,31,33,121,249,252,43': ['windows-phone'],
 }
diff --git a/taxonomy/ethernet.py b/taxonomy/ethernet.py
index b62f0a2..0635073 100644
--- a/taxonomy/ethernet.py
+++ b/taxonomy/ethernet.py
@@ -24,6 +24,7 @@
 # Galaxy S4.
 database = {
     '00:bb:3a': ['amazon'],
+    '0c:47:c9': ['amazon'],
     '10:ae:60': ['amazon'],
     '28:ef:01': ['amazon'],
     '74:75:48': ['amazon'],
@@ -35,6 +36,7 @@
 
     '30:85:a9': ['asus'],
     '5c:ff:35': ['asus'],
+    '60:a4:4c': ['asus'],
     '74:d0:2b': ['asus'],
     'ac:22:0b': ['asus'],
     'bc:ee:7b': ['asus'],
@@ -78,6 +80,7 @@
     '64:89:9a': ['lg'],
     '64:bc:0c': ['lg'],
     '78:f8:82': ['lg'],
+    '88:c9:d0': ['lg'],
     '8c:3a:e3': ['lg'],
     'a0:39:f7': ['lg'],
     'a0:91:69': ['lg'],
diff --git a/taxonomy/wifi.py b/taxonomy/wifi.py
index afbd709..3a12713 100644
--- a/taxonomy/wifi.py
+++ b/taxonomy/wifi.py
@@ -439,6 +439,8 @@
         ('BCM4339', 'Nexus 5', '5GHz'),
     'wifi4|probe:0,1,3,45,127,191,221(001018,2),221(00904c,51),htcap:016f,htagg:17,htmcs:000000ff,vhtcap:0f805932,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,extcap:0000000000000040|assoc:0,1,33,36,48,45,127,191,221(001018,2),221(0050f2,2),htcap:016f,htagg:17,htmcs:000000ff,vhtcap:0f805932,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,txpow:e003,extcap:0000000000000040|oui:lg':
         ('BCM4339', 'Nexus 5', '5GHz'),
+    'wifi4|probe:0,1,45,127,107,191,221(506f9a,16),221(001018,2),221(00904c,51),221(00904c,4),221(0050f2,8),htcap:016f,htagg:17,htmcs:000000ff,vhtcap:0f805932,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,extcap:0000088001400040|assoc:0,1,33,36,48,45,127,70,191,221(001018,2),221(00904c,4),221(0050f2,2),htcap:016f,htagg:17,htmcs:000000ff,vhtcap:0f805932,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,txpow:e003,extcap:0000008001400040|oui:lg':
+        ('BCM4339', 'Nexus 5', '5GHz'),
     'wifi4|probe:0,1,50,3,45,127,221(001018,2),221(00904c,51),htcap:112d,htagg:17,htmcs:000000ff,extcap:0000000000000040|assoc:0,1,33,36,48,50,45,221(001018,2),221(0050f2,2),htcap:112d,htagg:17,htmcs:000000ff,txpow:1303|oui:lg':
         ('BCM4339', 'Nexus 5', '2.4GHz'),
     'wifi4|probe:0,1,50,45,127,221(001018,2),221(00904c,51),htcap:112d,htagg:17,htmcs:000000ff,extcap:0000000000000040|assoc:0,1,33,36,48,50,45,221(001018,2),221(0050f2,2),htcap:112d,htagg:17,htmcs:000000ff,txpow:1303|oui:lg':
@@ -524,6 +526,8 @@
         ('QCA_WCN3660', 'Nexus 7 (2013)', '5GHz'),
     'wifi4|probe:0,1,45,221(0050f2,8),htcap:016e,htagg:03,htmcs:000000ff|assoc:0,1,33,36,48,45,221(0050f2,2),127,htcap:016e,htagg:03,htmcs:000000ff,txpow:1e0d,extcap:00000a02|oui:asus':
         ('QCA_WCN3660', 'Nexus 7 (2013)', '5GHz'),
+    'wifi4|probe:0,1,45,htcap:016e,htagg:03,htmcs:000000ff|assoc:0,1,33,36,48,45,221(0050f2,2),127,htcap:016e,htagg:03,htmcs:000000ff,txpow:1e0d,extcap:00000a02|oui:asus':
+        ('QCA_WCN3660', 'Nexus 7 (2013)', '5GHz'),
     'wifi4|probe:0,1,50,45,221(0050f2,8),127,221(0050f2,4),221(506f9a,9),htcap:012c,htagg:03,htmcs:000000ff,extcap:00000a02,wps:Nexus_7|assoc:0,1,50,48,45,221(0050f2,2),127,htcap:012c,htagg:03,htmcs:000000ff,extcap:00000a02':
         ('QCA_WCN3660', 'Nexus 7 (2013)', '2.4GHz'),
     'wifi4|probe:0,1,50,45,221(0050f2,8),221(0050f2,4),221(506f9a,9),htcap:012c,htagg:03,htmcs:000000ff,wps:Nexus_7|assoc:0,1,50,48,45,221(0050f2,2),127,htcap:012c,htagg:03,htmcs:000000ff,extcap:00000a02':
@@ -532,6 +536,8 @@
         ('QCA_WCN3660', 'Nexus 7 (2013)', '2.4GHz'),
     'wifi4|probe:0,1,50,3,45,221(0050f2,8),htcap:012c,htagg:03,htmcs:000000ff|assoc:0,1,50,48,45,221(0050f2,2),127,htcap:012c,htagg:03,htmcs:000000ff,extcap:00000a02|oui:asus':
         ('QCA_WCN3660', 'Nexus 7 (2013)', '2.4GHz'),
+    'wifi4|probe:0,1,50,45,htcap:016e,htagg:03,htmcs:000000ff|assoc:0,1,50,48,45,221(0050f2,2),127,htcap:012c,htagg:03,htmcs:000000ff,extcap:00000a02|oui:asus':
+        ('QCA_WCN3660', 'Nexus 7 (2013)', '2.4GHz'),
 
     'wifi4|probe:0,1,45,127,191,221(0050f2,4),221(506f9a,9),221(001018,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,extcap:000008800140,wps:Nexus_9|assoc:0,1,33,36,48,45,127,191,221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:e009,extcap:000008800140':
         ('BCM4354', 'Nexus 9', '5GHz'),
@@ -675,6 +681,8 @@
 
     'wifi4|probe:0,1,45,127,107,191,221(506f9a,16),221(00904c,4),221(0050f2,8),221(001018,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,extcap:0000088001400040|assoc:0,1,33,36,48,45,127,107,191,221(00904c,4),221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:e20b,extcap:0000088001400040|oui:samsung':
         ('BCM4354', 'Samsung Galaxy S5', '5GHz'),
+    'wifi4|probe:0,1,45,191,221(00904c,4),221(0050f2,8),221(001018,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa|assoc:0,1,33,36,48,45,191,221(00904c,4),221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:e20b|oui:samsung':
+        ('BCM4354', 'Samsung Galaxy S5', '5GHz'),
     'wifi4|probe:0,1,50,3,45,127,107,221(506f9a,16),221(00904c,4),221(0050f2,8),221(001018,2),htcap:002d,htagg:17,htmcs:0000ffff,extcap:000008800140|assoc:0,1,50,33,36,48,45,127,107,221(001018,2),221(0050f2,2),htcap:002d,htagg:17,htmcs:0000ffff,txpow:1209,extcap:000008800140|oui:samsung':
         ('BCM4354', 'Samsung Galaxy S5', '2.4GHz'),
     'wifi4|probe:0,1,50,3,45,127,107,221(506f9a,16),221(00904c,4),221(0050f2,8),221(001018,2),htcap:002d,htagg:17,htmcs:0000ffff,extcap:0000088001400040|assoc:0,1,50,33,36,48,45,127,107,221(001018,2),221(0050f2,2),htcap:002d,htagg:17,htmcs:0000ffff,txpow:1209,extcap:000008800140|oui:samsung':
@@ -684,6 +692,8 @@
         ('BCM4358', 'Samsung Galaxy S6', '5GHz'),
     'wifi4|probe:0,1,45,127,191,221(00904c,4),221(0050f2,8),221(001018,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,extcap:0000088001400040|assoc:0,1,33,36,45,127,191,221(00904c,4),221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:e002,extcap:0000088001400040|oui:samsung':
         ('BCM4358', 'Samsung Galaxy S6', '5GHz'),
+    'wifi4|probe:0,1,45,191,221(00904c,4),221(0050f2,8),221(001018,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa|assoc:0,1,33,36,48,45,191,221(00904c,4),221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:e002|oui:samsung':
+        ('BCM4358', 'Samsung Galaxy S6', '5GHz'),
     'wifi4|probe:0,1,50,3,45,127,221(00904c,4),221(0050f2,8),221(001018,2),htcap:002d,htagg:17,htmcs:0000ffff,extcap:0000088001400040|assoc:0,1,50,33,36,45,127,221(001018,2),221(0050f2,2),htcap:002d,htagg:17,htmcs:0000ffff,txpow:1402,extcap:0000088001400040|oui:samsung':
         ('BCM4358', 'Samsung Galaxy S6', '2.4GHz'),
     'wifi4|probe:0,1,50,3,45,127,221(00904c,4),221(0050f2,8),221(001018,2),htcap:002d,htagg:17,htmcs:0000ffff,extcap:0000088001400040|assoc:0,1,50,33,36,48,45,127,221(001018,2),221(0050f2,2),htcap:002d,htagg:17,htmcs:0000ffff,txpow:1402,extcap:0000088001400040|oui:samsung':
diff --git a/waveguide/fake/wifi b/waveguide/fake/wifi
new file mode 100755
index 0000000..9270bc7
--- /dev/null
+++ b/waveguide/fake/wifi
@@ -0,0 +1,28 @@
+#!/bin/sh
+echo "fake/wifi:" "$@" >&2
+cd "$(dirname "$0")"
+
+if [ "$1" = "scan" ]; then
+  # TODO(willangley): pass the dev in an env var for tests, since looking it up
+  #   from the platform cannot be expected to work.
+  iw_scan="./iw dev wlan-22:22 scan"
+  while [ -n "$2" ]; do
+    shift
+    case "$1" in
+      -b)
+        shift
+        ;;
+      --scan-freq)
+        shift
+        iw_scan="$iw_scan freq $1"
+        ;;
+      --scan-*)
+        iw_scan="$iw_scan ${1#--scan-}"
+        ;;
+    esac
+  done
+  exec $iw_scan
+else
+  echo "fake/wifi: first arg ('$1') must be 'scan'" >&2
+  exit 99
+fi
diff --git a/waveguide/waveguide.py b/waveguide/waveguide.py
index b419ced..cbc9fe8 100755
--- a/waveguide/waveguide.py
+++ b/waveguide/waveguide.py
@@ -93,6 +93,15 @@
 consensus_start = None
 
 
+def BandForFreq(freq):
+  if freq / 100 == 24:
+    return '2.4'
+  elif freq / 1000 == 5:
+    return '5'
+  else:
+    raise ValueError('Frequency %d is not in any known band', freq)
+
+
 def UpdateConsensus(new_uptime_ms, new_consensus_key):
   """Update the consensus key based on received multicast packets."""
   global consensus_key, consensus_start
@@ -324,9 +333,15 @@
               args=['iw', 'dev', self.vdevname, 'info'])
       # channel scan more than once in case we miss hearing a beacon
       for _ in range(opt.initial_scans):
+        if self.flags & wgdata.ApFlags.Can2G:
+          band = '2.4'
+        elif self.flags & wgdata.ApFlags.Can5G:
+          band = '5'
+
         RunProc(
             callback=self._ScanResults,
-            args=['iw', 'dev', self.vdevname, 'scan', 'ap-force', 'passive'])
+            args=['wifi', 'scan', '-b', band, '--scan-ap-force',
+                  '--scan-passive'])
         self.UpdateStationInfo()
       self.next_scan_time = now
       self.did_initial_scan = True
@@ -338,8 +353,9 @@
       self.Log('scanning %d MHz (%d/%d)', scan_freq, self.scan_idx + 1,
                len(self.allowed_freqs))
       RunProc(callback=self._ScanResults,
-              args=['iw', 'dev', self.vdevname, 'scan', 'freq', str(scan_freq),
-                    'ap-force', 'passive'])
+              args=['wifi', 'scan', '-b', BandForFreq(scan_freq),
+                    '--scan-freq', str(scan_freq), '--scan-ap-force',
+                    '--scan-passive'])
       chan_interval = opt.scan_interval / len(self.allowed_freqs)
       # Randomly fiddle with the timing to avoid permanent alignment with
       # other nodes also doing scans.  If we're perfectly aligned with
@@ -586,9 +602,10 @@
         disabled = (g.group(3) == 'disabled')
         self.Debug('phy freq=%d chan=%d disabled=%d', freq, chan, disabled)
         if not disabled:
-          if freq / 100 == 24:
+          band = BandForFreq(freq)
+          if band == '2.4':
             self.flags |= wgdata.ApFlags.Can2G
-          if freq / 1000 == 5:
+          elif band == '5':
             self.flags |= wgdata.ApFlags.Can5G
           self.allowed_freqs.add(freq)
           freq_to_chan[freq] = chan
@@ -766,7 +783,7 @@
         a = wgdata.Assoc(mac=mac, rssi=rssi, last_seen=last_seen, can5G=can5G)
         if mac not in self.assoc_list:
           self.Debug('Added: %r', a)
-          self.wifiblaster_controller.Measure(self.vdevname, mac)
+          self.wifiblaster_controller.MeasureOnAssociation(self.vdevname, mac)
         assoc_list[mac] = a
 
     for line in stdout.split('\n'):
@@ -887,16 +904,18 @@
 
   - Scheduling parameters
 
-    wifiblaster.enable      Enable WiFi performance measurement.
-    wifiblaster.interval    Average time between automated measurements in
-                            seconds, or 0 to disable automated measurements.
-    wifiblaster.measureall  Unix time at which to measure all clients.
+    wifiblaster.enable         Enable WiFi performance measurement.
+    wifiblaster.interval       Average time between automated measurements in
+                               seconds, or 0 to disable automated measurements.
+    wifiblaster.measureall     Unix time at which to measure all clients.
+    wifiblaster.onassociation  Enable WiFi performance measurement after clients
+                               associate.
 
   - Measurement parameters
 
-    wifiblaster.duration    Measurement duration in seconds.
-    wifiblaster.fraction    Number of samples per measurement.
-    wifiblaster.size        Packet size in bytes.
+    wifiblaster.duration       Measurement duration in seconds.
+    wifiblaster.fraction       Number of samples per measurement.
+    wifiblaster.size           Packet size in bytes.
   """
 
   def __init__(self, managers, basedir):
@@ -968,6 +987,12 @@
                     '-f', str(fraction), '-s', str(size),
                     helpers.DecodeMAC(client)])
 
+  def MeasureOnAssociation(self, interface, client):
+    """Measures the performance of a client after association."""
+    onassociation = self._ReadParameter('onassociation', self._StrToBool)
+    if onassociation:
+      self.Measure(interface, client)
+
   def Poll(self, now):
     """Polls the state machine."""
 
diff --git a/waveguide/wifiblaster_controller_test.py b/waveguide/wifiblaster_controller_test.py
index 04e71f8..6b562b1 100755
--- a/waveguide/wifiblaster_controller_test.py
+++ b/waveguide/wifiblaster_controller_test.py
@@ -208,6 +208,15 @@
       wvtest.WVPASSEQ(CountRuns(), 1)
       wc.Poll(501)
       wvtest.WVPASSEQ(CountRuns(), 0)
+
+      # Measure on association only if enabled.
+      wc.MeasureOnAssociation(manager.vdevname,
+                              manager.GetState().assoc[0].mac)
+      wvtest.WVPASSEQ(CountRuns(), 0)
+      WriteConfig('onassociation', 'True')
+      wc.MeasureOnAssociation(manager.vdevname,
+                              manager.GetState().assoc[0].mac)
+      wvtest.WVPASSEQ(CountRuns(), 1)
   finally:
     time.time = oldtime
     shutil.rmtree(d)
diff --git a/wifi/configs.py b/wifi/configs.py
index 7518872..11867b2 100644
--- a/wifi/configs.py
+++ b/wifi/configs.py
@@ -77,6 +77,7 @@
 {require_ht}
 {require_vht}
 {hidden}
+{ap_isolate}
 
 ht_capab={ht20}{ht40}{guard_interval}{ht_rxstbc}
 {vht_settings}
@@ -275,6 +276,7 @@
   enable_wmm = 'wmm_enabled=1' if opt.enable_wmm else ''
   hidden = 'ignore_broadcast_ssid=1' if opt.hidden_mode else ''
   bridge = 'bridge=%s' % opt.bridge if opt.bridge else ''
+  ap_isolate = 'ap_isolate=1' if opt.client_isolation else ''
   hostapd_conf_parts = [_HOSTCONF_TPL.format(
       interface=interface, band=band, channel=channel, width=width,
       protocols=protocols, hostapd_band=hostapd_band,
@@ -282,7 +284,8 @@
       require_ht=require_ht, require_vht=require_vht, ht20=ht20, ht40=ht40,
       ht_rxstbc=ht_rxstbc, vht_settings=vht_settings,
       guard_interval=guard_interval, enable_wmm=enable_wmm, hidden=hidden,
-      auth_algs=auth_algs, bridge=bridge, ssid=utils.sanitize_ssid(opt.ssid))]
+      ap_isolate=ap_isolate, auth_algs=auth_algs, bridge=bridge,
+      ssid=utils.sanitize_ssid(opt.ssid))]
 
   if opt.encryption != 'NONE':
     hostapd_conf_parts.append(_HOSTCONF_WPA_TPL.format(
diff --git a/wifi/configs_test.py b/wifi/configs_test.py
index 3956642..77773ce 100755
--- a/wifi/configs_test.py
+++ b/wifi/configs_test.py
@@ -309,6 +309,7 @@
 
 
 
+
 ht_capab=[HT20][RX-STBC1]
 
 """
@@ -331,6 +332,7 @@
 
 
 
+
 ht_capab=[HT20][RX-STBC1]
 
 """
@@ -363,6 +365,7 @@
     self.yottasecond_timeouts = False
     self.persist = False
     self.interface_suffix = ''
+    self.client_isolation = False
 
 
 # pylint: disable=protected-access
diff --git a/wifi/iw.py b/wifi/iw.py
index db6590f..647e56c 100644
--- a/wifi/iw.py
+++ b/wifi/iw.py
@@ -53,8 +53,9 @@
   return subprocess.check_output(('iw', 'dev', interface, 'link'), **kwargs)
 
 
-def _scan(interface, **kwargs):
-  return subprocess.check_output(('iw', 'dev', interface, 'scan'), **kwargs)
+def _scan(interface, scan_args, **kwargs):
+  return subprocess.check_output(['iw', 'dev', interface, 'scan'] + scan_args,
+                                 **kwargs)
 
 
 _WIPHY_RE = re.compile(r'Wiphy (?P<phy>\S+)')
@@ -362,6 +363,6 @@
   return result
 
 
-def scan(interface):
+def scan(interface, scan_args):
   """Return 'iw scan' output for printing."""
-  return _scan(interface)
+  return _scan(interface, scan_args)
diff --git a/wifi/utils.py b/wifi/utils.py
index 7a90065..c3b237e 100644
--- a/wifi/utils.py
+++ b/wifi/utils.py
@@ -355,3 +355,7 @@
   subprocess.call(['lockfile-create', '--use-pid', '--retry', str(retries),
                    lockfile])
   signal.alarm(0)
+
+
+def unlock(lockfile):
+  subprocess.call(['lockfile-remove', lockfile])
diff --git a/wifi/wifi.py b/wifi/wifi.py
index fb38bcd..9ba8b31 100755
--- a/wifi/wifi.py
+++ b/wifi/wifi.py
@@ -4,6 +4,7 @@
 
 from __future__ import print_function
 
+import atexit
 import glob
 import os
 import re
@@ -39,6 +40,7 @@
 bssid=                            BSSID to use []
 e,encryption=                     Encryption type to use (WPA_PSK_AES, WPA2_PSK_AES, WPA12_PSK_AES, WPA_PSK_TKIP, WPA2_PSK_TKIP, WPA12_PSK_TKIP, WEP, or NONE) [WPA2_PSK_AES]
 f,force-restart                   Force restart even if already running with these options
+C,client-isolation                Enable client isolation, preventing bridging of frames between associated stations.
 H,hidden-mode                     Enable hidden mode (disable SSID advertisements)
 M,enable-wmm                      Enable wmm extensions (needed for block acks)
 G,short-guard-interval            Enable short guard interval
@@ -50,10 +52,14 @@
 P,persist                         For set commands, persist options so we can restore them with 'wifi restore'.  For stop commands, remove persisted options.
 S,interface-suffix=               Interface suffix []
 lock-timeout=                     How long, in seconds, to wait for another /bin/wifi process to finish before giving up. [60]
+scan-ap-force                     (Scan only) scan when in AP mode
+scan-passive                      (Scan only) do not probe, scan passively
+scan-freq=                        (Scan only) limit scan to specific frequencies.
 """
 
 _FINGERPRINTS_DIRECTORY = '/tmp/wifi/fingerprints'
 _LOCKFILE = '/tmp/wifi/wifi'
+lockfile_taken = False
 
 
 # pylint: disable=protected-access
@@ -513,7 +519,15 @@
   if interface is None:
     raise utils.BinWifiException('No client interface for band %s', band)
 
-  print(iw.scan(interface))
+  scan_args = []
+  if opt.scan_ap_force:
+    scan_args += ['ap-force']
+  if opt.scan_passive:
+    scan_args += ['passive']
+  if opt.scan_freq:
+    scan_args += ['freq', opt.scan_freq]
+
+  print(iw.scan(interface, scan_args))
 
   return True
 
@@ -976,6 +990,8 @@
   Raises:
     BinWifiException: On file write failures.
   """
+  global lockfile_taken
+
   optspec = _OPTSPEC_FORMAT.format(
       bin=__file__.split('/')[-1],
       ssid='%s_TestWifi' % subprocess.check_output(('serial')).strip())
@@ -983,8 +999,6 @@
   opt, _, extra = parser.parse(argv)
   stringify_options(opt)
 
-  utils.lock(_LOCKFILE, int(opt.lock_timeout))
-
   if not extra:
     parser.fatal('Must specify a command (see usage for details).')
     return 1
@@ -1008,6 +1022,11 @@
   except KeyError:
     parser.fatal('Unrecognized command %s' % extra[0])
 
+  if not lockfile_taken:
+    utils.lock(_LOCKFILE, int(opt.lock_timeout))
+    atexit.register(utils.unlock, _LOCKFILE)
+    lockfile_taken = True
+
   success = function(opt)
 
   if success: