Merge lp:~pedronis/ubuntu-push/empty into lp:ubuntu-push
- empty
- Merge into trunk
Proposed by
Samuele Pedroni
Status: | Superseded |
---|---|
Proposed branch: | lp:~pedronis/ubuntu-push/empty |
Merge into: | lp:ubuntu-push |
Diff against target: |
564 lines (+339/-29) 8 files modified
client/client.go (+6/-6) client/client_test.go (+14/-0) config/config.go (+130/-21) config/config_test.go (+105/-0) logger/logger.go (+27/-0) logger/logger_test.go (+25/-0) server/acceptance/cmd/acceptanceclient.go (+7/-2) ubuntu-push-client.go (+25/-0) |
To merge this branch: | bzr merge lp:~pedronis/ubuntu-push/empty |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Push Hackers | Pending | ||
Review via email:
|
This proposal has been superseded by a proposal from 2014-04-15.
Commit message
empty merge for testing
Description of the change
To post a comment you must log in.
Unmerged revisions
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'client/client.go' |
2 | --- client/client.go 2014-04-11 16:37:48 +0000 |
3 | +++ client/client.go 2014-04-15 15:10:41 +0000 |
4 | @@ -57,7 +57,7 @@ |
5 | // The PEM-encoded server certificate |
6 | CertPEMFile string `json:"cert_pem_file"` |
7 | // The logging level (one of "debug", "info", "error") |
8 | - LogLevel string `json:"log_level"` |
9 | + LogLevel logger.ConfigLogLevel `json:"log_level"` |
10 | } |
11 | |
12 | // PushClient is the Ubuntu Push Notifications client-side daemon. |
13 | @@ -95,13 +95,13 @@ |
14 | |
15 | // configure loads its configuration, and sets it up. |
16 | func (client *PushClient) configure() error { |
17 | - f, err := os.Open(client.configPath) |
18 | + _, err := os.Stat(client.configPath) |
19 | if err != nil { |
20 | - return fmt.Errorf("opening config: %v", err) |
21 | + return fmt.Errorf("config: %v", err) |
22 | } |
23 | - err = config.ReadConfig(f, &client.config) |
24 | + err = config.ReadFiles(&client.config, client.configPath, "<flags>") |
25 | if err != nil { |
26 | - return fmt.Errorf("reading config: %v", err) |
27 | + return fmt.Errorf("config: %v", err) |
28 | } |
29 | // ignore spaces |
30 | client.config.Addr = strings.Replace(client.config.Addr, " ", "", -1) |
31 | @@ -110,7 +110,7 @@ |
32 | } |
33 | |
34 | // later, we'll be specifying more logging options in the config file |
35 | - client.log = logger.NewSimpleLogger(os.Stderr, client.config.LogLevel) |
36 | + client.log = logger.NewSimpleLogger(os.Stderr, client.config.LogLevel.Level()) |
37 | |
38 | // overridden for testing |
39 | client.idder = identifier.New() |
40 | |
41 | === modified file 'client/client_test.go' |
42 | --- client/client_test.go 2014-04-11 16:21:45 +0000 |
43 | +++ client/client_test.go 2014-04-15 15:10:41 +0000 |
44 | @@ -19,10 +19,12 @@ |
45 | import ( |
46 | "encoding/json" |
47 | "errors" |
48 | + "flag" |
49 | "fmt" |
50 | "io/ioutil" |
51 | "net/http" |
52 | "net/http/httptest" |
53 | + "os" |
54 | "path/filepath" |
55 | "reflect" |
56 | "testing" |
57 | @@ -37,6 +39,7 @@ |
58 | testibus "launchpad.net/ubuntu-push/bus/testing" |
59 | "launchpad.net/ubuntu-push/client/session" |
60 | "launchpad.net/ubuntu-push/client/session/levelmap" |
61 | + "launchpad.net/ubuntu-push/config" |
62 | helpers "launchpad.net/ubuntu-push/testing" |
63 | "launchpad.net/ubuntu-push/testing/condition" |
64 | "launchpad.net/ubuntu-push/util" |
65 | @@ -79,6 +82,7 @@ |
66 | } |
67 | |
68 | func (cs *clientSuite) SetUpSuite(c *C) { |
69 | + config.IgnoreParsedFlags = true // because configure() uses <flags> |
70 | cs.timeouts = util.SwapTimeouts([]time.Duration{0}) |
71 | cs.leveldbPath = "" |
72 | } |
73 | @@ -142,6 +146,16 @@ |
74 | c.Check(cli.config.ExchangeTimeout.TimeDuration(), Equals, time.Duration(10*time.Millisecond)) |
75 | } |
76 | |
77 | +func (cs *clientSuite) TestConfigureWorksWithFlags(c *C) { |
78 | + flag.CommandLine = flag.NewFlagSet("client", flag.ContinueOnError) |
79 | + os.Args = []string{"client", "-addr", "foo:7777"} |
80 | + cli := NewPushClient(cs.configPath, cs.leveldbPath) |
81 | + err := cli.configure() |
82 | + c.Assert(err, IsNil) |
83 | + c.Assert(cli.config, NotNil) |
84 | + c.Check(cli.config.Addr, Equals, "foo:7777") |
85 | +} |
86 | + |
87 | func (cs *clientSuite) TestConfigureSetsUpLog(c *C) { |
88 | cli := NewPushClient(cs.configPath, cs.leveldbPath) |
89 | c.Check(cli.log, IsNil) |
90 | |
91 | === modified file 'config/config.go' |
92 | --- config/config.go 2014-03-25 18:49:18 +0000 |
93 | +++ config/config.go 2014-04-15 15:10:41 +0000 |
94 | @@ -20,6 +20,7 @@ |
95 | import ( |
96 | "encoding/json" |
97 | "errors" |
98 | + "flag" |
99 | "fmt" |
100 | "io" |
101 | "io/ioutil" |
102 | @@ -27,6 +28,7 @@ |
103 | "os" |
104 | "path/filepath" |
105 | "reflect" |
106 | + "strconv" |
107 | "strings" |
108 | "time" |
109 | ) |
110 | @@ -118,6 +120,22 @@ |
111 | return fillDestConfig(destValue, p1) |
112 | } |
113 | |
114 | +// FromString are config holders that can be set by parsing a string. |
115 | +type FromString interface { |
116 | + SetFromString(enc string) error |
117 | +} |
118 | + |
119 | +// UnmarshalJSONViaString helps unmarshalling from JSON for FromString |
120 | +// supporting config holders. |
121 | +func UnmarshalJSONViaString(dest FromString, b []byte) error { |
122 | + var enc string |
123 | + err := json.Unmarshal(b, &enc) |
124 | + if err != nil { |
125 | + return err |
126 | + } |
127 | + return dest.SetFromString(enc) |
128 | +} |
129 | + |
130 | // ConfigTimeDuration can hold a time.Duration in a configuration struct, |
131 | // that is parsed from a string as supported by time.ParseDuration. |
132 | type ConfigTimeDuration struct { |
133 | @@ -125,13 +143,11 @@ |
134 | } |
135 | |
136 | func (ctd *ConfigTimeDuration) UnmarshalJSON(b []byte) error { |
137 | - var enc string |
138 | - var v time.Duration |
139 | - err := json.Unmarshal(b, &enc) |
140 | - if err != nil { |
141 | - return err |
142 | - } |
143 | - v, err = time.ParseDuration(enc) |
144 | + return UnmarshalJSONViaString(ctd, b) |
145 | +} |
146 | + |
147 | +func (ctd *ConfigTimeDuration) SetFromString(enc string) error { |
148 | + v, err := time.ParseDuration(enc) |
149 | if err != nil { |
150 | return err |
151 | } |
152 | @@ -148,12 +164,11 @@ |
153 | type ConfigHostPort string |
154 | |
155 | func (chp *ConfigHostPort) UnmarshalJSON(b []byte) error { |
156 | - var enc string |
157 | - err := json.Unmarshal(b, &enc) |
158 | - if err != nil { |
159 | - return err |
160 | - } |
161 | - _, _, err = net.SplitHostPort(enc) |
162 | + return UnmarshalJSONViaString(chp, b) |
163 | +} |
164 | + |
165 | +func (chp *ConfigHostPort) SetFromString(enc string) error { |
166 | + _, _, err := net.SplitHostPort(enc) |
167 | if err != nil { |
168 | return err |
169 | } |
170 | @@ -198,23 +213,117 @@ |
171 | return ioutil.ReadFile(p) |
172 | } |
173 | |
174 | -// ReadFiles reads configuration from a set of files. Uses ReadConfig internally. |
175 | +// used to implement getting config values with flag.Parse() |
176 | +type val struct { |
177 | + destField destField |
178 | + accu map[string]json.RawMessage |
179 | +} |
180 | + |
181 | +func (v *val) String() string { // used to show default |
182 | + return string(v.accu[v.destField.configName()]) |
183 | +} |
184 | + |
185 | +func (v *val) IsBoolFlag() bool { |
186 | + return v.destField.fld.Type.Kind() == reflect.Bool |
187 | +} |
188 | + |
189 | +func (v *val) marshalAsNeeded(s string) (json.RawMessage, error) { |
190 | + var toMarshal interface{} |
191 | + switch v.destField.dest.(type) { |
192 | + case *string, FromString: |
193 | + toMarshal = s |
194 | + case *bool: |
195 | + bit, err := strconv.ParseBool(s) |
196 | + if err != nil { |
197 | + return nil, err |
198 | + } |
199 | + toMarshal = bit |
200 | + default: |
201 | + return json.RawMessage(s), nil |
202 | + } |
203 | + return json.Marshal(toMarshal) |
204 | +} |
205 | + |
206 | +func (v *val) Set(s string) error { |
207 | + marshalled, err := v.marshalAsNeeded(s) |
208 | + if err != nil { |
209 | + return err |
210 | + } |
211 | + v.accu[v.destField.configName()] = marshalled |
212 | + return nil |
213 | +} |
214 | + |
215 | +func readOneConfig(accu map[string]json.RawMessage, cfgPath string) error { |
216 | + r, err := os.Open(cfgPath) |
217 | + if err != nil { |
218 | + return err |
219 | + } |
220 | + defer r.Close() |
221 | + err = json.NewDecoder(r).Decode(&accu) |
222 | + if err != nil { |
223 | + return err |
224 | + } |
225 | + return nil |
226 | +} |
227 | + |
228 | +// used to implement -cfg@= |
229 | +type readConfigAtVal struct { |
230 | + accu map[string]json.RawMessage |
231 | +} |
232 | + |
233 | +func (v *readConfigAtVal) String() string { |
234 | + return "<config.json>" |
235 | +} |
236 | + |
237 | +func (v *readConfigAtVal) Set(path string) error { |
238 | + return readOneConfig(v.accu, path) |
239 | +} |
240 | + |
241 | +// readUsingFlags gets config values from command line flags. |
242 | +func readUsingFlags(accu map[string]json.RawMessage, destValue reflect.Value) error { |
243 | + if flag.Parsed() { |
244 | + if IgnoreParsedFlags { |
245 | + return nil |
246 | + } |
247 | + return fmt.Errorf("too late, flags already parsed") |
248 | + } |
249 | + destStruct := destValue.Elem() |
250 | + for destField := range traverseStruct(destStruct) { |
251 | + help := destField.fld.Tag.Get("help") |
252 | + flag.Var(&val{destField, accu}, destField.configName(), help) |
253 | + } |
254 | + flag.Var(&readConfigAtVal{accu}, "cfg@", "get config values from file") |
255 | + flag.Parse() |
256 | + return nil |
257 | +} |
258 | + |
259 | +// IgnoreParsedFlags will just have ReadFiles ignore <flags> if the |
260 | +// command line was already parsed. |
261 | +var IgnoreParsedFlags = false |
262 | + |
263 | +// ReadFiles reads configuration from a set of files. The string |
264 | +// "<flags>" can be used as a pseudo file-path, it will consider |
265 | +// command line flags, invoking flag.Parse(). Among those the flag |
266 | +// -cfg@=FILE can be used to get further config values from FILE. |
267 | func ReadFiles(destConfig interface{}, cfgFpaths ...string) error { |
268 | destValue, err := checkDestConfig("destConfig", destConfig) |
269 | if err != nil { |
270 | return err |
271 | } |
272 | // do the parsing in two phases for better error handling |
273 | - var p1 map[string]json.RawMessage |
274 | + p1 := make(map[string]json.RawMessage) |
275 | readOne := false |
276 | for _, cfgPath := range cfgFpaths { |
277 | + if cfgPath == "<flags>" { |
278 | + err := readUsingFlags(p1, destValue) |
279 | + if err != nil { |
280 | + return err |
281 | + } |
282 | + readOne = true |
283 | + continue |
284 | + } |
285 | if _, err := os.Stat(cfgPath); err == nil { |
286 | - r, err := os.Open(cfgPath) |
287 | - if err != nil { |
288 | - return err |
289 | - } |
290 | - defer r.Close() |
291 | - err = json.NewDecoder(r).Decode(&p1) |
292 | + err := readOneConfig(p1, cfgPath) |
293 | if err != nil { |
294 | return err |
295 | } |
296 | |
297 | === modified file 'config/config_test.go' |
298 | --- config/config_test.go 2014-03-25 18:49:18 +0000 |
299 | +++ config/config_test.go 2014-04-15 15:10:41 +0000 |
300 | @@ -18,6 +18,9 @@ |
301 | |
302 | import ( |
303 | "bytes" |
304 | + "encoding/json" |
305 | + "flag" |
306 | + "fmt" |
307 | "io/ioutil" |
308 | "os" |
309 | "path/filepath" |
310 | @@ -230,3 +233,105 @@ |
311 | c.Check(res, DeepEquals, []string{"b", "c_list", "d"}) |
312 | |
313 | } |
314 | + |
315 | +type testConfig3 struct { |
316 | + A bool |
317 | + B string |
318 | + C []string `json:"c_list"` |
319 | + D ConfigTimeDuration `help:"duration"` |
320 | + E ConfigHostPort |
321 | + F string |
322 | +} |
323 | + |
324 | +type configFlagsSuite struct{} |
325 | + |
326 | +var _ = Suite(&configFlagsSuite{}) |
327 | + |
328 | +func (s *configFlagsSuite) SetUpTest(c *C) { |
329 | + flag.CommandLine = flag.NewFlagSet("cmd", flag.PanicOnError) |
330 | + // supress outputs |
331 | + flag.Usage = func() { flag.PrintDefaults() } |
332 | + flag.CommandLine.SetOutput(ioutil.Discard) |
333 | +} |
334 | + |
335 | +func (s *configFlagsSuite) TestReadUsingFlags(c *C) { |
336 | + os.Args = []string{"cmd", "-a=1", "-b=foo", "-c_list", `["x","y"]`, "-d", "10s", "-e=localhost:80"} |
337 | + var cfg testConfig3 |
338 | + p := make(map[string]json.RawMessage) |
339 | + err := readUsingFlags(p, reflect.ValueOf(&cfg)) |
340 | + c.Assert(err, IsNil) |
341 | + c.Check(p, DeepEquals, map[string]json.RawMessage{ |
342 | + "a": json.RawMessage("true"), |
343 | + "b": json.RawMessage(`"foo"`), |
344 | + "c_list": json.RawMessage(`["x","y"]`), |
345 | + "d": json.RawMessage(`"10s"`), |
346 | + "e": json.RawMessage(`"localhost:80"`), |
347 | + }) |
348 | +} |
349 | + |
350 | +func (s *configFlagsSuite) TestReadUsingFlagsBoolError(c *C) { |
351 | + os.Args = []string{"cmd", "-a=zoo"} |
352 | + var cfg testConfig3 |
353 | + p := make(map[string]json.RawMessage) |
354 | + c.Check(func() { readUsingFlags(p, reflect.ValueOf(&cfg)) }, PanicMatches, ".*invalid boolean.*-a.*") |
355 | +} |
356 | + |
357 | +func (s *configFlagsSuite) TestReadFilesAndFlags(c *C) { |
358 | + // test <flags> pseudo file |
359 | + os.Args = []string{"cmd", "-b=x"} |
360 | + tmpDir := c.MkDir() |
361 | + cfgPath := filepath.Join(tmpDir, "cfg.json") |
362 | + err := ioutil.WriteFile(cfgPath, []byte(`{"a": 42, "c_list": ["y", "z"]}`), os.ModePerm) |
363 | + c.Assert(err, IsNil) |
364 | + var cfg testConfig1 |
365 | + err = ReadFiles(&cfg, cfgPath, "<flags>") |
366 | + c.Assert(err, IsNil) |
367 | + c.Check(cfg.A, Equals, 42) |
368 | + c.Check(cfg.B, Equals, "x") |
369 | + c.Check(cfg.C, DeepEquals, []string{"y", "z"}) |
370 | +} |
371 | + |
372 | +func (s *configFlagsSuite) TestReadFilesAndFlagsConfigAtSupport(c *C) { |
373 | + // test <flags> pseudo file |
374 | + tmpDir := c.MkDir() |
375 | + cfgPath := filepath.Join(tmpDir, "cfg.json") |
376 | + os.Args = []string{"cmd", "-a=42", fmt.Sprintf("-cfg@=%s", cfgPath)} |
377 | + err := ioutil.WriteFile(cfgPath, []byte(`{"b": "x", "c_list": ["y", "z"]}`), os.ModePerm) |
378 | + c.Assert(err, IsNil) |
379 | + var cfg testConfig1 |
380 | + err = ReadFiles(&cfg, "<flags>") |
381 | + c.Assert(err, IsNil) |
382 | + c.Check(cfg.A, Equals, 42) |
383 | + c.Check(cfg.B, Equals, "x") |
384 | + c.Check(cfg.C, DeepEquals, []string{"y", "z"}) |
385 | +} |
386 | + |
387 | +func (s *configFlagsSuite) TestReadUsingFlagsHelp(c *C) { |
388 | + os.Args = []string{"cmd", "-h"} |
389 | + buf := bytes.NewBufferString("") |
390 | + flag.CommandLine.Init("cmd", flag.ContinueOnError) |
391 | + flag.CommandLine.SetOutput(buf) |
392 | + var cfg testConfig3 |
393 | + p := map[string]json.RawMessage{ |
394 | + "d": json.RawMessage(`"2s"`), |
395 | + } |
396 | + readUsingFlags(p, reflect.ValueOf(&cfg)) |
397 | + c.Check(buf.String(), Matches, `(?s).*-cfg@=<config.json>: get config values from file\n.*-d="2s": duration.*`) |
398 | +} |
399 | + |
400 | +func (s *configFlagsSuite) TestReadUsingFlagsAlreadyParsed(c *C) { |
401 | + os.Args = []string{"cmd"} |
402 | + flag.Parse() |
403 | + var cfg struct{} |
404 | + p := make(map[string]json.RawMessage) |
405 | + err := readUsingFlags(p, reflect.ValueOf(&cfg)) |
406 | + c.Assert(err, ErrorMatches, "too late, flags already parsed") |
407 | + err = ReadFiles(&cfg, "<flags>") |
408 | + c.Assert(err, ErrorMatches, "too late, flags already parsed") |
409 | + IgnoreParsedFlags = true |
410 | + defer func() { |
411 | + IgnoreParsedFlags = false |
412 | + }() |
413 | + err = ReadFiles(&cfg, "<flags>") |
414 | + c.Assert(err, IsNil) |
415 | +} |
416 | |
417 | === modified file 'logger/logger.go' |
418 | --- logger/logger.go 2014-02-24 10:27:38 +0000 |
419 | +++ logger/logger.go 2014-04-15 15:10:41 +0000 |
420 | @@ -23,6 +23,8 @@ |
421 | "log" |
422 | "os" |
423 | "runtime" |
424 | + |
425 | + "launchpad.net/ubuntu-push/config" |
426 | ) |
427 | |
428 | // Logger is a simple logger interface with logging at levels. |
429 | @@ -119,3 +121,28 @@ |
430 | lg.outputFunc(2, fmt.Sprintf("DEBUG "+format, v...)) |
431 | } |
432 | } |
433 | + |
434 | +// config bits |
435 | + |
436 | +// ConfigLogLevel can hold a log level in a configuration struct. |
437 | +type ConfigLogLevel string |
438 | + |
439 | +func (cll *ConfigLogLevel) ConfigFromJSONString() {} |
440 | + |
441 | +func (cll *ConfigLogLevel) UnmarshalJSON(b []byte) error { |
442 | + return config.UnmarshalJSONViaString(cll, b) |
443 | +} |
444 | + |
445 | +func (cll *ConfigLogLevel) SetFromString(enc string) error { |
446 | + _, ok := levelToNLevel[enc] |
447 | + if !ok { |
448 | + return fmt.Errorf("not a log level: %s", enc) |
449 | + } |
450 | + *cll = ConfigLogLevel(enc) |
451 | + return nil |
452 | +} |
453 | + |
454 | +// Level returns the log level string held in cll. |
455 | +func (cll ConfigLogLevel) Level() string { |
456 | + return string(cll) |
457 | +} |
458 | |
459 | === modified file 'logger/logger_test.go' |
460 | --- logger/logger_test.go 2014-02-10 22:51:43 +0000 |
461 | +++ logger/logger_test.go 2014-04-15 15:10:41 +0000 |
462 | @@ -25,6 +25,8 @@ |
463 | "testing" |
464 | |
465 | . "launchpad.net/gocheck" |
466 | + |
467 | + "launchpad.net/ubuntu-push/config" |
468 | ) |
469 | |
470 | func TestLogger(t *testing.T) { TestingT(t) } |
471 | @@ -138,3 +140,26 @@ |
472 | logger.Output(1, "foobaz") |
473 | c.Check(buf.String(), Matches, "logger_test.go:[0-9]+: foobar\nlogger_test.go:[0-9]+: foobaz\n") |
474 | } |
475 | + |
476 | +type testLogLevelConfig struct { |
477 | + Lvl ConfigLogLevel |
478 | +} |
479 | + |
480 | +func (s *loggerSuite) TestReadConfigLogLevel(c *C) { |
481 | + buf := bytes.NewBufferString(`{"lvl": "debug"}`) |
482 | + var cfg testLogLevelConfig |
483 | + err := config.ReadConfig(buf, &cfg) |
484 | + c.Assert(err, IsNil) |
485 | + c.Check(cfg.Lvl.Level(), Equals, "debug") |
486 | +} |
487 | + |
488 | +func (s *loggerSuite) TestReadConfigLogLevelErrors(c *C) { |
489 | + var cfg testLogLevelConfig |
490 | + checkError := func(jsonCfg string, expectedError string) { |
491 | + buf := bytes.NewBufferString(jsonCfg) |
492 | + err := config.ReadConfig(buf, &cfg) |
493 | + c.Check(err, ErrorMatches, expectedError) |
494 | + } |
495 | + checkError(`{"lvl": 1}`, "lvl:.*type string") |
496 | + checkError(`{"lvl": "foo"}`, "lvl: not a log level: foo") |
497 | +} |
498 | |
499 | === modified file 'server/acceptance/cmd/acceptanceclient.go' |
500 | --- server/acceptance/cmd/acceptanceclient.go 2014-04-10 13:52:31 +0000 |
501 | +++ server/acceptance/cmd/acceptanceclient.go 2014-04-15 15:10:41 +0000 |
502 | @@ -48,13 +48,18 @@ |
503 | fmt.Fprintf(os.Stderr, "Usage: acceptancclient [options] <config.json> <device id>\n") |
504 | flag.PrintDefaults() |
505 | } |
506 | + missingArg := func(what string) { |
507 | + fmt.Fprintf(os.Stderr, "missing %s\n", what) |
508 | + flag.Usage() |
509 | + os.Exit(2) |
510 | + } |
511 | flag.Parse() |
512 | narg := flag.NArg() |
513 | switch { |
514 | case narg < 1: |
515 | - log.Fatal("missing config file") |
516 | + missingArg("config file") |
517 | case narg < 2: |
518 | - log.Fatal("missing device-id") |
519 | + missingArg("device-id") |
520 | } |
521 | configFName := flag.Arg(0) |
522 | f, err := os.Open(configFName) |
523 | |
524 | === modified file 'ubuntu-push-client.go' |
525 | --- ubuntu-push-client.go 2014-03-12 13:25:20 +0000 |
526 | +++ ubuntu-push-client.go 2014-04-15 15:10:41 +0000 |
527 | @@ -19,12 +19,37 @@ |
528 | import ( |
529 | "log" |
530 | |
531 | + "launchpad.net/go-dbus/v1" |
532 | "launchpad.net/go-xdg/v0" |
533 | |
534 | "launchpad.net/ubuntu-push/client" |
535 | ) |
536 | |
537 | +const NAME = "com.ubuntu.PushNotifications" |
538 | + |
539 | +// grabName grabs ownership of the dbus name, and bails the client as |
540 | +// soon as somebody else grabs it. |
541 | +func grabName() { |
542 | + conn, err := dbus.Connect(dbus.SessionBus) |
543 | + if err != nil { |
544 | + log.Fatalf("bus: %v", err) |
545 | + } |
546 | + |
547 | + flags := dbus.NameFlagAllowReplacement | dbus.NameFlagReplaceExisting |
548 | + n := conn.RequestName(NAME, flags) |
549 | + go func() { |
550 | + for err := range n.C { |
551 | + if err != nil { |
552 | + log.Fatalf("FATAL: name channel got: %v", err) |
553 | + } |
554 | + } |
555 | + }() |
556 | +} |
557 | + |
558 | func main() { |
559 | + // XXX: this is a quick hack to ensure unicity |
560 | + grabName() |
561 | + |
562 | cfgFname, err := xdg.Config.Find("ubuntu-push-client/config.json") |
563 | if err != nil { |
564 | log.Fatalf("unable to find a configuration file: %v", err) |