-
Notifications
You must be signed in to change notification settings - Fork 609
cmd/snap-gpio-helper: add gpio-chardev export/unexport commands #15212
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cmd/snap-gpio-helper: add gpio-chardev export/unexport commands #15212
Conversation
8de225e
to
96bc984
Compare
96bc984
to
8d02191
Compare
cmd/snap-gpio-helper/common.go
Outdated
stat := &unix.Stat_t{} | ||
if err := unixStat(chip.Path(), stat); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
stat := &unix.Stat_t{} | |
if err := unixStat(chip.Path(), stat); err != nil { | |
var stat unix.Stat_t | |
if err := unixStat(chip.Path(), &stat); err != nil { |
cmd/snap-gpio-helper/chip.go
Outdated
"golang.org/x/sys/unix" | ||
) | ||
|
||
type GPIOChardev interface { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO this belongs somewhere in another package that is just imported here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was originally thinking of moving it to the gadget package, maybe it should go there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually thinking again about it, This is not going to get used in other places (at least for now) and moving into another package would unnecessarily fragment it. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tend to avoid mixing "main" and complex implementation. Would it be a problem to move out (mocking/testing)?
I will park further reviews until the base branch is merged. |
Thu Apr 17 09:23:30 UTC 2025 No spread failures reported |
@ZeyadYasser this needs a rebase |
8b9d45d
to
acd4ae0
Compare
gadget/device/gpio_chardev.go
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder whether this and some/all of common.go shouldn't instead go into a new sandbox/gpio?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First full pass. Thanks for writing this!
I have some small comments but mainly:
- Use
File.SyscallConn.Control
instead offile.Fd
- I'd strongly suggest to use contexts around
snap-gpio-helper
so that interrupting the process behaves correctly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
did a pass on the sandbox/gpio stuff mostly
sandbox/gpio/common.go
Outdated
} | ||
|
||
func removeEphermalUdevTaggingRule(gadget, slot string) error { | ||
// XXX: is rule reload/trigger nessacary |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo, also do we know the answer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAICT, not really since the device is removed afterwards anyway
sandbox/gpio/common.go
Outdated
if err := os.MkdirAll(filepath.Dir(devPath), 0755); err != nil { | ||
return err | ||
} | ||
if err := unixMknod(devPath, uint32(stat.Mode), int(stat.Rdev)); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this line probably merits a comment about what it does
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking about this some time and I wonder if we should symlink to the device that is already created by udev? I don't know what the design for this was but doing so would have the following consequences:
- For administrators, looking at /dev/snap would be self-documenting
- When the actual device goes away, the device node is removed and we have a stale symlink - an arguably better outcome.
I'm not pushing for this. I just want to know what you think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know what this is the state in the spec, of this but I think the original proposal was to use a symlink, I let Maciej and Zeyad comment on this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only doing a symlink on the slot side might not be possible, because the generated apparmor rule on plug connection needs to know the underlying aggregator chip path (/dev/gpiochipX) in advance and this will be dynamically allocated on boot.
snapd/interfaces/builtin/gpio_chardev.go
Lines 184 to 192 in cdb451c
func (iface *gpioChardevInterface) AppArmorConnectedPlug(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { | |
slotSnapName := slot.Snap().InstanceName() | |
plugSnapName := plug.Snap().InstanceName() | |
snippet := "# Allow access to exported gpio chardev lines\n" | |
// Allow access to exported virtual slot device. | |
snippet += fmt.Sprintf("/dev/snap/gpio-chardev/%s/%s rwk,\n", slotSnapName, slot.Name()) | |
// Allow access to plug-side symlink to exported virtual slot device. | |
snippet += fmt.Sprintf("/dev/snap/gpio-chardev/%s/{,*} r,\n", plugSnapName) | |
spec.AddSnippet(snippet) |
but I think the original proposal was to use a symlink
The plug-side device is indeed a symlink, the helper command is responsible for the slot-side device.
snapd/interfaces/builtin/gpio_chardev.go
Line 176 in cdb451c
ExecStart: fmt.Sprintf("/bin/sh -c 'mkdir -p %q && ln -s %q %q'", filepath.Dir(symlink), target, symlink), |
This is specified in SD129
create a character device node for the slot, with major/minor numbers corresponding to the newly created device
sandbox/gpio/common.go
Outdated
return filepath.Join(filepath.Join(dirs.GlobalRootDir, ephermalUdevRulesDir), fname) | ||
} | ||
|
||
func addEphermalUdevTaggingRule(ctx context.Context, chip *ChardevChip, instanceName, slotName string) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo Ephemeral
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! I have almost no reservations anymore.
I think my biggest question right now is the exact order of operations in the export path. See my comments inline for details.
Thanks for switching the FD handling over to Control
and for the nice use of contexts!
sandbox/gpio/common.go
Outdated
if err := os.MkdirAll(filepath.Dir(devPath), 0755); err != nil { | ||
return err | ||
} | ||
if err := unixMknod(devPath, uint32(stat.Mode), int(stat.Rdev)); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking about this some time and I wonder if we should symlink to the device that is already created by udev? I don't know what the design for this was but doing so would have the following consequences:
- For administrators, looking at /dev/snap would be self-documenting
- When the actual device goes away, the device node is removed and we have a stale symlink - an arguably better outcome.
I'm not pushing for this. I just want to know what you think.
@ZeyadYasser please work with @bboozzoo and @zyga to land this, no major objections from me at this point |
30f8d3c
to
5cfcd17
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I stopped reading around common.go.
Please have a look at the code around mknod (more important) and around timers (not much important).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with one extra nil check.
See my comment on the Range.String function as well, but don't implement anything more in this PR please.
sandbox/gpio/common.go
Outdated
} | ||
|
||
// This has to match the memory layout of `struct gpiochip_info` found | ||
// in /include/uapi/linux/gpio.h in the kernel. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// in /include/uapi/linux/gpio.h in the kernel. | |
// in /usr/include/uapi/linux/gpio.h in the kernel. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or without /
if within the kernel source tree, depending on which one this is referring to
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is in the kernel tree headers https://github.com/torvalds/linux/blob/834a4a689699090a406d1662b03affa8b155d025/include/uapi/linux/gpio.h#L32-L36
sandbox/gpio/common.go
Outdated
} | ||
|
||
stat, ok := fi.Sys().(*syscall.Stat_t) | ||
if !ok { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if !ok { | |
if !ok || stat == nil { |
var commaSeparated strings.Builder | ||
size := len(r) | ||
for i, span := range r { | ||
commaSeparated.WriteString(span.String()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI: there's the TextAppender interface for cases like this, so that we can avoid small bits of memory needed only to construct a buffer.
You can totally skip that but the API is interesting and worth looking at the rationale at golang/go#62384
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
main "github.com/snapcore/snapd/cmd/snap-gpio-helper" | ||
. "gopkg.in/check.v1" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
main "github.com/snapcore/snapd/cmd/snap-gpio-helper" | |
. "gopkg.in/check.v1" | |
. "gopkg.in/check.v1" | |
main "github.com/snapcore/snapd/cmd/snap-gpio-helper" | |
and in other files
main "github.com/snapcore/snapd/cmd/snap-gpio-helper" | ||
"github.com/snapcore/snapd/strutil" | ||
. "gopkg.in/check.v1" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
main "github.com/snapcore/snapd/cmd/snap-gpio-helper" | |
"github.com/snapcore/snapd/strutil" | |
. "gopkg.in/check.v1" | |
. "gopkg.in/check.v1" | |
main "github.com/snapcore/snapd/cmd/snap-gpio-helper" | |
"github.com/snapcore/snapd/strutil" | |
sandbox/gpio/common.go
Outdated
} | ||
|
||
// This has to match the memory layout of `struct gpiochip_info` found | ||
// in /include/uapi/linux/gpio.h in the kernel. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or without /
if within the kernel source tree, depending on which one this is referring to
sandbox/gpio/common.go
Outdated
return false, err | ||
} | ||
stat, ok := finfo.Sys().(*syscall.Stat_t) | ||
if !ok { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here too
Signed-off-by: Zeyad Gouda <[email protected]>
Signed-off-by: Zeyad Gouda <[email protected]>
Signed-off-by: Zeyad Gouda <[email protected]>
…g to be set Signed-off-by: Zeyad Gouda <[email protected]>
Signed-off-by: Zeyad Gouda <[email protected]>
Signed-off-by: Zeyad Gouda <[email protected]>
Signed-off-by: Zeyad Gouda <[email protected]>
Signed-off-by: Zeyad Gouda <[email protected]>
Signed-off-by: Zeyad Gouda <[email protected]>
Signed-off-by: Zeyad Gouda <[email protected]>
Signed-off-by: Zeyad Gouda <[email protected]>
Signed-off-by: Zeyad Gouda <[email protected]>
Signed-off-by: Zeyad Gouda <[email protected]>
Signed-off-by: Zeyad Gouda <[email protected]>
Signed-off-by: Zeyad Gouda <[email protected]>
Signed-off-by: Zeyad Gouda <[email protected]>
Signed-off-by: Zeyad Gouda <[email protected]>
Signed-off-by: Zeyad Gouda <[email protected]>
Signed-off-by: Zeyad Gouda <[email protected]>
Signed-off-by: Zeyad Gouda <[email protected]>
Signed-off-by: Zeyad Gouda <[email protected]>
f1a3fa2
to
0d11203
Compare
Note: This PR builds on top of #15172 to reuse and refactor common helpers.This PR adds implementation for the export/unexport commands for the snap-gpio-chardev helper.
Relevant commits, until #15172 is merged:Export command: 22bc4c4Unexport command: 8de225eFor context, please check the SD129 spec.