1#!/usr/bin/env python3

2

3 import sys

4 import os

5 import time

6

7 from subprocess import check_call, check_output, CalledProcessError

8

9 try:

10 from subprocess import DEVNULL # >= python3.3

11 except ImportError:

12 DEVNULL = open(os.devnull, "wb")

13

14 from uuid import uuid4

15 from argparse import ArgumentParser

16

17 CONNECTIONS_PATH = "/etc/NetworkManager/system-connections/"

18

19

20 def wifi_connection_section(ssid, uuid):

21

22 if not uuid:

23 uuid = uuid4()

24

25 connection = """

26[connection]

27 id=%s

28 uuid=%s

29 type=802-11-wireless

30 """ % (

31 ssid,

32 uuid,

33 )

34

35 wireless = """

36[802-11-wireless]

37 ssid=%s

38 mode=infrastructure""" % (

39 ssid

40 )

41

42 return connection + wireless

43

44

45 def wifi_security_section(security, key):

46 # Add security field to 802-11-wireless section

47 wireless_security = """

48 security=802-11-wireless-security

49

50[802-11-wireless-security]

51 """

52

53 if security.lower() == "wpa":

54 wireless_security += (

55 """

56 key-mgmt=wpa-psk

57 auth-alg=open

58 psk=%s

59 """

60 % key

61 )

62

63 elif security.lower() == "wep":

64 wireless_security += (

65 """

66 key-mgmt=none

67 wep-key=%s

68 """

69 % key

70 )

71

72 return wireless_security

73

74

75 def wifi_ip_sections():

76 ip = """

77[ipv4]

78 method=auto

79

80[ipv6]

81 method=auto

82 """

83

84 return ip

85

86

87 def mobilebroadband_connection_section(name, uuid, connection_type):

88 if not uuid:

89 uuid = uuid4()

90

91 connection_section = """

92[connection]

93 id={name}

94 uuid={uuid}

95 type={type}

96 autoconnect=false

97 """.format(

98 name=name, uuid=uuid, type=connection_type

99 )

100

101 return connection_section

102

103

104 def mobilebroadband_type_section(

105 connection_type, apn, username, password, pin

106):

107 number = "*99#" if connection_type == "gsm" else "#777"

108 type_section = """

109[{type}]

110 number={number}

111""".format(

112 type=connection_type, number=number

113 )

114

115 if apn:

116 type_section += "\napn={apn}".format(apn=apn)

117 if username:

118 type_section += "\nusername={username}".format(username=username)

119 if password:

120 type_section += "\npassword={password}".format(password=password)

121 if pin:

122 type_section += "\npin={pin}".format(pin=pin)

123

124 return type_section

125

126

127 def mobilebroadband_ppp_section():

128 return """

129[ppp]

130 lcp-echo-interval=4

131 lcp-echo-failure=30

132 """

133

134

135 def mobilebroadband_ip_section():

136 return """

137[ipv4]

138 method=auto

139 """

140

141

142 def mobilebroadband_serial_section():

143 return """

144[serial]

145 baud=115200

146 """

147

148

149 def block_until_created(connection, retries, interval):

150 while retries > 0:

151 try:

152 nmcli_con_list = check_output(

153 ["nmcli", "con", "list"],

154 stderr=DEVNULL,

155 universal_newlines=True,

156 )

157 except CalledProcessError:

158 check_call(["nmcli", "con", "reload"])

159 nmcli_con_list = check_output(

160 ["nmcli", "con", "show"],

161 stderr=DEVNULL,

162 universal_newlines=True,

163 )

164

165 if connection in nmcli_con_list:

166 print("Connection %s registered" % connection)

167 break

168

169 time.sleep(interval)

170 retries = retries - 1

171

172 if retries <= 0:

173 print("Failed to register %s." % connection, file=sys.stderr)

174 sys.exit(1)

175 else:

176 try:

177 check_call(["nmcli", "con", "up", "id", connection])

178 print("Connection %s activated." % connection)

179 except CalledProcessError as error:

180 print("Failed to activate %s." % connection, file=sys.stderr)

181 sys.exit(error.returncode)

182

183

184 def write_connection_file(name, connection_info):

185 try:

186 connection_file = open(CONNECTIONS_PATH + name, "w")

187 connection_file.write(connection_info)

188 os.fchmod(connection_file.fileno(), 0o600)

189 connection_file.close()

190 except IOError:

191 print(

192 "Can't write to "

193 + CONNECTIONS_PATH

194 + name

195 + ". Is this command being run as root?",

196 file=sys.stderr,

197 )

198 sys.exit(1)

199

200

201 def create_wifi_connection(args):

202 wifi_connection = wifi_connection_section(args.ssid, args.uuid)

203

204 if args.security:

205 # Set security options

206 if not args.key:

207 print(

208 "You need to specify a key using --key "

209 "if using wireless security.",

210 file=sys.stderr,

211 )

212 sys.exit(1)

213

214 wifi_connection += wifi_security_section(args.security, args.key)

215 elif args.key:

216 print(

217 "You specified an encryption key "

218 "but did not give a security type "

219 "using --security.",

220 file=sys.stderr,

221 )

222 sys.exit(1)

223

224 try:

225 check_call(["rfkill", "unblock", "wlan", "wifi"])

226 except CalledProcessError:

227 print(

228 "Could not unblock wireless " "devices with rfkill.",

229 file=sys.stderr,

230 )

231 # Don't fail the script if unblock didn't work though

232

233 wifi_connection += wifi_ip_sections()

234

235 # NetworkManager replaces forward-slashes in SSIDs with asterisks

236 name = args.ssid.replace("/", "*")

237 write_connection_file(name, wifi_connection)

238

239 return name

240

241

242 def create_mobilebroadband_connection(args):

243 name = args.name

244

245 mobilebroadband_connection = mobilebroadband_connection_section(

246 name, args.uuid, args.type

247 )

248 mobilebroadband_connection += mobilebroadband_type_section(

249 args.type, args.apn, args.username, args.password, args.pin

250 )

251

252 if args.type == "cdma":

253 mobilebroadband_connection += mobilebroadband_ppp_section()

254

255 mobilebroadband_connection += mobilebroadband_ip_section()

256 mobilebroadband_connection += mobilebroadband_serial_section()

257

258 write_connection_file(name, mobilebroadband_connection)

259 return name

260

261

262 def main():

263 parser = ArgumentParser()

264 subparsers = parser.add_subparsers(help="sub command help")

265

266 wifi_parser = subparsers.add_parser(

267 "wifi", help="Create a Wifi connection."

268 )

269 wifi_parser.add_argument("ssid", help="The SSID to connect to.")

270 wifi_parser.add_argument(

271 "-S",

272 "--security",

273 choices=["wpa", "wep"],

274 help=(

275 "The type of security to be used by the "

276 "connection. No security will be used if "

277 "nothing is specified."

278 ),

279 )

280 wifi_parser.add_argument(

281 "-K", "--key", help="The encryption key required by the router."

282 )

283 wifi_parser.set_defaults(func=create_wifi_connection)

284

285 mobilebroadband_parser = subparsers.add_parser(

286 "mobilebroadband",

287 help="Create a " "mobile " "broadband " "connection.",

288 )

289 mobilebroadband_parser.add_argument(

290 "type", choices=["gsm", "cdma"], help="The type of connection."

291 )

292 mobilebroadband_parser.add_argument(

293 "-n", "--name", default="MobileBB", help="The name of the connection."

294 )

295 mobilebroadband_parser.add_argument(

296 "-a", "--apn", help="The APN to connect to."

297 )

298 mobilebroadband_parser.add_argument(

299 "-u",

300 "--username",

301 help="The username required by the " "mobile broadband access point.",

302 )

303 mobilebroadband_parser.add_argument(

304 "-p",

305 "--password",

306 help="The password required by the " "mobile broadband access point.",

307 )

308 mobilebroadband_parser.add_argument(

309 "-P", "--pin", help="The PIN of the SIM " "card, if set."

310 )

311 mobilebroadband_parser.set_defaults(func=create_mobilebroadband_connection)

312

313 parser.add_argument(

314 "-U",

315 "--uuid",

316 help="""The uuid to assign to the connection for use by

317 NetworkManager. One will be generated if not

318 specified here.""",

319 )

320 parser.add_argument(

321 "-R",

322 "--retries",

323 help="""The number of times to attempt bringing up the

324 connection until it is confirmed as active.""",

325 default=5,

326 )

327 parser.add_argument(

328 "-I",

329 "--interval",

330 help=(

331 "The time to wait between attempts to detect "

332 "the registration of the connection."

333 ),

334 default=2,

335 )

336 args = parser.parse_args()

337

338 # Call function to create the appropriate connection type

339 connection_name = args.func(args)

340 # Make sure we don't exit until the connection is fully created

341 block_until_created(connection_name, args.retries, args.interval)

342

343

344 if __name__ == "__main__":

345 main()