@@ -938,6 +938,170 @@ static FnCallResult FnCallGetUsers(ARG_UNUSED EvalContext *ctx, ARG_UNUSED const
938
938
939
939
/*********************************************************************/
940
940
941
+
942
+ #if defined(HAVE_GETPWENT ) && !defined(__ANDROID__ )
943
+
944
+ static FnCallResult FnCallFindLocalUsers (EvalContext * ctx , ARG_UNUSED const Policy * policy , const FnCall * fp , const Rlist * finalargs )
945
+ {
946
+ assert (fp != NULL );
947
+ bool allocated = false;
948
+ JsonElement * json = VarNameOrInlineToJson (ctx , fp , finalargs , false, & allocated );
949
+
950
+ // we failed to produce a valid JsonElement, so give up
951
+ if (json == NULL )
952
+ {
953
+ Log (LOG_LEVEL_ERR , "Function '%s' couldn't parse argument '%s'" ,
954
+ fp -> name , RlistScalarValueSafe (finalargs ));
955
+ return FnFailure ();
956
+ }
957
+ else if (JsonGetElementType (json ) != JSON_ELEMENT_TYPE_CONTAINER )
958
+ {
959
+ Log (LOG_LEVEL_ERR , "Bad argument '%s' in function '%s': Expected data container or slist" ,
960
+ RlistScalarValueSafe (finalargs ), fp -> name );
961
+ JsonDestroyMaybe (json , allocated );
962
+ return FnFailure ();
963
+ }
964
+
965
+ JsonElement * parent = JsonObjectCreate (10 );
966
+ setpwent ();
967
+ struct passwd * pw ;
968
+ while ((pw = getpwent ()) != NULL )
969
+ {
970
+ JsonIterator iter = JsonIteratorInit (json );
971
+ bool can_add_to_json = true;
972
+ JsonElement * element = JsonIteratorNextValue (& iter );
973
+ while (element != NULL )
974
+ {
975
+ if (JsonGetElementType (element ) != JSON_ELEMENT_TYPE_PRIMITIVE )
976
+ {
977
+ Log (LOG_LEVEL_ERR , "Bad argument '%s' in function '%s': Filter cannot include nested data" ,
978
+ RlistScalarValueSafe (finalargs ), fp -> name );
979
+ JsonDestroyMaybe (json , allocated );
980
+ JsonDestroy (parent );
981
+ return FnFailure ();
982
+ }
983
+ const char * field = JsonPrimitiveGetAsString (element );
984
+ const Rlist * tuple = RlistFromSplitString (field , '=' );
985
+ assert (tuple != NULL );
986
+ const char * attribute = TrimWhitespace (RlistScalarValue (tuple ));
987
+
988
+ if (tuple -> next == NULL )
989
+ {
990
+ Log (LOG_LEVEL_ERR , "Invalid filter field '%s' in function '%s': Expected attributes and values to be separated with '='" ,
991
+ fp -> name , field );
992
+ JsonDestroyMaybe (json , allocated );
993
+ JsonDestroy (parent );
994
+ return FnFailure ();
995
+ }
996
+ const char * value = TrimWhitespace (RlistScalarValue (tuple -> next ));
997
+
998
+ if (StringEqual (attribute , "name" ))
999
+ {
1000
+ if (!StringMatchFull (value , pw -> pw_name ))
1001
+ {
1002
+ can_add_to_json = false;
1003
+ }
1004
+ }
1005
+ else if (StringEqual (attribute , "uid" ))
1006
+ {
1007
+ char uid_string [PRINTSIZE (pw -> pw_uid )];
1008
+ int ret = snprintf (uid_string , sizeof (uid_string ), "%u" , pw -> pw_uid );
1009
+
1010
+ if (ret < 0 )
1011
+ {
1012
+ Log (LOG_LEVEL_ERR , "Couldn't convert the uid of '%s' to string in function '%s'" ,
1013
+ pw -> pw_name , fp -> name );
1014
+ JsonDestroyMaybe (json , allocated );
1015
+ JsonDestroy (parent );
1016
+ return FnFailure ();
1017
+ }
1018
+ assert ((size_t ) ret < sizeof (uid_string ));
1019
+
1020
+ if (!StringMatchFull (value , uid_string ))
1021
+ {
1022
+ can_add_to_json = false;
1023
+ }
1024
+ }
1025
+ else if (StringEqual (attribute , "gid" ))
1026
+ {
1027
+ char gid_string [PRINTSIZE (pw -> pw_uid )];
1028
+ int ret = snprintf (gid_string , sizeof (gid_string ), "%u" , pw -> pw_uid );
1029
+
1030
+ if (ret < 0 )
1031
+ {
1032
+ Log (LOG_LEVEL_ERR , "Couldn't convert the gid of '%s' to string in function '%s'" ,
1033
+ pw -> pw_name , fp -> name );
1034
+ JsonDestroyMaybe (json , allocated );
1035
+ JsonDestroy (parent );
1036
+ return FnFailure ();
1037
+ }
1038
+ assert ((size_t ) ret < sizeof (gid_string ));
1039
+
1040
+ if (!StringMatchFull (value , gid_string ))
1041
+ {
1042
+ can_add_to_json = false;
1043
+ }
1044
+ }
1045
+ else if (StringEqual (attribute , "gecos" ))
1046
+ {
1047
+ if (!StringMatchFull (value , pw -> pw_gecos ))
1048
+ {
1049
+ can_add_to_json = false;
1050
+ }
1051
+ }
1052
+ else if (StringEqual (attribute , "dir" ))
1053
+ {
1054
+ if ((!StringMatchFull (value , pw -> pw_dir )))
1055
+ {
1056
+ can_add_to_json = false;
1057
+ }
1058
+ }
1059
+ else if (StringEqual (attribute , "shell" ))
1060
+ {
1061
+ if (!StringMatchFull (value , pw -> pw_shell ))
1062
+ {
1063
+ can_add_to_json = false;
1064
+ }
1065
+ }
1066
+ else
1067
+ {
1068
+ Log (LOG_LEVEL_ERR , "Invalid attribute '%s' in function '%s': not supported" ,
1069
+ attribute , fp -> name );
1070
+ JsonDestroyMaybe (json , allocated );
1071
+ JsonDestroy (parent );
1072
+ return FnFailure ();
1073
+ }
1074
+ element = JsonIteratorNextValue (& iter );
1075
+ }
1076
+ if (can_add_to_json )
1077
+ {
1078
+ JsonElement * child = JsonObjectCreate (6 );
1079
+ JsonObjectAppendInteger (child , "uid" , pw -> pw_uid );
1080
+ JsonObjectAppendInteger (child , "gid" , pw -> pw_gid );
1081
+ JsonObjectAppendString (child , "gecos" , pw -> pw_gecos );
1082
+ JsonObjectAppendString (child , "dir" , pw -> pw_dir );
1083
+ JsonObjectAppendString (child , "shell" , pw -> pw_shell );
1084
+ JsonObjectAppendObject (parent , pw -> pw_name , child );
1085
+ }
1086
+ }
1087
+ endpwent ();
1088
+ JsonDestroyMaybe (json , allocated );
1089
+
1090
+ return FnReturnContainerNoCopy (parent );
1091
+ }
1092
+
1093
+ #else
1094
+
1095
+ static FnCallResult FnCallFindLocalUsers (ARG_UNUSED EvalContext * ctx , ARG_UNUSED const Policy * policy , ARG_UNUSED const FnCall * fp , ARG_UNUSED const Rlist * finalargs )
1096
+ {
1097
+ Log (LOG_LEVEL_ERR , "findlocalusers is not implemented" );
1098
+ return FnFailure ();
1099
+ }
1100
+
1101
+ #endif
1102
+
1103
+ /*********************************************************************/
1104
+
941
1105
static FnCallResult FnCallEscape (ARG_UNUSED EvalContext * ctx , ARG_UNUSED const Policy * policy , ARG_UNUSED const FnCall * fp , const Rlist * finalargs )
942
1106
{
943
1107
char buffer [CF_BUFSIZE ];
@@ -10530,7 +10694,11 @@ static const FnCallArg IS_DATATYPE_ARGS[] =
10530
10694
{CF_ANYSTRING , CF_DATA_TYPE_STRING , "Type" },
10531
10695
{NULL , CF_DATA_TYPE_NONE , NULL }
10532
10696
};
10533
-
10697
+ static const FnCallArg FIND_LOCAL_USERS_ARGS [] =
10698
+ {
10699
+ {CF_ANYSTRING , CF_DATA_TYPE_STRING , "Filter list" },
10700
+ {NULL , CF_DATA_TYPE_NONE , NULL }
10701
+ };
10534
10702
10535
10703
/*********************************************************/
10536
10704
/* FnCalls are rvalues in certain promise constraints */
@@ -10870,6 +11038,8 @@ const FnCallType CF_FNCALL_TYPES[] =
10870
11038
FNCALL_OPTION_VARARG , FNCALL_CATEGORY_DATA , SYNTAX_STATUS_NORMAL ),
10871
11039
FnCallTypeNew ("version_compare" , CF_DATA_TYPE_CONTEXT , VERSION_COMPARE_ARGS , & FnCallVersionCompare , "Compare two version numbers with a specified operator" ,
10872
11040
FNCALL_OPTION_NONE , FNCALL_CATEGORY_UTILS , SYNTAX_STATUS_NORMAL ),
11041
+ FnCallTypeNew ("findlocalusers" , CF_DATA_TYPE_CONTAINER , FIND_LOCAL_USERS_ARGS , & FnCallFindLocalUsers , "Find matching local users" ,
11042
+ FNCALL_OPTION_VARARG , FNCALL_CATEGORY_DATA , SYNTAX_STATUS_NORMAL ),
10873
11043
10874
11044
// Functions section following new naming convention
10875
11045
FnCallTypeNew ("string_mustache" , CF_DATA_TYPE_STRING , STRING_MUSTACHE_ARGS , & FnCallStringMustache , "Expand a Mustache template from arg1 into a string using the optional data container in arg2 or datastate()" ,
0 commit comments