[RFC] Add Sysctl interface support for OSX/Linux/FreeBSD#4352
[RFC] Add Sysctl interface support for OSX/Linux/FreeBSD#4352elthariel wants to merge 3 commits into
Conversation
Signed-off-by: Julien 'Lta' BALLET <contact@lta.io>
| @@ -0,0 +1,87 @@ | |||
| lib LibC | |||
There was a problem hiding this comment.
This lib declaration IMO should be extracted to a separate file for each architecture (src/lib_c/<arch>/c/sysctl.cr).
There was a problem hiding this comment.
I thought about it, but it would be to a lot of duplication, as the implementation would be the same for osx/freebsd and all the linux variants.
Also it was implemented this way in src/process/executable_path.cr
Should I still move it there ?
There was a problem hiding this comment.
Sorry for the misinformation! as this is in lib_c you don't need the skip macro.
There was a problem hiding this comment.
Shall I move this in the arch-dependant folders ?
There was a problem hiding this comment.
Yes. The bindings in those folders should actually be automatically generated from the .h files on each platform, but I never got the generator to work. @ysbaddaden?
There was a problem hiding this comment.
That's fun. I hacked something similar for OpenGL
There was a problem hiding this comment.
Anyway, the variants of sysctl (sysctlbyname) I'm using isn't really POSIX compliant. Plus this syscall is marked as deprecated on Linux and is not available on linux libc. It doesn't look like it's going to play nicely with that script ?
There was a problem hiding this comment.
I second that anything LibC should be in the arch specific under src/lib_c with file namings following the actual C headers; thus way we may be capable in the future to generate these on-the-flt.
We prefer POSIX, but don't enforce it, if it requires some specific but public API for a platform, or the function is so common across platforms that it can be relied on, you can use it.
That being said, if something is deprecated please don't use it; it will likely break some day.
There was a problem hiding this comment.
@ysbaddaden, The syscall is deprecated on Linux but it's not available through the libc anymore, so no risks there. So your suggestion is to write those by hands until it is supported by the generator, am I right ?
| path = name_to_path name | ||
| read_proc_sys(path) | ||
| {% else %} | ||
| raise NotImplemented("Sysctl", "is not supported on this platform") |
| path = name_to_path name | ||
| read_proc_sys(path).to_i32 | ||
| {% else %} | ||
| raise NotImplemented("Sysctl", "is not supported on this platform") |
| # | ||
| class NotImplemented < Exception | ||
| def initialize(what : String, reason = "is not implemented") | ||
| super [what, reason].join(" ") |
There was a problem hiding this comment.
Use Tuple instead of an Array: {what, reason}.join(' ')
| if File.readable?(path) | ||
| File.read(path) | ||
| else | ||
| raise Errno.new("Unable to read #{path}", Errno::ENOENT) |
There was a problem hiding this comment.
Shouldn't this be just Exception?
There was a problem hiding this comment.
We might use the standard exception here but it would be inconsistent with the other system's implementation where we probably want to libc errno to be reported to the user.
| if res == 0 | ||
| String.new(value, value_len.value - 1, 1) | ||
| else | ||
| raise Errno.new("Unable to fetch sysctl value #{name}") |
There was a problem hiding this comment.
Shouldn't this be just Exception?
There was a problem hiding this comment.
sysctl sets the errno in case of error, so I think we should propagate it.
There was a problem hiding this comment.
As an aside: I think Errno is terrible design since it exposes platform-specifics at a high level, and on a more practical level you can't rescue only a single errno without using if inside rescue, however i'm too lazy to make an RFC
There was a problem hiding this comment.
IMHO, one of the point of Crystal is to allow system programming as well, which requires exposing platform specific details. That being said, I understand this should be allowed, not forced.
| # Sysctl.get_int("hw.ncpu") # => 8 | ||
| # ``` | ||
| # | ||
| def get_i32(name : String) : Int32 |
There was a problem hiding this comment.
I'd drop get_ prefix for consistency with Ruby/Crystal API expectations (namely no accessor - get_, set_, is_ - prefixes).
There was a problem hiding this comment.
What would you name setters? I don't see a good alternative here.
There was a problem hiding this comment.
makes perfect sense
There was a problem hiding this comment.
# getter
def i32(name : String) : Int32; end
# setter
def i32(name : String, value : Int32) : Void; endThere was a problem hiding this comment.
@Sija Is there any precedence for that naming in the stdlib? I think it's terrible.
There was a problem hiding this comment.
I've seen API like that in kemal-session shard for instance.
There was a problem hiding this comment.
I've seen and done that in Ruby (and python fwiw) as well
| # Sysctl.get_str("kernel.random.boot_id", 42) # => "1c629298-a60b-48df-bba1-9ea77f6b37ba" | ||
| # ``` | ||
| # | ||
| def get_str(name : String, max_size : Int32) : String |
| # on a certain system or platform | ||
| # | ||
| # ``` | ||
| # Sysctl.get_int("net.ipv4.forward") # Sysctl doesn't exist on Windows |
There was a problem hiding this comment.
Typically we use # raises ExceptionType (message) when documenting raising in examples (ref)
| @@ -0,0 +1,87 @@ | |||
| lib LibC | |||
| # Sysctl.get_int("hw.ncpu") # => 8 | ||
| # ``` | ||
| # | ||
| def get_i32(name : String) : Int32 |
There was a problem hiding this comment.
What would you name setters? I don't see a good alternative here.
|
Thanks to both of you for the quick and insightful review |
| {% elsif flag?(:linux) %} | ||
| ["net.ipv4.ip_forward", "kernel.pty.max"] | ||
| {% else %} | ||
| [] |
There was a problem hiding this comment.
Should be [] of String (see failed travis build).
Signed-off-by: Julien 'Lta' BALLET <contact@lta.io>
ysbaddaden
left a comment
There was a problem hiding this comment.
I'm not sure this fits into the stdlib, mostly because this is platform specific. We may eventually make a few platform specific syscalls to extract some values (like the number of CPUs), but that would be internal to the corelib, not exposed: getting the number of available CPU on any platform is a great abstraction, raw access to sysctl isn't.
I think this would fit better as a Shard.
| def initialize(what : String, reason = "is not implemented") | ||
| super {what, reason}.join(" ") | ||
| end | ||
| end |
There was a problem hiding this comment.
No, this should fail to compile, not fail at runtime. One solution is to not expose the Sysctl type on unsupported platforms.
There was a problem hiding this comment.
Shall we keep the NotImplemented exception ? (like ruby & python for example), should I move it to a separate PR ? or should I get rid of it ?
There was a problem hiding this comment.
No. It may be meaningful in interpreted languages, not in a compiled language: not implemented => compilation failure.
There was a problem hiding this comment.
Duh. Now you're pointing at it...
Would only be useful with dlopen & friends. I'll get rid of it tomorrow
|
@ysbaddaden, Even if it Linux deprecated it, That being said, I might be mistaken about it, and maybe it's better to do occasional on-the-spot wrappings like here: https://github.com/crystal-lang/crystal/blob/master/src/process/executable_path.cr#L74 It's up to you and the community. What would be the process to reach a verdict here ? |
Signed-off-by: Julien 'Lta' BALLET <contact@lta.io>
|
Pointers are required to bind to C libraries, they speed up implementations, both internally (for core/stdlib) and externally (for shards, apps); and without them we wouldn't achieve much, if anything. On the other hand, |
|
Hum... This is embarrassing. Just confirmed with the Single Unix Specification v4, @ysbaddaden, To get back on the initial topic, I think |
|
Do you have more examples and use cases that would benefit everyone? I mean other than the number of available CPU? |
|
I haven't really thought about it. That's an API I use from times on times with C and since I needed it for the number of cpu on OSX, I thought it would be a useful contribution. I'll have a look and think a bit about it if it can gives input for the community to decide whether it is or not |
|
I should precise that I'm more familiar with the Linux system APIs, which aren't based out of sysctl anymore, outside of a few idiomatic things you'll find here and here. On the OSX side of things, it seems like a good way to get access to a bunch of system data about shared memory, VM, networking and hardware. I'm no OSX experts, so there might be better way to get access to this. We might also need input from OpenBSD and FreeBSD guys, but I think that might be the only way to access a whole bunch of similar system data. I think FreeBSD started to move to something similar to procfs, but I don't think OpenBSD was on that path (although I must confess the last time I worked with awesome openbsd was quite a while ago). I certainly don't want to push on this topic, as you made pretty good points as of how this wouldn't be the most widely useful API. On the other hand, we'll need at least some portion of it internally so why not expose it ? TBH, I don't have anywhere near enough vision on Crystal's community and it's goals to have a strong opinion. You'll have to place the threshold :) |
|
As @ysbaddaden said, this seems a very good fit for a separate shard:
So, I'd put it in a shard, and if the ecosystem embraces it and it becomes a very common item in |
|
@mverzilli So be it. I'm not sure as I'm going to effectively create the shard as having it out of stdlib defeats the initial purpose of it. |
Hi,
This is an attempt at defining an interface to the
sysctlAPI of the various UNIXes. OpenBSD is not yet supported as they don't have an easy way to get a sysctl by name (that I'm aware of). We could implement it by spawning asysctlprocess or define a static map somewhere.The underlying goal of this is to have a way to query the number of CPUs on OSX to provide a better default for the number of default threads on the multithread support branch(es). In addition to that we could use it for
System.hostname, and I think this would be useful for many folks out there.This has only been tested on OSX yet. I'll soon test it on Linux but I'll need help for FreeBSD. Any comment on this would be greatly appreciated, especially regarding general usefulness, design and method naming.
Unicornly yours,
Lta.
Signed-off-by: Julien 'Lta' BALLET contact@lta.io