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

Implement User/Group from_name?/from_id? like Ruby #10182

Merged
merged 2 commits into from
Jan 11, 2021

Conversation

wonderix
Copy link
Contributor

@wonderix wonderix commented Jan 3, 2021

Ruby handles ENOENT, ESRCH, EBADF and EPERM as not found when calling getgrgid_r, getgrnam_r, getpwnam_r or getpwuid_r.

I implemented the same behavior for crystal. Unfortunately, it's not possible to write unit tests for this.

Fixes #8069.

@straight-shoota straight-shoota added kind:bug A bug in the code. Does not apply to documentation, specs, etc. topic:stdlib:system labels Jan 3, 2021
@straight-shoota straight-shoota linked an issue Jan 3, 2021 that may be closed by this pull request
@straight-shoota
Copy link
Member

Thank you for your recent contributions! It's very appreciated.

FYI: If you write Resolves #8069 or Fixes #8069 in the PR description, Github automatically links the issue to this PR and closes it on merge.

@j8r
Copy link
Contributor

j8r commented Jan 3, 2021

Note that your commit isn't attributed to your GitHub account. If you set locally a Git mail, which is also linked to your GH account, it will be.

@wonderix
Copy link
Contributor Author

wonderix commented Jan 3, 2021

@j8r, I added my second email address to my github account. Hopefully, this does the job.

buf = Bytes.new(buf.size * 2)
else
raise RuntimeError.from_errno("getpwuid_r")
end
Copy link
Member

Choose a reason for hiding this comment

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

The code for all four methods looks very similar now, except for a few differences like what's the function to call and the name to use for from_errno. Could we extract them to a separate method that yields? That way all that logic will be in a single place. Or maybe not a method that yields, I'm sure there's a more DRY way to do this.

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 created a macro to wrap the functions.
I also tried putting it in a method that yields. But that was too complicated.

Copy link
Member

@asterite asterite left a comment

Choose a reason for hiding this comment

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

Please refactor to avoid duplicate code.

@straight-shoota
Copy link
Member

I would suggest a yielding method similar to System.retry_wstr_buffer:

def from_username?(username : String)
  username.check_no_null_byte
  pwd = uninitialized LibC::Passwd
  pwd_pointer = pointerof(pwd)
  retry_with_buffer("getpwnam_r", GETPW_R_SIZE_MAX) do |buf|
    LibC.getpwnam_r(username, pwd_pointer, buf, buf.size, pointerof(pwd_pointer))
  end
 
  from_struct(pwd) if pwd_pointer
end

def retry_with_buffer(function_name, max_buffer)
  initial_buf = uninitialized UInt8[1024]
  buf = initial_buf

  while (ret = yield buf.to_slice) != 0
    case ret
    when LibC::ENOENT, LibC::ESRCH, LibC::EBADF, LibC::EPERM
      return nil
    when LibC::ERANGE
      raise RuntimeError.from_errno(function_name) if buf.size >= max_buffer
      buf = Bytes.new(buf.size * 2)
    else
      raise RuntimeError.from_errno(function_name)
    end
  end
end

@asterite
Copy link
Member

asterite commented Jan 6, 2021

Yes, a yielding method is better. Macros should be avoided unless absolutely necessary.

@wonderix
Copy link
Contributor Author

wonderix commented Jan 6, 2021

Thanks for the hint. I implemented it now like suggested by @straight-shoota

@straight-shoota straight-shoota added this to the 1.0.0 milestone Jan 6, 2021
@bcardiff bcardiff merged commit a9832d7 into crystal-lang:master Jan 11, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind:bug A bug in the code. Does not apply to documentation, specs, etc. topic:stdlib:system
Projects
None yet
Development

Successfully merging this pull request may close these issues.

getpwuid_r may return ENOENT for "No such user"
5 participants