Tobias Stoeckmann wrote:
>
> And I still think that the current code is a bit too permissive in parsing
> things. I mean what's the point in allowing lines like:
>
> sometextwithoutspace/bin/ksh should be used for logins # seriously!
>
> Which would result in /bin/ksh, by the way.
>
> Didn't notice the consequences that arise by keeping the descriptor open,
> so I'm fine with an alternative approach. Yet we might make the code a
> bit easier to review by not allowing such weird lines. What it should
> expect and enforce:
>
> - a valid line has to start with a slash
> - comments are chopped off
> - comments are supposed to be at the beginning of a line
>
> So if somebody writes "/bin/ksh # comment", that actually leads to "/bin/ksh
> ",
> with an additional whitespace at the end. Currently we couldn't even specify a
> shell with a whitespace in its path.
ok. i was going to leave the behavior alone, but we can fix that too.
- use getline to read lines of any length.
- only consider lines that start with a /.
- truncate lines after a #, but not after spaces.
Index: gen/getusershell.c
===================================================================
RCS file: /cvs/src/lib/libc/gen/getusershell.c,v
retrieving revision 1.16
diff -u -p -r1.16 getusershell.c
--- gen/getusershell.c 14 Sep 2015 16:09:13 -0000 1.16
+++ gen/getusershell.c 5 Dec 2015 18:24:33 -0000
@@ -28,14 +28,13 @@
* SUCH DAMAGE.
*/
-#include <sys/stat.h>
-
#include <ctype.h>
#include <limits.h>
#include <paths.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
/*
@@ -44,7 +43,7 @@
*/
static char *okshells[] = { _PATH_BSHELL, _PATH_CSHELL, _PATH_KSHELL, NULL };
-static char **curshell, **shells, *strings;
+static char **curshell, **shells;
static char **initshells(void);
/*
@@ -66,11 +65,14 @@ getusershell(void)
void
endusershell(void)
{
-
+ char **s;
+
+ if ((s = shells))
+ while (*s)
+ free(*s++);
free(shells);
shells = NULL;
- free(strings);
- strings = NULL;
+
curshell = NULL;
}
@@ -84,48 +86,50 @@ setusershell(void)
static char **
initshells(void)
{
- char **sp, *cp;
+ size_t nshells, nalloc, linesize;
+ char *line;
FILE *fp;
- struct stat statb;
free(shells);
shells = NULL;
- free(strings);
- strings = NULL;
+
if ((fp = fopen(_PATH_SHELLS, "re")) == NULL)
return (okshells);
- if (fstat(fileno(fp), &statb) == -1) {
- (void)fclose(fp);
- return (okshells);
- }
- if (statb.st_size > SIZE_MAX) {
- (void)fclose(fp);
- return (okshells);
- }
- if ((strings = malloc((size_t)statb.st_size)) == NULL) {
- (void)fclose(fp);
- return (okshells);
- }
- shells = calloc((size_t)(statb.st_size / 3 + 2), sizeof (char *));
- if (shells == NULL) {
- (void)fclose(fp);
- free(strings);
- strings = NULL;
- return (okshells);
- }
- sp = shells;
- cp = strings;
- while (fgets(cp, PATH_MAX + 1, fp) != NULL) {
- while (*cp != '#' && *cp != '/' && *cp != '\0')
- cp++;
- if (*cp == '#' || *cp == '\0')
+
+ line = NULL;
+ nalloc = 10; // just an initial guess
+ nshells = 0;
+ shells = reallocarray(NULL, nalloc, sizeof (char *));
+ if (shells == NULL)
+ goto fail;
+ linesize = 0;
+ while (getline(&line, &linesize, fp) != -1) {
+ if (*line != '/')
continue;
- *sp++ = cp;
- while (!isspace((unsigned char)*cp) && *cp != '#' && *cp !=
'\0')
- cp++;
- *cp++ = '\0';
+ line[strcspn(line, "#\n")] = '\0';
+ if (!(shells[nshells] = strdup(line)))
+ goto fail;
+
+ nshells++;
+ if (nshells == nalloc) {
+ char **new = reallocarray(shells, nalloc * 2,
sizeof(char *));
+ if (!new)
+ goto fail;
+ shells = new;
+ nalloc *= 2;
+ }
}
- *sp = NULL;
+ free(line);
+ shells[nshells] = NULL;
(void)fclose(fp);
return (shells);
+
+fail:
+ free(line);
+ while (nshells)
+ free(shells[nshells--]);
+ free(shells);
+ shells = NULL;
+ (void)fclose(fp);
+ return (okshells);
}