String shadowPasswordEntry = cryptFunction.apply(password);
String shadowFile = "/etc/shadow";
// note we are using awk variables so that the user can be defined as a
// shell variable (ex. $USER) As the block is in single quotes,
// shell interpolation wouldn't work otherwise
Statement replaceEntryInTempFile = exec(format(
"awk -v user=^%1$s: -v password='%2$s' 'BEGIN { FS=OFS=\":\" } $0 ~ user { $2 = password } 1' %3$s >%3$s.%1$s",
login, shadowPasswordEntry, shadowFile));
// would have preferred to use exec <>3 && style, but for some reason
// the sha512 line breaks in both awk and sed during an inline
// expansion. unfortunately, we have to save a temp file. In this case,
// somewhat avoiding collisions by naming the file .user, conceding it
// isn't using any locks to prevent overlapping changes
Statement replaceShadowFile = exec(format("test -f %2$s.%1$s && mv %2$s.%1$s %2$s", login, shadowFile));
return new StatementList(ImmutableList.of(replaceEntryInTempFile, replaceShadowFile)).render(family);
} catch (Exception e) {
propagate(e);
return null;
}