Skip to content

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

Merged
merged 21 commits into from
Apr 17, 2025

Conversation

ZeyadYasser
Copy link
Contributor

@ZeyadYasser ZeyadYasser commented Mar 15, 2025

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:

For context, please check the SD129 spec.

@ZeyadYasser ZeyadYasser added the Needs Samuele review Needs a review from Samuele before it can land label Mar 15, 2025
@ZeyadYasser ZeyadYasser force-pushed the gpiod-helper-commands branch from 8de225e to 96bc984 Compare March 15, 2025 15:56
@ZeyadYasser ZeyadYasser changed the title Gpiod helper commands cmd/snap-gpio-helper: add gpio-chardev export/unexport commands Mar 15, 2025
@ZeyadYasser ZeyadYasser force-pushed the gpiod-helper-commands branch from 96bc984 to 8d02191 Compare March 17, 2025 09:07
Comment on lines 154 to 155
stat := &unix.Stat_t{}
if err := unixStat(chip.Path(), stat); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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 {

"golang.org/x/sys/unix"
)

type GPIOChardev interface {
Copy link
Contributor

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.

Copy link
Contributor Author

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?

Copy link
Contributor Author

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?

Copy link
Contributor

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)?

@ZeyadYasser ZeyadYasser requested review from bboozzoo and zyga March 19, 2025 13:54
@zyga
Copy link
Contributor

zyga commented Mar 19, 2025

I will park further reviews until the base branch is merged.

Copy link

github-actions bot commented Mar 19, 2025

Thu Apr 17 09:23:30 UTC 2025
The following results are from: https://github.com/canonical/snapd/actions/runs/14489069391

No spread failures reported

@zyga
Copy link
Contributor

zyga commented Mar 27, 2025

@ZeyadYasser this needs a rebase

@ZeyadYasser ZeyadYasser force-pushed the gpiod-helper-commands branch from 8b9d45d to acd4ae0 Compare March 27, 2025 14:54
Copy link
Collaborator

@pedronis pedronis Mar 27, 2025

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?

Copy link
Contributor

@zyga zyga left a 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 of file.Fd
  • I'd strongly suggest to use contexts around snap-gpio-helper so that interrupting the process behaves correctly.

@ZeyadYasser ZeyadYasser requested review from zyga and pedronis April 4, 2025 13:43
Copy link
Collaborator

@pedronis pedronis left a 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

}

func removeEphermalUdevTaggingRule(gadget, slot string) error {
// XXX: is rule reload/trigger nessacary
Copy link
Collaborator

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?

Copy link
Contributor Author

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

if err := os.MkdirAll(filepath.Dir(devPath), 0755); err != nil {
return err
}
if err := unixMknod(devPath, uint32(stat.Mode), int(stat.Rdev)); err != nil {
Copy link
Collaborator

@pedronis pedronis Apr 7, 2025

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

Copy link
Contributor

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.

Copy link
Collaborator

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

Copy link
Contributor Author

@ZeyadYasser ZeyadYasser Apr 10, 2025

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.

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.

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

return filepath.Join(filepath.Join(dirs.GlobalRootDir, ephermalUdevRulesDir), fname)
}

func addEphermalUdevTaggingRule(ctx context.Context, chip *ChardevChip, instanceName, slotName string) error {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo Ephemeral

Copy link
Contributor

@zyga zyga left a 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!

if err := os.MkdirAll(filepath.Dir(devPath), 0755); err != nil {
return err
}
if err := unixMknod(devPath, uint32(stat.Mode), int(stat.Rdev)); err != nil {
Copy link
Contributor

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 ZeyadYasser requested review from bboozzoo and pedronis April 9, 2025 15:32
@pedronis pedronis removed their request for review April 9, 2025 16:24
@pedronis
Copy link
Collaborator

pedronis commented Apr 9, 2025

@ZeyadYasser please work with @bboozzoo and @zyga to land this, no major objections from me at this point

@ZeyadYasser ZeyadYasser requested a review from zyga April 10, 2025 19:51
@ZeyadYasser ZeyadYasser force-pushed the gpiod-helper-commands branch 2 times, most recently from 30f8d3c to 5cfcd17 Compare April 10, 2025 19:58
Copy link
Contributor

@zyga zyga left a 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).

@ZeyadYasser ZeyadYasser requested review from bboozzoo and zyga April 15, 2025 11:26
Copy link
Contributor

@zyga zyga left a 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.

}

// This has to match the memory layout of `struct gpiochip_info` found
// in /include/uapi/linux/gpio.h in the kernel.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// in /include/uapi/linux/gpio.h in the kernel.
// in /usr/include/uapi/linux/gpio.h in the kernel.

Copy link
Contributor

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

stat, ok := fi.Sys().(*syscall.Stat_t)
if !ok {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if !ok {
if !ok || stat == nil {

var commaSeparated strings.Builder
size := len(r)
for i, span := range r {
commaSeparated.WriteString(span.String())
Copy link
Contributor

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

Copy link
Contributor

@bboozzoo bboozzoo left a 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"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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

Comment on lines 25 to 27
main "github.com/snapcore/snapd/cmd/snap-gpio-helper"
"github.com/snapcore/snapd/strutil"
. "gopkg.in/check.v1"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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"

}

// This has to match the memory layout of `struct gpiochip_info` found
// in /include/uapi/linux/gpio.h in the kernel.
Copy link
Contributor

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

return false, err
}
stat, ok := finfo.Sys().(*syscall.Stat_t)
if !ok {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here too

@ZeyadYasser ZeyadYasser force-pushed the gpiod-helper-commands branch from f1a3fa2 to 0d11203 Compare April 16, 2025 09:12
@ZeyadYasser ZeyadYasser merged commit 3548b47 into canonical:master Apr 17, 2025
76 of 77 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs Samuele review Needs a review from Samuele before it can land
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants