-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
[RFC] Add Sysctl interface support for OSX/Linux/FreeBSD #4352
Conversation
Signed-off-by: Julien 'Lta' BALLET <contact@lta.io>
@@ -0,0 +1,87 @@ | |||
lib LibC |
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 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See: #4237
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 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the misinformation! as this is in lib_c
you don't need the skip
macro.
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.
Shall I move this in the arch-dependant folders ?
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fun. I hacked something similar for OpenGL
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@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 ?
src/sysctl.cr
Outdated
path = name_to_path name | ||
read_proc_sys(path) | ||
{% else %} | ||
raise NotImplemented("Sysctl", "is not supported on this platform") |
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.
Missing .new
.
src/sysctl.cr
Outdated
path = name_to_path name | ||
read_proc_sys(path).to_i32 | ||
{% else %} | ||
raise NotImplemented("Sysctl", "is not supported on this platform") |
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.
Missing .new
.
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
src/exception.cr
Outdated
# | ||
class NotImplemented < Exception | ||
def initialize(what : String, reason = "is not implemented") | ||
super [what, reason].join(" ") |
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.
Use Tuple
instead of an Array
: {what, reason}.join(' ')
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.
Roger
if File.readable?(path) | ||
File.read(path) | ||
else | ||
raise Errno.new("Unable to read #{path}", Errno::ENOENT) |
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.
Shouldn't this be just Exception
?
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be just Exception
?
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.
sysctl
sets the errno
in case of error, so I think we should propagate it.
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
src/sysctl.cr
Outdated
# Sysctl.get_int("hw.ncpu") # => 8 | ||
# ``` | ||
# | ||
def get_i32(name : String) : Int32 |
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'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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would you name setters? I don't see a good alternative 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.
makes perfect sense
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.
# getter
def i32(name : String) : Int32; end
# setter
def i32(name : String, value : Int32) : Void; end
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.
Perfetto!
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.
@Sija Is there any precedence for that naming in the stdlib? I think it's terrible.
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've seen API like that in kemal-session shard for instance.
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've seen and done that in Ruby (and python fwiw) as well
src/sysctl.cr
Outdated
# Sysctl.get_str("kernel.random.boot_id", 42) # => "1c629298-a60b-48df-bba1-9ea77f6b37ba" | ||
# ``` | ||
# | ||
def get_str(name : String, max_size : Int32) : 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.
ditto
src/exception.cr
Outdated
# 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typically we use # raises ExceptionType (message)
when documenting raising in examples (ref)
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
@@ -0,0 +1,87 @@ | |||
lib LibC |
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.
See: #4237
src/sysctl.cr
Outdated
# Sysctl.get_int("hw.ncpu") # => 8 | ||
# ``` | ||
# | ||
def get_i32(name : String) : Int32 |
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.
What would you name setters? I don't see a good alternative here.
Thanks to both of you for the quick and insightful review |
spec/std/sysctl_spec.cr
Outdated
{% elsif flag?(:linux) %} | ||
["net.ipv4.ip_forward", "kernel.pty.max"] | ||
{% else %} | ||
[] |
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.
Should be [] of String
(see failed travis build).
Signed-off-by: Julien 'Lta' BALLET <contact@lta.io>
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'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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. It may be meaningful in interpreted languages, not in a compiled language: not implemented => compilation failure.
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.
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
sysctl
API 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 asysctl
process 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