Skip to content

Conversation

mat-hek
Copy link
Contributor

@mat-hek mat-hek commented Aug 20, 2025

These changes are made under both the "Apache 2.0" and the "GNU Lesser General
Public License 2.1 or later" license terms (dual license).

SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later

@pguyot
Copy link
Collaborator

pguyot commented Aug 20, 2025

POSIX says that getenv is not thread safe. You should hold global->env_spinlock. See nif_erlang_localtime.

@mat-hek
Copy link
Contributor Author

mat-hek commented Aug 21, 2025

Done ✅


if (IS_NULL_PTR(env_var_value_tmp)) {
result = FALSE_ATOM;
goto nif_os_getenv_1_unlock_return;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we'd rather duplicate the spin unlock and have a return than a goto. At least it's the most common pattern here. See AVMCCS-L011 in c coding style guide.

goto nif_os_getenv_1_unlock_return;
}

strcpy(env_var_value, env_var_value_tmp);
Copy link
Collaborator

Choose a reason for hiding this comment

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

This looks like strdup(3) to me, even if you use the strlen later on. You could just strdup, release the spinlock and then test for memory error. If you want to strlen first, you can strndup with len + 1.

true =
case atomvm:platform() of
generic_unix ->
is_list(os:getenv("PATH"));
Copy link
Collaborator

Choose a reason for hiding this comment

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

You could test that it starts with / or contains / which probably is always true.

int ok;
const char *env_var = interop_list_to_utf8_string(env_var_list, &ok);
if (UNLIKELY(!ok)) {
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
Copy link
Collaborator

Choose a reason for hiding this comment

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

We usually raise badarg here instead of out of memory even if both reasons would yield 0.

VALIDATE_VALUE(env_var_list, term_is_list);

int ok;
const char *env_var = interop_list_to_utf8_string(env_var_list, &ok);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not sure env_var is freed.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not sure env_var is freed.

Where is it freed?

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've mistaken it with env_var_value, which also wasn't always freed 🫠 Now both should be fixed

_ ->
true
end,
false = os:getenv("NON_EXISTENT_PATH_VAR"),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Since you do utf8, you could try a codepoint > 255.

Also what happens if string is empty or improper?

%% @doc Get an environment variable value if defined
%% @end
%%-----------------------------------------------------------------------------
-spec getenv(Name :: string()) -> string() | false.
Copy link
Collaborator

Choose a reason for hiding this comment

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

OTP uses env_var_name() and env_var_value() that are nonempty_string().

@mat-hek mat-hek force-pushed the mf/os-getenv branch 3 times, most recently from 06be48c to 7e201ca Compare August 25, 2025 09:37
@mat-hek mat-hek requested a review from pguyot August 25, 2025 09:39

test_os_getenv() ->
ok =
case atomvm:platform() of
Copy link
Collaborator

Choose a reason for hiding this comment

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

This test is executed by BEAM to assert compatibility, you need erlang:system_info(machine).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

AFAIR this is to check if we can expect the PATH env variable to be there. Not sure how erlang:system_info(machine) would help, since it seems to always return the string "ATOM" 🤔

Copy link
Collaborator

Choose a reason for hiding this comment

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

You need to check erlang:system_info(machine) returns "ATOM" before calling atomvm:platform(). On the BEAM: "BEAM" and os:type() can be used.
Anyway tests must be runnable on the BEAM, so this code must be changed.

Copy link
Collaborator

@bettio bettio left a comment

Choose a reason for hiding this comment

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

Fairly good, only minor changes are required.


test_os_getenv() ->
ok =
case atomvm:platform() of
Copy link
Collaborator

Choose a reason for hiding this comment

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

You need to check erlang:system_info(machine) returns "ATOM" before calling atomvm:platform(). On the BEAM: "BEAM" and os:type() can be used.
Anyway tests must be runnable on the BEAM, so this code must be changed.

smp_spinlock_lock(&ctx->global->env_spinlock);
#endif

char *env_var_value = getenv(env_var);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I suggest using const char *.

return FALSE_ATOM;
}

env_var_value = strdup(env_var_value);
Copy link
Collaborator

@bettio bettio Sep 5, 2025

Choose a reason for hiding this comment

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

I suggest doing: char *env_var_value_copy = strdup(env_var_value); instead of reassigning.

@mat-hek mat-hek force-pushed the mf/os-getenv branch 3 times, most recently from 1930be4 to 5d6238f Compare September 11, 2025 16:24
@mat-hek mat-hek requested a review from bettio September 11, 2025 18:51
Copy link
Collaborator

@bettio bettio left a comment

Choose a reason for hiding this comment

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

Just a few minor notes.
By the way: UNLIKELY(IS_NULL_PTR(x)) is redundant and can be simplified to IS_NULL_PTR(x)

smp_spinlock_unlock(&ctx->global->env_spinlock);
#endif

if (UNLIKELY(IS_NULL_PTR(env_var_value))) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

One last thing: UNLIKELY(IS_NULL_PTR(x)) is redundant: IS_NULL_PTR is a shorthand for UNLIKELY(x == 0).

@@ -229,657 +235,549 @@ DECLARE_MATH_NIF_FUN(sqrt)
DECLARE_MATH_NIF_FUN(tan)
DECLARE_MATH_NIF_FUN(tanh)

static const struct Nif binary_at_nif =
{
static const struct Nif binary_at_nif = {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would avoid to merge this with all this unrelated noise, still I understand the struggle. So I can start aligning nifs.c more to our .clang-format. git rebase should handle this for you.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, it was unintentional, VS Code doesn't respect clang-ignore for some reason. Yet formatting nifs.c (and opcodesswitch.h :P) would be much appreciated ;)

Copy link
Collaborator

@bettio bettio left a comment

Choose a reason for hiding this comment

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

Everything looks good now. Let's rebase and squash commits together, after that I will merge it.

Signed-off-by: Mateusz Front <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants