Skip to content
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

proposal: os: add a function that would tell us about the operating system's version #54951

Closed
iMacker2020 opened this issue Sep 8, 2022 · 13 comments

Comments

@iMacker2020
Copy link

iMacker2020 commented Sep 8, 2022

Currently there is no standard way of determining the operating system version in Go. Therefore Go should have a method called Version() added to the os package, that returns information about the operating system a Go program is running on. This method would be useful for scenarios such as crash reporting and library loading.

The os.Version() method would return a structure that is defined like this:

type OSInfo struct {
  Name string
  Distributor string
  Major string
  Minor string
  Patch string
  Miscellaneous string
}

Name would be set to the name of the operating system.
Distributor is the name of the company that makes that operating system.
Major is the major version of this operating system.
Minor is the minor version of the operating system.
Patch is the patch level of the operating system.
Miscellaneous is reserved for special situations that might require its use.

When a program calls the os.Version() method a variable of type OSInfo is returned with all the fields of this structure being set to appropriate values. The program can then determine the operating system's version using the fields provided.

One scenario this method would be useful is for crash reporting. If a program crashes or experiences an error that requires information to be sent to the developer, the os.Version() method would make gathering important information very easy and cross platform. The developer would only have to call this method once and the exact version of the operating system, name and other important information would be completely accessible.

Another scenario this method would be useful for is supporting multiple versions of an operating system. If an API is missing in an early version of the operating system and used by the program, there would be no need to discontinue supporting the older version. The os.Version() method would tell the program exactly which version of the operating system it is running on, then the program can load a library that contains an implementation of the needed API.

Why not use an already existing operating system functions to determine the version? I am sure each operating system has a way to determine its version already. How are we to become a better and more platform independent language if we think like that? A rich standard library means more portability and convenience to the programmer. Having one method that works on all supported operating systems mean saving time researching a solution for each operating system separately.

The os.Version() method would be a useful and convenient method to use. It would provide the program with very detailed information about its environment. This information would lead to the creation of better programs for everyone.

@gopherbot gopherbot added this to the Proposal milestone Sep 8, 2022
@ianlancetaylor ianlancetaylor moved this to Incoming in Proposals Sep 8, 2022
@ianlancetaylor
Copy link
Member

  1. Note that the fields must be capitalized or they are useless, and that by convention we don't use underscores in names in Go (https://go.dev/doc/effective_go#mixed-caps).
  2. What is the difference between the name field and runtime.GOOS?
  3. I don't know what distributor means for a free operating system.
  4. I don't know what the major and minor versions are for Windows.

@iMacker2020
Copy link
Author

iMacker2020 commented Sep 8, 2022

Ok. That will be fixed. I think removing "_version" from each field should do.
Update: It is done.

  • What is the difference between the name field and runtime.GOOS?

I would assume they return the same value.
EDIT: Ok now that I think about it maybe we should do better and return the operating system's common name.
Example: instead of "Darwin" we return "Mac OS", "iPadOS", "iOS", or "watchOS".

  • I don't know what distributor means for a free operating system.

It would mean RedHat, Canonical/Ubuntu, Fedora, Arch, ... so the name of the distribution.

  • I don't know what the major and minor versions are for Windows.

https://en.wikipedia.org/wiki/List_of_Microsoft_Windows_versions
If you look at this page you will see a table with a "Version" column. The value in his column is what would be returned. For 32-bit Windows XP the major version would be 5 and the minor version would be 1 (Windows NT 5.1). Since Windows 10 Microsoft has done questionable things with their versioning, I think the easy thing to do is to make our own table that corresponds to Windows version after 10.

Windows 10's major version could be 10 and its minor version would be its version. So Windows 10 version 1809 would have a minor version of 1809. The patch level could be set to the build version.

Windows 11 major version could be 11 and its minor version be the version. So Windows 11, version 22H2, build number 22621, would have these values:
Major = 11
Minor = 22H2
Patch = 22621
Miscellaneous = "Professional" or "Home"

I do realize that major, minor, and patch types should be changed to string.
EDIT: changed the named and the types.

@julieqiu julieqiu removed this from Go Security Sep 8, 2022
@apparentlymart
Copy link

I think it could help to expand on exactly how the implementation would decide a value for each of these fields on each of Go's supported platforms. The discussion so far seems to be about whether it will be possible to produce a reliable and normalized set of information across all platforms, and I think the best way to answer that question would be to try to show exactly how it would work, and note any situations where a particular platform doesn't seem to have a reasonable answer to one of these.

As an example of what I mean, I'll describe a hypothetical way to implement this API on Linux in particular. I'm not intending to recommend this approach, only to describe what might be possible. I'd then invite the original proposer or anyone else interested to try a similar exercise for another platform, and perhaps to make counter-proposals for other ways the Linux port could implement this which might produce a more useful answer.


The Linux Standard Base includes a proposal for a standardized representation of a specific "operating system release". There is a currently-citation-less statement in the Wikipedia article that this standard has not been updated since 2015 but that the lsb_release command (which is an implementation of the release description model) is "sometimes still available".

On my current Ubuntu system, lsb_release -a returns the following information:

$ lsb_release -a
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.5 LTS
Release:        20.04
Codename:       focal

From the above I suppose we might populate an OSInfo data as follows:

OSInfo{
    Name: "Ubuntu", // "Distributor ID"
    Distributor: "Ubuntu", // "Distributor ID"
    Major: "20", // Parsed from "Release"
    Minor: "04", // Parsed from "Release"
    Patch: "", // Not machine-readable
    Miscellaneous: "", // Unclear what would belong in this field
}

This already seems to raise some questions:

  • Is the "Release" field guaranteed to be a some sequences of digits interspersed with dots? Is it reasonable to consider them to be major, minor and patch release numbers? I can't find any actual specification for what a "release number" is, and so it may just be coincidence that Ubuntu's implementation happens to have a pair of numbers that could be described as "Major" and "Minor" release numbers.

    For Ubuntu in particular, I happen to know that "20" represents the year 2020 and "04" represents the month of April. Is it reasonable to consider a year to be a major version and a month to be a minor version? In practice, I believe that Ubuntu considers "20.04" as a whole to be the major release number and then ".5" is a patch release (but we can only see that in the human-readable "Description" string that isn't suitable for machine interpretation). I don't think that interpretation generalizes to other Linux-based operating systems, however.

  • The distinction between Name and Distributor is unclear here, so I just put the "Distributor ID" in both fields. I suppose it could be argued that Name could be "Linux" instead -- hard-coded into the Linux port of Go -- but that raises the question of whether we're describing the operating system as a whole or the kernel it's running on.

  • Overall, it isn't clear to me that the information derived this way is structured enough to make programmatic decisions. For example, I'm not sure how I could use this to make a decision between a set of libraries to use because I cannot predict how future versions of each operating system or entirely new operating systems would appear in this data structure, and so any logic I write would risk being forward-incompatible.

    This could in principle meet your other use-case of just including the OS description in your system's logs as additional diagnostic information, but I think the same need could be met by just writing out the "Description" string from lsb_release without any further interpretation, because human readers can then read all of the same information from that string.

    If generating a human-readable description of the operating system is the key requirement here then it seems to me that just a free-form string would be sufficient, and that applications should only pass it verbatim to a human for interpretation and not make any direct decisions based on it.

@seankhliao
Copy link
Member

Looking at how existing packages try to do it, a common theme appears to be the need to maintain large tables that still only support major distributions.
This sort of fuzzy matching with data tables that can easily go out of date doesn't feel like it would be suitable for the standard library.

@rittneje
Copy link

Do these meet your needs?

https://pkg.go.dev/golang.org/x/sys/unix#Uname
https://pkg.go.dev/golang.org/x/sys/windows#RtlGetVersion

Obviously they are not OS-agnostic but might be good enough.

@brunexgeek
Copy link

I suggest the version be a single string, so it can accommodate any versioning scheme (not just semantic versioning). It's better than trying to make it fit. Also, I'd suggest to remove the miscellaneous field, it's too generic to be useful.

@iMacker2020
Copy link
Author

@apparentlymart I will agree that the state that Linux is in makes it very hard to work with. There are kernel versions, distributions, and a lot of independent libraries that may or may not come with a certain distribution. It is not something I want to help support. This proposal would work very well with Windows and Mac OS...and a lot of other great operating systems.

The distinction between Name and Distributor is unclear here, so I just put the "Distributor ID" in both fields. I suppose it could be argued that Name could be "Linux" instead -- hard-coded into the Linux port of Go -- but that raises the question of whether we're describing the operating system as a whole or the kernel it's running on.

I think the Name field on Linux should be set to "Linux". I think that would be easier for the user to work with. I also believe we are describing the operating system as a whole.

Yes there are operating system specific functions available to give us the operating system version, but I still believe that providing a single function for all operating systems would make querying operating systems for info much easier.

@iMacker2020
Copy link
Author

@rittneje Thank you for the suggestions. They could be useful for future projects.

@iMacker2020
Copy link
Author

@seankhliao yes tables can go out of date, but they can also be updated.

@iMacker2020
Copy link
Author

I suggest the version be a single string, so it can accommodate any versioning scheme (not just semantic versioning). It's better than trying to make it fit. Also, I'd suggest to remove the miscellaneous field, it's too generic to be useful.

My original idea was just that. Problem was the user would have to parse a string and hope it stays in the same format between operating system versions. Using a structure is more precise.

The Miscellaneous field could be useful. On Windows it could store the edition. So it could report "Professional" or "Home". On Linux maybe it could report the version of libc that is being used.

@iMacker2020
Copy link
Author

Well it looks like my proposal is not going to be approved. Linux seems to be the major concern. If I had to come up with a system that accommodated Linux, there would have to be a way to query for a lot more information than just operating system version. The system would have to get information like operating system version, kernel version, library availability and version, distributor, architecture, cpu name, cpu speed, core count, system memory, and probably more. Maybe a system package with a query() method that takes a string and returns a string. The input value probably being a constant like "CORE_COUNT", and the output value being what you wanted to know about.

I'm not sure if I should make a proposal for something like this. Let me know what you think. Thank you.

@iMacker2020 iMacker2020 closed this as not planned Won't fix, can't repro, duplicate, stale Sep 18, 2022
@seankhliao
Copy link
Member

Most likely such functions should be implemented outside of the standard library first, ref: https://go.dev/doc/faq#x_in_std , and only brought in once they have demonstrated stability, accuracy, and good ergonomics

@rittneje
Copy link

rittneje commented Sep 18, 2022

@iMacker2020 I feel like the two use cases you outlined demonstrate that a common struct is not that useful anyway.

If you are just looking for some opaque data you can log, then there is no need at all for a common struct. Just a string (or any or some such thing) will do.

If you are actually going to make OS-dependent decisions about the information, then having a common struct does not accomplish much. For example, if you are making decisions about the Linux version, then you must already know you are working with Linux, so using a Linux-specific mechanism to get the version doesn't sound like an issue.

It should also be noted that ultimately the OS maintainers will do whatever they want as far as versioning goes. Thus any attempt to commonize beyond an opaque string is unlikely to work well.

All in all, this seems like a good candidate for x/sys (which as I mentioned already supports it for Unix and Windows), but maybe not the standard library itself.

@golang golang locked and limited conversation to collaborators Sep 19, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

7 participants