Merge lp:~sergiusens/ciborium/inital_implementation into lp:ciborium

Proposed by Sergio Schvezov
Status: Merged
Approved by: Ricardo Salveti
Approved revision: 3
Merged at revision: 3
Proposed branch: lp:~sergiusens/ciborium/inital_implementation
Merge into: lp:ciborium
Diff against target: 349 lines (+331/-0)
3 files modified
cmd/ciborium/main.go (+89/-0)
notifications/notifications.go (+64/-0)
udisks2/udisks2.go (+178/-0)
To merge this branch: bzr merge lp:~sergiusens/ciborium/inital_implementation
Reviewer Review Type Date Requested Status
Ricardo Salveti (community) Approve
Review via email: mp+229543@code.launchpad.net

Description of the change

Golang setup:

sudo apt install golang-go
# export GOPATH; e.g.;
export GOPATH=~/go

mkdir -p $GOPATH/src/launchpad.net
cd $GOPATH/src/launchpad.net
bzr branch lp:~sergiusens/ciborium/initial_implementation ciborium

To test on desktop:
go build launchpad.net/ciborium/cmd/ciborium
# go get missing deps and iterate
./ciborium

To test on device:
# prereq for happy case
# * udisks2 polkit rules set to "yes" for everything
# * make /media writable
GOARCH=arm go build launchpad.net/ciborium/cmd/ciborium
adb push ciborium /tmp
adb shell
sudo -iu phablet
/tmp/ciborium

To post a comment you must log in.
3. By Sergio Schvezov

Reformatting messages

Revision history for this message
Ricardo Salveti (rsalveti) wrote :

Looks fine, go for it.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'cmd'
2=== added directory 'cmd/ciborium'
3=== added file 'cmd/ciborium/main.go'
4--- cmd/ciborium/main.go 1970-01-01 00:00:00 +0000
5+++ cmd/ciborium/main.go 2014-08-05 02:16:17 +0000
6@@ -0,0 +1,89 @@
7+/*
8+ * Copyright 2014 Canonical Ltd.
9+ *
10+ * Authors:
11+ * Sergio Schvezov: sergio.schvezov@cannical.com
12+ *
13+ * ciborium is free software; you can redistribute it and/or modify
14+ * it under the terms of the GNU General Public License as published by
15+ * the Free Software Foundation; version 3.
16+ *
17+ * nuntium is distributed in the hope that it will be useful,
18+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
19+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+ * GNU General Public License for more details.
21+ *
22+ * You should have received a copy of the GNU General Public License
23+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
24+ */
25+
26+package main
27+
28+import (
29+ "fmt"
30+ "log"
31+ "time"
32+
33+ "launchpad.net/ciborium/notifications"
34+ "launchpad.net/ciborium/udisks2"
35+ "launchpad.net/go-dbus/v1"
36+)
37+
38+type message struct{ Summary, Body string }
39+
40+var (
41+ msgStorageSucces message = message{
42+ Summary: "Storage device detected",
43+ Body: "This device will be scanned for new content",
44+ }
45+
46+ msgStorageFail message = message{
47+ Summary: "Failed to add storage device",
48+ Body: "Make sure the storage device is correctly formated",
49+ }
50+)
51+
52+var supportedFS []string = []string{"vfat"}
53+
54+func main() {
55+ var (
56+ systemBus, sessionBus *dbus.Connection
57+ err error
58+ )
59+
60+ if systemBus, err = dbus.Connect(dbus.SystemBus); err != nil {
61+ log.Fatal("Connection error: ", err)
62+ }
63+ log.Print("Using system bus on ", systemBus.UniqueName)
64+
65+ if sessionBus, err = dbus.Connect(dbus.SessionBus); err != nil {
66+ log.Fatal("Connection error: ", err)
67+ }
68+ log.Print("Using session bus on ", sessionBus.UniqueName)
69+
70+ udisks2, err := udisks2.NewStorageWatcher(systemBus, supportedFS...)
71+ if err != nil {
72+ log.Fatal(err)
73+ }
74+
75+ timeout := time.Second * 4
76+ n := notifications.NewNotificationHandler(sessionBus, "ciborium", "system-settings", timeout)
77+
78+ go func() {
79+ for a := range udisks2.DriveAdded {
80+ if mountpoint, err := a.Mount(systemBus); err != nil {
81+ if err := n.SimpleNotify(msgStorageFail.Summary, msgStorageFail.Body); err != nil {
82+ log.Println(err)
83+ }
84+ } else {
85+ fmt.Println("Mounted", mountpoint)
86+ if err := n.SimpleNotify(msgStorageSucces.Summary, msgStorageSucces.Body); err != nil {
87+ log.Println(err)
88+ }
89+ }
90+ }
91+ }()
92+
93+ done := make(chan bool)
94+ <-done
95+}
96
97=== added directory 'notifications'
98=== added file 'notifications/notifications.go'
99--- notifications/notifications.go 1970-01-01 00:00:00 +0000
100+++ notifications/notifications.go 2014-08-05 02:16:17 +0000
101@@ -0,0 +1,64 @@
102+/*
103+ * Copyright 2014 Canonical Ltd.
104+ *
105+ * Authors:
106+ * Sergio Schvezov: sergio.schvezov@cannical.com
107+ *
108+ * ciborium is free software; you can redistribute it and/or modify
109+ * it under the terms of the GNU General Public License as published by
110+ * the Free Software Foundation; version 3.
111+ *
112+ * nuntium is distributed in the hope that it will be useful,
113+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
114+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
115+ * GNU General Public License for more details.
116+ *
117+ * You should have received a copy of the GNU General Public License
118+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
119+ */
120+
121+// Notifications lives on a well-knwon bus.Address
122+
123+package notifications
124+
125+import (
126+ "time"
127+
128+ "launchpad.net/go-dbus/v1"
129+)
130+
131+const (
132+ dbusName = "org.freedesktop.Notifications"
133+ dbusInterface = "org.freedesktop.Notifications"
134+ dbusPath = "/org/freedesktop/Notifications"
135+ dbusNotifyMethod = "Notify"
136+)
137+
138+type VariantMap map[string]dbus.Variant
139+
140+type Notification struct {
141+ dbusObject *dbus.ObjectProxy
142+ appName, icon string
143+ timeout int32
144+}
145+
146+func NewNotificationHandler(conn *dbus.Connection, appName, icon string, timeout time.Duration) *Notification {
147+ return &Notification{
148+ dbusObject: conn.Object(dbusName, dbusPath),
149+ appName: appName,
150+ icon: icon,
151+ timeout: int32(timeout.Seconds()) * 1000,
152+ }
153+}
154+
155+func (n *Notification) SimpleNotify(summary, body string) error {
156+ return n.Notify(summary, body, nil, nil)
157+}
158+
159+func (n *Notification) Notify(summary, body string, actions []string, hints VariantMap) error {
160+ var reuseId uint32
161+ if _, err := n.dbusObject.Call(dbusInterface, dbusNotifyMethod, n.appName, reuseId, n.icon, summary, body, actions, hints, n.timeout); err != nil {
162+ return err
163+ }
164+ return nil
165+}
166
167=== added directory 'udisks2'
168=== added file 'udisks2/udisks2.go'
169--- udisks2/udisks2.go 1970-01-01 00:00:00 +0000
170+++ udisks2/udisks2.go 2014-08-05 02:16:17 +0000
171@@ -0,0 +1,178 @@
172+/*
173+ * Copyright 2014 Canonical Ltd.
174+ *
175+ * Authors:
176+ * Sergio Schvezov: sergio.schvezov@cannical.com
177+ *
178+ * ciborium is free software; you can redistribute it and/or modify
179+ * it under the terms of the GNU General Public License as published by
180+ * the Free Software Foundation; version 3.
181+ *
182+ * nuntium is distributed in the hope that it will be useful,
183+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
184+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
185+ * GNU General Public License for more details.
186+ *
187+ * You should have received a copy of the GNU General Public License
188+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
189+ */
190+
191+package udisks2
192+
193+import (
194+ "reflect"
195+ "runtime"
196+ "sort"
197+
198+ "log"
199+
200+ "launchpad.net/go-dbus/v1"
201+)
202+
203+const (
204+ dbusName = "org.freedesktop.UDisks2"
205+ dbusObject = "/org/freedesktop/UDisks2"
206+ dbusObjectManagerInterface = "org.freedesktop.DBus.ObjectManager"
207+ dbusBlockInterface = "org.freedesktop.UDisks2.Block"
208+ dbusFilesystemInterface = "org.freedesktop.UDisks2.Filesystem"
209+ dbusAddedSignal = "InterfacesAdded"
210+ dbusRemovedSignal = "InterfacesRemoved"
211+)
212+
213+type VariantMap map[string]dbus.Variant
214+type InterfacesAndProperties map[string]VariantMap
215+
216+type Storage struct {
217+ Path dbus.ObjectPath
218+ Props InterfacesAndProperties
219+}
220+
221+type driveMap map[dbus.ObjectPath]InterfacesAndProperties
222+
223+type UDisks2 struct {
224+ conn *dbus.Connection
225+ validFS sort.StringSlice
226+ DriveAdded chan *Storage
227+ driveAdded *dbus.SignalWatch
228+ drives driveMap
229+}
230+
231+func (u *UDisks2) connectToSignal(path dbus.ObjectPath, inter, member string) (*dbus.SignalWatch, error) {
232+ w, err := u.conn.WatchSignal(&dbus.MatchRule{
233+ Type: dbus.TypeSignal,
234+ Sender: dbusName,
235+ Interface: dbusObjectManagerInterface,
236+ Member: member,
237+ Path: path})
238+ return w, err
239+}
240+
241+func (u *UDisks2) connectToSignalInterfacesAdded() (*dbus.SignalWatch, error) {
242+ return u.connectToSignal(dbusObject, dbusObjectManagerInterface, dbusAddedSignal)
243+}
244+
245+func (u *UDisks2) initInterfacesAddedChan() {
246+ go func() {
247+ for msg := range u.driveAdded.C {
248+ var addedEvent Storage
249+ if err := msg.Args(&addedEvent.Path, &addedEvent.Props); err != nil {
250+ log.Print(err)
251+ continue
252+ }
253+ if addedEvent.desiredEvent(u.validFS) {
254+ u.DriveAdded <- &addedEvent
255+ }
256+ }
257+ log.Print("Shutting down InterfacesAdded channel")
258+ close(u.DriveAdded)
259+ }()
260+
261+ u.emitExistingDevices()
262+}
263+
264+func (u *UDisks2) emitExistingDevices() {
265+ obj := u.conn.Object(dbusName, dbusObject)
266+ reply, err := obj.Call(dbusObjectManagerInterface, "GetManagedObjects")
267+ if err != nil {
268+ log.Println("Cannot get initial state for devices:", err)
269+ }
270+
271+ allDevices := make(map[dbus.ObjectPath]InterfacesAndProperties)
272+ if err := reply.Args(&allDevices); err != nil {
273+ log.Println("Cannot get initial state for devices:", err)
274+ }
275+
276+ for objectPath, props := range allDevices {
277+ s := Storage{objectPath, props}
278+ if s.desiredEvent(u.validFS) {
279+ u.DriveAdded <- &s
280+ }
281+ }
282+}
283+
284+func NewStorageWatcher(conn *dbus.Connection, filesystems ...string) (u *UDisks2, err error) {
285+ u = &UDisks2{
286+ conn: conn,
287+ validFS: sort.StringSlice(filesystems),
288+ DriveAdded: make(chan *Storage),
289+ }
290+ if err := u.initDriveWatch(); err != nil {
291+ return &UDisks2{}, err
292+ }
293+ runtime.SetFinalizer(u, cleanDriveWatch)
294+ return u, nil
295+}
296+
297+func cleanDriveWatch(u *UDisks2) {
298+ log.Print("Cancelling InterfacesAdded signal watch")
299+ u.driveAdded.Cancel()
300+}
301+
302+func (u *UDisks2) initDriveWatch() (err error) {
303+ if u.driveAdded, err = u.connectToSignalInterfacesAdded(); err != nil {
304+ return err
305+ }
306+ u.initInterfacesAddedChan()
307+ return nil
308+}
309+
310+func (s *Storage) desiredEvent(validFS sort.StringSlice) bool {
311+ if _, ok := s.Props[dbusFilesystemInterface]; !ok {
312+ return false
313+ }
314+
315+ propBlock, ok := s.Props[dbusBlockInterface]
316+ if !ok {
317+ log.Println("Doesn't hold block interface")
318+ return false
319+ }
320+ id, ok := propBlock["IdType"]
321+ if !ok {
322+ log.Println("Doesn't hold IdType")
323+ return false
324+ }
325+
326+ fs := reflect.ValueOf(id.Value).String()
327+ i := validFS.Search(fs)
328+ if i >= validFS.Len() || validFS[i] != fs {
329+ log.Println(fs, "not in:", validFS)
330+ return false
331+ }
332+
333+ return true
334+}
335+
336+func (s *Storage) Mount(conn *dbus.Connection) (mountpoint string, err error) {
337+ obj := conn.Object(dbusName, s.Path)
338+ options := make(VariantMap)
339+ options["auth.no_user_interaction"] = dbus.Variant{true}
340+ reply, err := obj.Call(dbusFilesystemInterface, "Mount", options)
341+ if err != nil {
342+ return "", err
343+ }
344+ if err := reply.Args(&mountpoint); err != nil {
345+ return "", err
346+ }
347+
348+ return mountpoint, err
349+}

Subscribers

People subscribed via source and target branches