From 6091827530d6dd43479d6709fb6e9f745c11e900 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 18 Nov 2009 00:42:52 +0100 Subject: initial commit --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitignore (limited to '.gitignore') diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..6169dd062e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +systemd +*.o -- cgit v1.2.3-54-g00ecf From 6a66a1af459a5a0e1d77644bf4f7929ad1c5eb5f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 20 Jan 2010 18:27:25 +0100 Subject: update gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 6169dd062e..ff976ff9a9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ systemd *.o +test-engine -- cgit v1.2.3-54-g00ecf From 1ffba6fe82d65f2a87b53a21c7927bca8176038c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 21 Jan 2010 00:51:37 +0100 Subject: fix job merging --- .gitignore | 1 + Makefile | 5 +- job.c | 100 +++++++++++++++++++++- job.h | 5 ++ manager.c | 259 ++++++++++++++++++++++++++++++++++++++------------------ test-engine.c | 21 ++++- test-job-type.c | 65 ++++++++++++++ test2/g.service | 3 + 8 files changed, 371 insertions(+), 88 deletions(-) create mode 100644 test-job-type.c create mode 100644 test2/g.service (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index ff976ff9a9..94e3036c97 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ systemd *.o test-engine +test-job-type diff --git a/Makefile b/Makefile index f4bbb2aef5..5c6d798fba 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ LIBS=-lrt COMMON=name.o util.o set.o hashmap.o strv.o job.o manager.o conf-parser.o load-fragment.o socket-util.o log.o -all: systemd test-engine +all: systemd test-engine test-job-type systemd: main.o $(COMMON) $(CC) $(CFLAGS) -o $@ $^ $(LIBS) @@ -11,5 +11,8 @@ systemd: main.o $(COMMON) test-engine: test-engine.o $(COMMON) $(CC) $(CFLAGS) -o $@ $^ $(LIBS) +test-job-type: test-job-type.o $(COMMON) + $(CC) $(CFLAGS) -o $@ $^ $(LIBS) + clean: rm -f *.o systemd test-engine diff --git a/job.c b/job.c index bbb8214895..6241f203f9 100644 --- a/job.c +++ b/job.c @@ -1,6 +1,7 @@ /*-*- Mode: C; c-basic-offset: 8 -*-*/ #include +#include #include "macro.h" #include "job.h" @@ -127,7 +128,7 @@ void job_dependency_delete(Job *subject, Job *object, bool *matters) { job_dependency_free(l); } -void job_dump(Job *j, FILE*f, const char *prefix) { +const char* job_type_to_string(JobType t) { static const char* const job_type_table[_JOB_TYPE_MAX] = { [JOB_START] = "start", @@ -139,6 +140,15 @@ void job_dump(Job *j, FILE*f, const char *prefix) { [JOB_TRY_RESTART] = "try-restart", }; + + if (t < 0 || t >= _JOB_TYPE_MAX) + return "n/a"; + + return job_type_table[t]; +} + +void job_dump(Job *j, FILE*f, const char *prefix) { + static const char* const job_state_table[_JOB_STATE_MAX] = { [JOB_WAITING] = "waiting", [JOB_RUNNING] = "running", @@ -153,7 +163,7 @@ void job_dump(Job *j, FILE*f, const char *prefix) { "%s\tAction: %s → %s\n" "%s\tState: %s\n", prefix, j->id, - prefix, name_id(j->name), job_type_table[j->type], + prefix, name_id(j->name), job_type_to_string(j->type), prefix, job_state_table[j->state]); } @@ -168,3 +178,89 @@ bool job_is_anchor(Job *j) { return false; } + +static bool types_match(JobType a, JobType b, JobType c, JobType d) { + return + (a == c && b == d) || + (a == d && b == c); +} + +int job_type_merge(JobType *a, JobType b) { + if (*a == b) + return 0; + + /* Merging is associative! a merged with b merged with c is + * the same as a merged with c merged with b. */ + + /* Mergeability is transitive! if a can be merged with b and b + * with c then a also with c */ + + /* Also, if a merged with b cannot be merged with c, then + * either a or b cannot be merged with c either */ + + if (types_match(*a, b, JOB_START, JOB_VERIFY_STARTED)) + *a = JOB_START; + else if (types_match(*a, b, JOB_START, JOB_RELOAD) || + types_match(*a, b, JOB_START, JOB_RELOAD_OR_START) || + types_match(*a, b, JOB_VERIFY_STARTED, JOB_RELOAD_OR_START) || + types_match(*a, b, JOB_RELOAD, JOB_RELOAD_OR_START)) + *a = JOB_RELOAD_OR_START; + else if (types_match(*a, b, JOB_START, JOB_RESTART) || + types_match(*a, b, JOB_START, JOB_TRY_RESTART) || + types_match(*a, b, JOB_VERIFY_STARTED, JOB_RESTART) || + types_match(*a, b, JOB_RELOAD, JOB_RESTART) || + types_match(*a, b, JOB_RELOAD_OR_START, JOB_RESTART) || + types_match(*a, b, JOB_RELOAD_OR_START, JOB_TRY_RESTART) || + types_match(*a, b, JOB_RESTART, JOB_TRY_RESTART)) + *a = JOB_RESTART; + else if (types_match(*a, b, JOB_VERIFY_STARTED, JOB_RELOAD)) + *a = JOB_RELOAD; + else if (types_match(*a, b, JOB_VERIFY_STARTED, JOB_TRY_RESTART) || + types_match(*a, b, JOB_RELOAD, JOB_TRY_RESTART)) + *a = JOB_TRY_RESTART; + else + return -EEXIST; + + return 0; +} + +bool job_type_mergeable(JobType a, JobType b) { + return job_type_merge(&a, b) >= 0; +} + +bool job_type_is_superset(JobType a, JobType b) { + + /* Checks whether operation a is a "superset" of b */ + + if (a == b) + return true; + + switch (a) { + case JOB_START: + return b == JOB_VERIFY_STARTED; + + case JOB_RELOAD: + return b == JOB_VERIFY_STARTED; + + case JOB_RELOAD_OR_START: + return + b == JOB_RELOAD || + b == JOB_START; + + case JOB_RESTART: + return + b == JOB_START || + b == JOB_VERIFY_STARTED || + b == JOB_RELOAD || + b == JOB_RELOAD_OR_START || + b == JOB_TRY_RESTART; + + case JOB_TRY_RESTART: + return + b == JOB_VERIFY_STARTED || + b == JOB_RELOAD; + default: + return false; + + } +} diff --git a/job.h b/job.h index 7a44ae0776..33c599eaba 100644 --- a/job.h +++ b/job.h @@ -90,4 +90,9 @@ bool job_is_anchor(Job *j); int job_merge(Job *j, Job *other); +const char* job_type_to_string(JobType t); +int job_type_merge(JobType *a, JobType b); +bool job_type_mergeable(JobType a, JobType b); +bool job_type_is_superset(JobType a, JobType b); + #endif diff --git a/manager.c b/manager.c index da2d433474..94ad680eb4 100644 --- a/manager.c +++ b/manager.c @@ -55,12 +55,24 @@ static void transaction_delete_job(Manager *m, Job *j) { assert(m); assert(j); + /* Deletes one job from the transaction */ + manager_transaction_unlink_job(m, j); if (!j->linked) job_free(j); } +static void transaction_delete_name(Manager *m, Name *n) { + Job *j; + + /* Deletes all jobs associated with a certain name from the + * transaction */ + + while ((j = hashmap_get(m->transaction_jobs, n))) + transaction_delete_job(m, j); +} + static void transaction_abort(Manager *m) { Job *j; @@ -81,6 +93,11 @@ static void transaction_find_jobs_that_matter_to_anchor(Manager *m, Job *j, unsi assert(m); + /* A recursive sweep through the graph that marks all names + * that matter to the anchor job, i.e. are directly or + * indirectly a dependency of the anchor job via paths that + * are fully marked as mattering. */ + for (l = j ? j->subject_list : m->transaction_anchor; l; l = l->subject_next) { /* This link does not matter */ @@ -98,40 +115,6 @@ static void transaction_find_jobs_that_matter_to_anchor(Manager *m, Job *j, unsi } } -static bool types_match(JobType a, JobType b, JobType c, JobType d) { - return - (a == c && b == d) || - (a == d && b == c); -} - -static int types_merge(JobType *a, JobType b) { - if (*a == b) - return 0; - - if (types_match(*a, b, JOB_START, JOB_VERIFY_STARTED)) - *a = JOB_START; - else if (types_match(*a, b, JOB_START, JOB_RELOAD) || - types_match(*a, b, JOB_START, JOB_RELOAD_OR_START) || - types_match(*a, b, JOB_VERIFY_STARTED, JOB_RELOAD_OR_START) || - types_match(*a, b, JOB_RELOAD, JOB_RELOAD_OR_START)) - *a = JOB_RELOAD_OR_START; - else if (types_match(*a, b, JOB_START, JOB_RESTART) || - types_match(*a, b, JOB_START, JOB_TRY_RESTART) || - types_match(*a, b, JOB_VERIFY_STARTED, JOB_RESTART) || - types_match(*a, b, JOB_RELOAD, JOB_RESTART) || - types_match(*a, b, JOB_RELOAD_OR_START, JOB_RESTART) || - types_match(*a, b, JOB_RELOAD_OR_START, JOB_TRY_RESTART) || - types_match(*a, b, JOB_RESTART, JOB_TRY_RESTART)) - *a = JOB_RESTART; - else if (types_match(*a, b, JOB_VERIFY_STARTED, JOB_RELOAD)) - *a = JOB_RELOAD; - else if (types_match(*a, b, JOB_VERIFY_STARTED, JOB_TRY_RESTART) || - types_match(*a, b, JOB_RELOAD, JOB_TRY_RESTART)) - *a = JOB_TRY_RESTART; - - return -EEXIST; -} - static void transaction_merge_and_delete_job(Manager *m, Job *j, Job *other, JobType t) { JobDependency *l, *last; @@ -140,6 +123,8 @@ static void transaction_merge_and_delete_job(Manager *m, Job *j, Job *other, Job assert(j->name == other->name); assert(!j->linked); + /* Merges 'other' into 'j' and then deletes j. */ + j->type = t; j->state = JOB_WAITING; @@ -183,6 +168,44 @@ static void transaction_merge_and_delete_job(Manager *m, Job *j, Job *other, Job transaction_delete_job(m, other); } +static int delete_one_unmergable_job(Manager *m, Job *j) { + Job *k; + + assert(j); + + /* Tries to delete one item in the linked list + * j->transaction_next->transaction_next->... that conflicts + * whith another one, in an attempt to make an inconsistent + * transaction work. */ + + /* We rely here on the fact that if a merged with b does not + * merge with c, either a or b merge with c neither */ + for (; j; j = j->transaction_next) + for (k = j->transaction_next; k; k = k->transaction_next) { + Job *d; + + /* Is this one mergeable? Then skip it */ + if (job_type_mergeable(j->type, k->type)) + continue; + + /* Ok, we found two that conflict, let's see if we can + * drop one of them */ + if (!j->matters_to_anchor) + d = j; + else if (!k->matters_to_anchor) + d = k; + else + return -ENOEXEC; + + /* Ok, we can drop one, so let's do so. */ + log_debug("Try to fix job merging by deleting job %s", name_id(d->name)); + transaction_delete_job(m, d); + return 0; + } + + return -EINVAL; +} + static int transaction_merge_jobs(Manager *m) { Job *j; void *state; @@ -190,13 +213,39 @@ static int transaction_merge_jobs(Manager *m) { assert(m); + /* First step, check whether any of the jobs for one specific + * task conflict. If so, try to drop one of them. */ + HASHMAP_FOREACH(j, m->transaction_jobs, state) { + JobType t; + Job *k; + + t = j->type; + for (k = j->transaction_next; k; k = k->transaction_next) { + if ((r = job_type_merge(&t, k->type)) >= 0) + continue; + + /* OK, we could not merge all jobs for this + * action. Let's see if we can get rid of one + * of them */ + + if ((r = delete_one_unmergable_job(m, j)) >= 0) + /* Ok, we managed to drop one, now + * let's ask our callers to call us + * again after garbage collecting */ + return -EAGAIN; + + /* We couldn't merge anything. Failure */ + return r; + } + } + + /* Second step, merge the jobs. */ HASHMAP_FOREACH(j, m->transaction_jobs, state) { JobType t = j->type; Job *k; for (k = j->transaction_next; k; k = k->transaction_next) - if ((r = types_merge(&t, k->type)) < 0) - return r; + assert_se(job_type_merge(&t, k->type) == 0); while ((k = j->transaction_next)) { if (j->linked) { @@ -213,6 +262,20 @@ static int transaction_merge_jobs(Manager *m) { return 0; } +static bool name_matters_to_anchor(Name *n, Job *j) { + assert(n); + assert(!j->transaction_prev); + + /* Checks whether at least one of the jobs for this name + * matters to the anchor. */ + + for (; j; j = j->transaction_next) + if (j->matters_to_anchor) + return true; + + return false; +} + static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned generation) { void *state; Name *n; @@ -220,19 +283,30 @@ static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned assert(m); assert(j); + assert(!j->transaction_prev); + + /* Does a recursive sweep through the ordering graph, looking + * for a cycle. If we find cycle we try to break it. */ /* Did we find a cycle? */ if (j->marker && j->generation == generation) { Job *k; /* So, we already have been here. We have a - * cycle. Let's try to break it. We go backwards in our - * path and try to find a suitable job to remove. */ + * cycle. Let's try to break it. We go backwards in + * our path and try to find a suitable job to + * remove. We use the marker to find our way back, + * since smart how we are we stored our way back in + * there. */ for (k = from; k; k = (k->generation == generation ? k->marker : NULL)) { - if (!k->matters_to_anchor) { + + if (!k->linked && + !name_matters_to_anchor(k->name, k)) { + /* Ok, we can drop this one, so let's + * do so. */ log_debug("Breaking order cycle by deleting job %s", name_id(k->name)); - transaction_delete_job(m, k); + transaction_delete_name(m, k->name); return -EAGAIN; } @@ -242,19 +316,25 @@ static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned break; } - return -ELOOP; + return -ENOEXEC; } + /* Make the marker point to where we come from, so that we can + * find our way backwards if we want to break a cycle */ j->marker = from; j->generation = generation; - /* We assume that the the dependencies are both-ways, and + /* We assume that the the dependencies are bidirectional, and * hence can ignore NAME_AFTER */ - SET_FOREACH(n, j->name->meta.dependencies[NAME_BEFORE], state) { Job *o; + /* Is there a job for this name? */ if (!(o = hashmap_get(m->transaction_jobs, n))) + + /* Ok, there is no job for this in the + * transaction, but maybe there is already one + * running? */ if (!(o = n->meta.job)) continue; @@ -266,36 +346,19 @@ static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned } static int transaction_verify_order(Manager *m, unsigned *generation) { - bool again; + Job *j; + int r; + void *state; + assert(m); assert(generation); - do { - Job *j; - int r; - void *state; - - again = false; - - HASHMAP_FOREACH(j, m->transaction_jobs, state) { + /* Check if the ordering graph is cyclic. If it is, try to fix + * that up by dropping one of the jobs. */ - /* Assume merged */ - assert(!j->transaction_next); - assert(!j->transaction_prev); - - if ((r = transaction_verify_order_one(m, j, NULL, (*generation)++)) < 0) { - - /* There was a cycleq, but it was fixed, - * we need to restart our algorithm */ - if (r == -EAGAIN) { - again = true; - break; - } - - return r; - } - } - } while (again); + HASHMAP_FOREACH(j, m->transaction_jobs, state) + if ((r = transaction_verify_order_one(m, j, NULL, (*generation)++)) < 0) + return r; return 0; } @@ -305,6 +368,8 @@ static void transaction_collect_garbage(Manager *m) { assert(m); + /* Drop jobs that are not required by any other job */ + do { void *state; Job *j; @@ -335,7 +400,9 @@ static int transaction_is_destructive(Manager *m, JobMode mode) { * existing jobs would be replaced */ HASHMAP_FOREACH(j, m->transaction_jobs, state) - if (j->name->meta.job && j->name->meta.job != j) + if (j->name->meta.job && + j->name->meta.job != j && + !job_type_is_superset(j->type, j->name->meta.job->type)) return -EEXIST; return 0; @@ -346,6 +413,8 @@ static int transaction_apply(Manager *m, JobMode mode) { Job *j; int r; + /* Moves the transaction jobs to the set of active jobs */ + HASHMAP_FOREACH(j, m->transaction_jobs, state) { if (j->linked) continue; @@ -376,6 +445,8 @@ static int transaction_apply(Manager *m, JobMode mode) { job_dependency_free(j->object_list); } + m->transaction_anchor = NULL; + return 0; rollback: @@ -403,23 +474,47 @@ static int transaction_activate(Manager *m, JobMode mode) { /* First step: figure out which jobs matter */ transaction_find_jobs_that_matter_to_anchor(m, NULL, generation++); - /* Second step: let's merge entries we can merge */ - if ((r = transaction_merge_jobs(m)) < 0) - goto rollback; + for (;;) { + /* Second step: Let's remove unneeded jobs that might + * be lurking. */ + transaction_collect_garbage(m); - /* Third step: verify order makes sense */ - if ((r = transaction_verify_order(m, &generation)) < 0) - goto rollback; + /* Third step: verify order makes sense and correct + * cycles if necessary and possible */ + if ((r = transaction_verify_order(m, &generation)) >= 0) + break; - /* Third step: do garbage colletion */ - transaction_collect_garbage(m); + if (r != -EAGAIN) + goto rollback; - /* Fourth step: check whether we can actually apply this */ + /* Let's see if the resulting transaction ordering + * graph is still cyclic... */ + } + + for (;;) { + /* Fourth step: let's drop unmergable entries if + * necessary and possible, merge entries we can + * merge */ + if ((r = transaction_merge_jobs(m)) >= 0) + break; + + if (r != -EAGAIN) + goto rollback; + + /* Fifth step: an entry got dropped, let's garbage + * collect its dependencies. */ + transaction_collect_garbage(m); + + /* Let's see if the resulting transaction still has + * unmergable entries ... */ + } + + /* Sith step: check whether we can actually apply this */ if (mode == JOB_FAIL) if ((r = transaction_is_destructive(m, mode)) < 0) goto rollback; - /* Fifth step: apply changes */ + /* Seventh step: apply changes */ if ((r = transaction_apply(m, mode)) < 0) goto rollback; @@ -664,10 +759,10 @@ int manager_load_name(Manager *m, const char *name, Name **_ret) { return -ENOMEM; } - if (set_put(ret->meta.names, n) < 0) { + if ((r = set_put(ret->meta.names, n)) < 0) { name_free(ret); free(n); - return -ENOMEM; + return r; } if ((r = name_link(ret)) < 0) { diff --git a/test-engine.c b/test-engine.c index 15ce70f1ac..f92b05a7dd 100644 --- a/test-engine.c +++ b/test-engine.c @@ -9,7 +9,7 @@ int main(int argc, char *argv[]) { Manager *m = NULL; - Name *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e; + Name *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *g = NULL; Job *j; assert_se(chdir("test2") == 0); @@ -33,13 +33,28 @@ int main(int argc, char *argv[]) { manager_dump_names(m, stdout, "\t"); printf("Test2: (Cyclic Order, Unfixable)\n"); - assert_se(manager_add_job(m, JOB_START, d, JOB_REPLACE, false, &j) == -ELOOP); + assert_se(manager_add_job(m, JOB_START, d, JOB_REPLACE, false, &j) == -ENOEXEC); manager_dump_jobs(m, stdout, "\t"); - printf("Test3: (Cyclic Order, Fixable)\n"); + printf("Test3: (Cyclic Order, Fixable, Garbage Collector)\n"); assert_se(manager_add_job(m, JOB_START, e, JOB_REPLACE, false, &j) == 0); manager_dump_jobs(m, stdout, "\t"); + printf("Test4: (Identical transaction)\n"); + assert_se(manager_add_job(m, JOB_START, e, JOB_FAIL, false, &j) == 0); + manager_dump_jobs(m, stdout, "\t"); + + printf("Loaded3:\n"); + assert_se(manager_load_name(m, "g.service", &g) == 0); + manager_dump_names(m, stdout, "\t"); + + printf("Test5: (Colliding transaction, fail)\n"); + assert_se(manager_add_job(m, JOB_START, g, JOB_FAIL, false, &j) == -EEXIST); + + printf("Test6: (Colliding transaction, replace)\n"); + assert_se(manager_add_job(m, JOB_START, g, JOB_REPLACE, false, &j) == 0); + manager_dump_jobs(m, stdout, "\t"); + manager_free(m); return 0; diff --git a/test-job-type.c b/test-job-type.c new file mode 100644 index 0000000000..db5c329e13 --- /dev/null +++ b/test-job-type.c @@ -0,0 +1,65 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#include +#include +#include +#include + +#include "job.h" + +int main(int argc, char*argv[]) { + JobType a, b, c, d, e, f, g; + + for (a = 0; a < _JOB_TYPE_MAX; a++) + for (b = 0; b < _JOB_TYPE_MAX; b++) { + + if (!job_type_mergeable(a, b)) + printf("Not mergeable: %s + %s\n", job_type_to_string(a), job_type_to_string(b)); + + for (c = 0; c < _JOB_TYPE_MAX; c++) { + + /* Verify transitivity of mergeability + * of job types */ + assert(!job_type_mergeable(a, b) || + !job_type_mergeable(b, c) || + job_type_mergeable(a, c)); + + d = a; + if (job_type_merge(&d, b) >= 0) { + + printf("%s + %s = %s\n", job_type_to_string(a), job_type_to_string(b), job_type_to_string(d)); + + /* Verify that merged entries can be + * merged with the same entries they + * can be merged with seperately */ + assert(!job_type_mergeable(a, c) || job_type_mergeable(d, c)); + assert(!job_type_mergeable(b, c) || job_type_mergeable(d, c)); + + /* Verify that if a merged + * with b is not mergable with + * c then either a or b is not + * mergeable with c either. */ + assert(job_type_mergeable(d, c) || !job_type_mergeable(a, c) || !job_type_mergeable(b, c)); + + e = b; + if (job_type_merge(&e, c) >= 0) { + + /* Verify associativity */ + + f = d; + assert(job_type_merge(&f, c) == 0); + + g = e; + assert(job_type_merge(&g, a) == 0); + + assert(f == g); + + printf("%s + %s + %s = %s\n", job_type_to_string(a), job_type_to_string(b), job_type_to_string(c), job_type_to_string(d)); + } + } + } + } + + + return 0; +} diff --git a/test2/g.service b/test2/g.service new file mode 100644 index 0000000000..e811e6083d --- /dev/null +++ b/test2/g.service @@ -0,0 +1,3 @@ +[Meta] +Description=G +Conflicts=e.service -- cgit v1.2.3-54-g00ecf From b52429d4e0de52b89c7d043164aa1ac720e9179f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 27 Jan 2010 22:43:50 +0100 Subject: implement trivial socket activated logger daemon --- .gitignore | 1 + Makefile | 5 +- logger.c | 496 +++++++++++++++++++++ test1/multiuser.target | 1 - test1/multiuser.target.wants/systemd-logger.socket | 1 + test1/systemd-logger.service | 6 + test1/systemd-logger.socket | 7 + 7 files changed, 515 insertions(+), 2 deletions(-) create mode 100644 logger.c create mode 120000 test1/multiuser.target.wants/systemd-logger.socket create mode 100644 test1/systemd-logger.service create mode 100644 test1/systemd-logger.socket (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 94e3036c97..7f892d0570 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ systemd *.o test-engine test-job-type +systemd-logger diff --git a/Makefile b/Makefile index 119d3dc663..d61e14b63a 100644 --- a/Makefile +++ b/Makefile @@ -25,11 +25,14 @@ COMMON= \ load-dropin.o \ execute.o -all: systemd test-engine test-job-type +all: systemd test-engine test-job-type systemd-logger systemd: main.o $(COMMON) $(CC) $(CFLAGS) -o $@ $^ $(LIBS) +systemd-logger: logger.o $(COMMON) + $(CC) $(CFLAGS) -o $@ $^ $(LIBS) + test-engine: test-engine.o $(COMMON) $(CC) $(CFLAGS) -o $@ $^ $(LIBS) diff --git a/logger.c b/logger.c new file mode 100644 index 0000000000..c7c4cb83d2 --- /dev/null +++ b/logger.c @@ -0,0 +1,496 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "log.h" +#include "list.h" + +#define STREAM_BUFFER 2048 +#define STREAMS_MAX 256 +#define SERVER_FD_START 3 +#define SERVER_FD_MAX 16 +#define TIMEOUT ((int) (10*MSEC_PER_SEC)) + +typedef struct Stream Stream; + +typedef struct Server { + int log_fd; + int epoll_fd; + + unsigned n_server_fd; + + LIST_HEAD(Stream, streams); + unsigned n_streams; +} Server; + +typedef enum StreamState { + STREAM_PRIORITY, + STREAM_PROCESS, + STREAM_RUNNING +} StreamState; + +struct Stream { + Server *server; + + StreamState state; + + int fd; + int priority; + char *process; + + char buffer[STREAM_BUFFER]; + size_t length; + + pid_t pid; + + LIST_FIELDS(Stream, stream); +}; + +#define IOVEC_SET_STRING(iovec, s) \ + do { \ + (iovec).iov_base = s; \ + (iovec).iov_len = strlen(s); \ + } while(false); + +static int stream_log(Stream *s, char *p, usec_t timestamp) { + + char header_priority[16], header_time[64], header_pid[16]; + time_t t; + struct tm *tm; + struct msghdr msghdr; + struct iovec iovec[5]; + + assert(s); + assert(p); + + if (*p == 0) + return 0; + + /* + * The format glibc uses is: + * + * time process[pid]: msg + */ + + snprintf(header_priority, sizeof(header_priority), "<%i>", s->priority); + char_array_0(header_priority); + + t = (time_t) (timestamp / USEC_PER_SEC); + if (!(tm = localtime(&t))) + return -EINVAL; + + if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0) + return -EINVAL; + + snprintf(header_pid, sizeof(header_pid), "[%llu]: ", (unsigned long long) s->pid); + char_array_0(header_pid); + + zero(iovec); + IOVEC_SET_STRING(iovec[0], header_priority); + IOVEC_SET_STRING(iovec[1], header_time); + IOVEC_SET_STRING(iovec[2], s->process); + IOVEC_SET_STRING(iovec[3], header_pid); + IOVEC_SET_STRING(iovec[4], p); + + zero(msghdr); + msghdr.msg_iov = iovec; + msghdr.msg_iovlen = ELEMENTSOF(iovec); + + if (sendmsg(s->server->log_fd, &msghdr, MSG_NOSIGNAL) < 0) + return -errno; + + return 0; +} + +static int stream_line(Stream *s, char *p, usec_t timestamp) { + int r; + + assert(s); + assert(p); + + p = strstrip(p); + + switch (s->state) { + + case STREAM_PRIORITY: + if ((r = safe_atoi(p, &s->priority)) < 0) + return r; + + if (s->priority < 0) + return -ERANGE; + + s->state = STREAM_PROCESS; + return 0; + + case STREAM_PROCESS: + if (!(s->process = strdup(p))) + return -ENOMEM; + + s->state = STREAM_RUNNING; + return 0; + + case STREAM_RUNNING: + return stream_log(s, p, timestamp); + } + + assert_not_reached("Unknown stream state"); +} + +static int stream_scan(Stream *s, usec_t timestamp) { + char *p; + size_t remaining; + int r = 0; + + assert(s); + + p = s->buffer; + remaining = s->length; + for (;;) { + char *newline; + + if (!(newline = memchr(p, '\n', remaining))) + break; + + *newline = 0; + + if ((r = stream_line(s, p, timestamp)) >= 0) { + remaining -= newline-p+1; + p = newline+1; + } + } + + if (p > s->buffer) { + memmove(s->buffer, p, remaining); + s->length = remaining; + } + + return r; +} + +static int stream_process(Stream *s, usec_t timestamp) { + ssize_t l; + int r; + assert(s); + + if ((l = read(s->fd, s->buffer+s->length, STREAM_BUFFER-s->length)) < 0) { + + if (errno == EAGAIN) + return 0; + + log_warning("Failed to read from stream: %s", strerror(errno)); + return -1; + } + + + if (l == 0) + return 0; + + s->length += l; + r = stream_scan(s, timestamp); + + if (r < 0) + return r; + + return 1; +} + +static void stream_free(Stream *s) { + assert(s); + + if (s->server) { + assert(s->server->n_streams > 0); + s->server->n_streams--; + LIST_REMOVE(Stream, stream, s->server->streams, s); + + } + + if (s->fd >= 0) { + if (s->server) + epoll_ctl(s->server->epoll_fd, EPOLL_CTL_DEL, s->fd, NULL); + + assert_se(close_nointr(s->fd) == 0); + } + + free(s->process); + free(s); +} + +static int stream_new(Server *s, int server_fd) { + Stream *stream; + int fd; + struct ucred ucred; + socklen_t len = sizeof(ucred); + struct epoll_event ev; + int r; + + assert(s); + + if ((fd = accept4(server_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC)) < 0) + return -errno; + + if (s->n_streams >= STREAMS_MAX) { + log_warning("Too many connections, refusing connection."); + assert_se(close_nointr(fd) == 0); + return 0; + } + + if (!(stream = new0(Stream, 1))) { + assert_se(close_nointr(fd) == 0); + return -ENOMEM; + } + + stream->fd = fd; + + if (getsockopt(stream->fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) { + r = -errno; + goto fail; + } + + if (shutdown(fd, SHUT_WR) < 0) { + r = -errno; + goto fail; + } + + zero(ev); + ev.data.ptr = stream; + ev.events = POLLIN; + if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) { + r = -errno; + goto fail; + } + + stream->pid = ucred.pid; + + stream->server = s; + LIST_PREPEND(Stream, stream, s->streams, stream); + s->n_streams ++; + + return 0; + +fail: + stream_free(stream); + return r; +} + +static int verify_environment(unsigned *n_sockets) { + unsigned long long pid; + const char *e; + int r; + unsigned ns; + + assert_se(n_sockets); + + if (!(e = getenv("LISTEN_PID"))) { + log_error("Missing $LISTEN_PID environment variable."); + return -ENOENT; + } + + if ((r = safe_atollu(e, &pid)) < 0) { + log_error("Failed to parse $LISTEN_PID: %s", strerror(-r)); + return r; + } + + if (pid != (unsigned long long) getpid()) { + log_error("Socket nor for me."); + return -ENOENT; + } + + if (!(e = getenv("LISTEN_FDS"))) { + log_error("Missing $LISTEN_FDS environment variable."); + return -ENOENT; + } + + if ((r = safe_atou(e, &ns)) < 0) { + log_error("Failed to parse $LISTEN_FDS: %s", strerror(-r)); + return -E2BIG; + } + + if (ns <= 0 || ns > SERVER_FD_MAX) { + log_error("Wrong number of file descriptors passed: %s", e); + return -E2BIG; + } + + *n_sockets = ns; + + return 0; +} + +static void server_done(Server *s) { + unsigned i; + assert(s); + + while (s->streams) + stream_free(s->streams); + + for (i = 0; i < s->n_server_fd; i++) + assert_se(close_nointr(SERVER_FD_START+i) == 0); + + if (s->log_fd >= 0) + assert_se(close_nointr(s->log_fd) == 0); + + if (s->epoll_fd >= 0) + assert_se(close_nointr(s->epoll_fd) == 0); +} + +static int server_init(Server *s, unsigned n_sockets) { + int r; + unsigned i; + union { + struct sockaddr sa; + struct sockaddr_un un; + } sa; + + assert(s); + assert(n_sockets > 0); + + zero(*s); + + s->n_server_fd = n_sockets; + s->log_fd = -1; + + if ((s->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) { + r = -errno; + log_error("Failed to create epoll object: %s", strerror(errno)); + goto fail; + } + + for (i = 0; i < n_sockets; i++) { + struct epoll_event ev; + + zero(ev); + ev.events = POLLIN; + ev.data.ptr = UINT_TO_PTR(SERVER_FD_START+i); + if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, SERVER_FD_START+i, &ev) < 0) { + r = -errno; + log_error("Failed to add server fd to epoll object: %s", strerror(errno)); + goto fail; + } + } + + if ((s->log_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) { + r = -errno; + log_error("Failed to create log fd: %s", strerror(errno)); + goto fail; + } + + zero(sa); + sa.un.sun_family = AF_UNIX; + strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path)); + + if (connect(s->log_fd, &sa.sa, sizeof(sa)) < 0) { + r = -errno; + log_error("Failed to connect log socket to /dev/log: %s", strerror(errno)); + goto fail; + } + + return 0; + +fail: + server_done(s); + return r; +} + +static int process_event(Server *s, struct epoll_event *ev) { + int r; + + assert(s); + + /* Yes, this is a bit ugly, we assume that that valid pointers + * are > SERVER_FD_START+SERVER_FD_MAX. Which is certainly + * true on Linux (and probably most other OSes, too, since the + * first 4k usually are part of a seperate null pointer + * dereference page. */ + + if (PTR_TO_UINT(ev->data.ptr) >= SERVER_FD_START && + PTR_TO_UINT(ev->data.ptr) < SERVER_FD_START+s->n_server_fd) { + + if (ev->events != POLLIN) { + log_info("Got invalid event from epoll. (1)"); + return -EIO; + } + + if ((r = stream_new(s, PTR_TO_UINT(ev->data.ptr))) < 0) { + log_info("Failed to accept new connection: %s", strerror(-r)); + return r; + } + + } else { + usec_t timestamp; + Stream *stream = ev->data.ptr; + + timestamp = now(CLOCK_REALTIME); + + if (!(ev->events & POLLIN)) { + log_info("Got invalid event from epoll. (3)"); + stream_free(stream); + return 0; + } + + if ((r = stream_process(stream, timestamp)) <= 0) { + + if (r < 0) + log_info("Got error on stream: %s", strerror(-r)); + + stream_free(stream); + return 0; + } + } + + return 0; +} + +int main(int argc, char *argv[]) { + Server server; + int r = 3; + unsigned n; + + log_info("systemd-logger running as pid %llu", (unsigned long long) getpid()); + + if (verify_environment(&n) < 0) + return 1; + + if (server_init(&server, n) < 0) + return 2; + + + for (;;) { + struct epoll_event event; + int n; + + if ((n = epoll_wait(server.epoll_fd, + &event, 1, + server.n_streams <= 0 ? TIMEOUT : -1)) < 0) { + + if (errno == EINTR) + continue; + + log_error("epoll_wait() failed: %s", strerror(errno)); + goto fail; + } + + if (n <= 0) + break; + + if ((r = process_event(&server, &event)) < 0) + goto fail; + } + r = 0; + +fail: + server_done(&server); + + log_info("systemd-logger stopped as pid %llu", (unsigned long long) getpid()); + + return r; +} diff --git a/test1/multiuser.target b/test1/multiuser.target index 35182421c4..f105e3c2f0 100644 --- a/test1/multiuser.target +++ b/test1/multiuser.target @@ -1,3 +1,2 @@ [Meta] -Wants=syslog.socket Description=Multi-User Target diff --git a/test1/multiuser.target.wants/systemd-logger.socket b/test1/multiuser.target.wants/systemd-logger.socket new file mode 120000 index 0000000000..4ce0a61aa1 --- /dev/null +++ b/test1/multiuser.target.wants/systemd-logger.socket @@ -0,0 +1 @@ +../systemd-logger.socket \ No newline at end of file diff --git a/test1/systemd-logger.service b/test1/systemd-logger.service new file mode 100644 index 0000000000..77af676351 --- /dev/null +++ b/test1/systemd-logger.service @@ -0,0 +1,6 @@ +[Meta] +Description=systemd Logging Daemon + +[Service] +ExecStart=/home/lennart/projects/systemd/systemd-logger +Type=simple diff --git a/test1/systemd-logger.socket b/test1/systemd-logger.socket new file mode 100644 index 0000000000..a013067ab2 --- /dev/null +++ b/test1/systemd-logger.socket @@ -0,0 +1,7 @@ +[Meta] +Description=systemd Logging Socket + +[Socket] +ExecStartPre=/bin/rm /tmp/systemd-logger +ListenStream==/systemd/logger +ListenStream=/tmp/systemd-logger -- cgit v1.2.3-54-g00ecf From 5630af71d72a2fa8142886ca2fb37e69a1906bca Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 2 Feb 2010 22:22:51 +0100 Subject: client: add a very basic Vala command line tool --- .gitignore | 2 + Makefile | 7 ++- systemctl.vala | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 systemctl.vala (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 7f892d0570..c15a61be48 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ systemd test-engine test-job-type systemd-logger +systemctl +systemctl.c diff --git a/Makefile b/Makefile index 2f739ce2ac..5a60eac9ff 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ COMMON= \ dbus-unit.o \ dbus-job.o -all: systemd test-engine test-job-type systemd-logger +all: systemd test-engine test-job-type systemd-logger systemctl systemd: main.o $(COMMON) $(CC) $(CFLAGS) -o $@ $^ $(LIBS) @@ -43,5 +43,8 @@ test-engine: test-engine.o $(COMMON) test-job-type: test-job-type.o $(COMMON) $(CC) $(CFLAGS) -o $@ $^ $(LIBS) +systemctl: systemctl.vala + valac --save-temps systemctl.vala --pkg=dbus-glib-1 --pkg=posix + clean: - rm -f *.o systemd test-engine + rm -f *.o systemd test-engine systemctl diff --git a/systemctl.vala b/systemctl.vala new file mode 100644 index 0000000000..f3b2e62e59 --- /dev/null +++ b/systemctl.vala @@ -0,0 +1,134 @@ +using DBus; +using GLib; + +[DBus (name = "org.freedesktop.systemd1")] +public interface Manager : DBus.Object { + + public struct UnitInfo { + string id; + string description; + string load_state; + string active_state; + ObjectPath unit_path; + uint32 job_id; + string job_type; + ObjectPath job_path; + } + + public struct JobInfo { + uint32 id; + string name; + string type; + string state; + ObjectPath job_path; + ObjectPath unit_path; + } + + public abstract UnitInfo[] ListUnits() throws DBus.Error; + public abstract JobInfo[] ListJobs() throws DBus.Error; + + public abstract ObjectPath LoadUnit(string name) throws DBus.Error; +} + +static string type = null; +static bool all = false; + +public static int job_info_compare(void* key1, void* key2) { + Manager.JobInfo *j1 = (Manager.JobInfo*) key1; + Manager.JobInfo *j2 = (Manager.JobInfo*) key2; + + return Posix.strcmp(j1->name, j2->name); +} + +public static int unit_info_compare(void* key1, void* key2) { + Manager.UnitInfo *u1 = (Manager.UnitInfo*) key1; + Manager.UnitInfo *u2 = (Manager.UnitInfo*) key2; + + int r = Posix.strcmp(Posix.strrchr(u1->id, '.'), Posix.strrchr(u2->id, '.')); + if (r != 0) + return r; + + return Posix.strcmp(u1->id, u2->id); +} + +static const OptionEntry entries[] = { + { "type", 't', 0, OptionArg.STRING, out type, "List only particular type of units", "TYPE" }, + { "all", 'a', 0, OptionArg.NONE, out all, "Show all units, including dead ones", null }, + { null } +}; + +int main (string[] args) { + + OptionContext context = new OptionContext(" -- Control systemd"); + context.add_main_entries(entries, null); + + try { + context.parse(ref args); + } catch (GLib.OptionError e) { + message("Failed to parse command line: %s".printf(e.message)); + } + + try { + Connection bus = Bus.get(BusType.SESSION); + + Manager manager = bus.get_object ( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1") as Manager; + + if (args[1] == "list-units" || args.length <= 1) { + var list = manager.ListUnits(); + uint n = 0; + Posix.qsort(list, list.length, sizeof(Manager.UnitInfo), unit_info_compare); + + stdout.printf("%-45s %-6s %-12s → %-15s\n\n", "UNIT", "LOAD", "ACTIVE", "JOB"); + + foreach (var i in list) { + + if (type != null && !i.id.has_suffix(".%s".printf(type))) + continue; + + if (!all && i.active_state == "inactive") + continue; + + stdout.printf("%-45s %-6s %-12s", i.id, i.load_state, i.active_state); + + if (i.job_id != 0) + stdout.printf("→ %-15s", i.job_type); + + stdout.puts("\n"); + n++; + } + + if (all) + stdout.printf("\n%u units listed.\n", n); + else + stdout.printf("\n%u live units listed. Pass --all to see dead units, too.\n", n); + + + } else if (args[1] == "list-jobs") { + var list = manager.ListJobs(); + Posix.qsort(list, list.length, sizeof(Manager.JobInfo), job_info_compare); + + foreach (var i in list) + stdout.printf("%-45s → %-15s %-7s\n", i.name, i.type, i.state); + + } else if (args[1] == "load") { + + if (args.length < 3) { + stderr.printf("Missing argument.\n"); + return 1; + } + + manager.LoadUnit(args[2]); + } else { + stderr.printf("Unknown command %s.\n", args[1]); + return 1; + } + + } catch (DBus.Error e) { + stderr.printf("%s\n".printf(e.message)); + } + + return 0; +} -- cgit v1.2.3-54-g00ecf From 501c7d0b77607fe858911b1c94070b63b6cc221e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 3 Feb 2010 12:39:12 +0100 Subject: systemadm: implement basic control UI systemadm --- .gitignore | 3 + Makefile | 9 +- systemadm.vala | 438 ++++++++++++++++++++++++++++++++++++++++++++++++ systemctl.vala | 109 +++++++----- systemd-interfaces.vala | 61 +++++++ 5 files changed, 577 insertions(+), 43 deletions(-) create mode 100644 systemadm.vala create mode 100644 systemd-interfaces.vala (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index c15a61be48..9c5193d27e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,6 @@ test-job-type systemd-logger systemctl systemctl.c +systemd-interfaces.c +systemadm +systemadm.c diff --git a/Makefile b/Makefile index 5a60eac9ff..41df4e29e3 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ COMMON= \ dbus-unit.o \ dbus-job.o -all: systemd test-engine test-job-type systemd-logger systemctl +all: systemd test-engine test-job-type systemd-logger systemctl systemadm systemd: main.o $(COMMON) $(CC) $(CFLAGS) -o $@ $^ $(LIBS) @@ -44,7 +44,10 @@ test-job-type: test-job-type.o $(COMMON) $(CC) $(CFLAGS) -o $@ $^ $(LIBS) systemctl: systemctl.vala - valac --save-temps systemctl.vala --pkg=dbus-glib-1 --pkg=posix + valac -g --save-temps systemctl.vala systemd-interfaces.vala --pkg=dbus-glib-1 --pkg=posix + +systemadm: systemadm.vala + valac -g --save-temps systemadm.vala systemd-interfaces.vala --pkg=dbus-glib-1 --pkg=posix --pkg gee-1.0 --pkg gtk+-2.0 clean: - rm -f *.o systemd test-engine systemctl + rm -f *.o systemd test-engine systemctl systemadm diff --git a/systemadm.vala b/systemadm.vala new file mode 100644 index 0000000000..18d3a367aa --- /dev/null +++ b/systemadm.vala @@ -0,0 +1,438 @@ +using Gtk; +using GLib; +using DBus; +using Pango; + +public class LeftLabel : Label { + public LeftLabel(string? text = null) { + if (text != null) + set_markup("%s".printf(text)); + set_alignment(1, 0); + set_padding(6, 0); + } +} + +public class RightLabel : Label { + public RightLabel(string? text = null) { + set_text_or_na(text); + set_alignment(0, 1); + set_ellipsize(EllipsizeMode.START); + set_selectable(true); + } + + public void set_text_or_na(string? text = null) { + if (text == null || text == "") + set_markup("n/a"); + else + set_text(text); + } +} + +public class MainWindow : Window { + + private TreeView unit_view; + private TreeView job_view; + + private ListStore unit_model; + private ListStore job_model; + + private Button start_button; + private Button stop_button; + private Button restart_button; + private Button reload_button; + private Button cancel_button; + + private Connection bus; + private Manager manager; + + private RightLabel unit_id_label; + private RightLabel unit_description_label; + private RightLabel unit_load_state_label; + private RightLabel unit_active_state_label; + private RightLabel unit_load_path_label; + private RightLabel unit_active_enter_timestamp_label; + private RightLabel unit_active_exit_timestamp_label; + private RightLabel unit_can_start_label; + private RightLabel unit_can_reload_label; + + private RightLabel job_id_label; + private RightLabel job_state_label; + private RightLabel job_type_label; + + public MainWindow() throws DBus.Error { + title = "systemdadm"; + position = WindowPosition.CENTER; + set_default_size(1000, 700); + set_border_width(12); + destroy.connect(Gtk.main_quit); + + Notebook notebook = new Notebook(); + add(notebook); + + Box unit_vbox = new VBox(false, 6); + notebook.append_page(unit_vbox, new Label("Units")); + unit_vbox.set_border_width(12); + + Box job_vbox = new VBox(false, 6); + notebook.append_page(job_vbox, new Label("Jobs")); + job_vbox.set_border_width(12); + + + unit_model = new ListStore(6, typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string)); + job_model = new ListStore(5, typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string)); + + unit_view = new TreeView.with_model(unit_model); + job_view = new TreeView.with_model(job_model); + + unit_view.cursor_changed.connect(unit_changed); + job_view.cursor_changed.connect(job_changed); + + unit_view.insert_column_with_attributes(-1, "Unit", new CellRendererText(), "text", 0); + unit_view.insert_column_with_attributes(-1, "Description", new CellRendererText(), "text", 1); + unit_view.insert_column_with_attributes(-1, "Load State", new CellRendererText(), "text", 2); + unit_view.insert_column_with_attributes(-1, "Active State", new CellRendererText(), "text", 3); + unit_view.insert_column_with_attributes(-1, "Job", new CellRendererText(), "text", 4); + + job_view.insert_column_with_attributes(-1, "Job", new CellRendererText(), "text", 0); + job_view.insert_column_with_attributes(-1, "Unit", new CellRendererText(), "text", 1); + job_view.insert_column_with_attributes(-1, "Type", new CellRendererText(), "text", 2); + job_view.insert_column_with_attributes(-1, "State", new CellRendererText(), "text", 3); + + ScrolledWindow scroll = new ScrolledWindow(null, null); + scroll.set_policy(PolicyType.AUTOMATIC, PolicyType.AUTOMATIC); + scroll.set_shadow_type(ShadowType.IN); + scroll.add(unit_view); + unit_vbox.pack_start(scroll, true, true, 0); + + scroll = new ScrolledWindow(null, null); + scroll.set_policy(PolicyType.AUTOMATIC, PolicyType.AUTOMATIC); + scroll.set_shadow_type(ShadowType.IN); + scroll.add(job_view); + job_vbox.pack_start(scroll, true, true, 0); + + unit_id_label = new RightLabel(); + unit_description_label = new RightLabel(); + unit_load_state_label = new RightLabel(); + unit_active_state_label = new RightLabel(); + unit_load_path_label = new RightLabel(); + unit_active_enter_timestamp_label = new RightLabel(); + unit_active_exit_timestamp_label = new RightLabel(); + unit_can_start_label = new RightLabel(); + unit_can_reload_label = new RightLabel(); + + job_id_label = new RightLabel(); + job_state_label = new RightLabel(); + job_type_label = new RightLabel(); + + Table unit_table = new Table(9, 2, false); + unit_table.set_row_spacings(6); + unit_table.set_border_width(12); + unit_vbox.pack_start(unit_table, false, true, 0); + + Table job_table = new Table(2, 2, false); + job_table.set_row_spacings(6); + job_table.set_border_width(12); + job_vbox.pack_start(job_table, false, true, 0); + + unit_table.attach(new LeftLabel("Id:"), 0, 1, 0, 1, AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(unit_id_label, 1, 2, 0, 1, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(new LeftLabel("Description:"), 0, 1, 1, 2, AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(unit_description_label, 1, 2, 1, 2, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(new LeftLabel("Load State:"), 0, 1, 2, 3, AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(unit_load_state_label, 1, 2, 2, 3, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(new LeftLabel("Active State:"), 0, 1, 3, 4, AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(unit_active_state_label, 1, 2, 3, 4, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(new LeftLabel("Load Path:"), 0, 1, 4, 5, AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(unit_load_path_label, 1, 2, 4, 5, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(new LeftLabel("Active Enter Timestamp:"), 0, 1, 5, 6, AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(unit_active_enter_timestamp_label, 1, 2, 5, 6, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(new LeftLabel("Active Exit Timestamp:"), 0, 1, 6, 7, AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(unit_active_exit_timestamp_label, 1, 2, 6, 7, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(new LeftLabel("Can Start/Stop:"), 0, 1, 7, 8, AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(unit_can_start_label, 1, 2, 7, 8, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(new LeftLabel("Can Reload:"), 0, 1, 8, 9, AttachOptions.FILL, AttachOptions.FILL, 0, 0); + unit_table.attach(unit_can_reload_label, 1, 2, 8, 9, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); + + job_table.attach(new LeftLabel("Id:"), 0, 1, 0, 1, AttachOptions.FILL, AttachOptions.FILL, 0, 0); + job_table.attach(job_id_label, 1, 2, 0, 1, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); + job_table.attach(new LeftLabel("State:"), 0, 1, 1, 2, AttachOptions.FILL, AttachOptions.FILL, 0, 0); + job_table.attach(job_state_label, 1, 2, 1, 2, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); + job_table.attach(new LeftLabel("Type:"), 0, 1, 2, 3, AttachOptions.FILL, AttachOptions.FILL, 0, 0); + job_table.attach(job_type_label, 1, 2, 2, 3, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0); + + ButtonBox bbox = new HButtonBox(); + bbox.set_layout(ButtonBoxStyle.START); + bbox.set_spacing(6); + unit_vbox.pack_start(bbox, false, true, 0); + + start_button = new Button.with_mnemonic("_Start"); + stop_button = new Button.with_mnemonic("Sto_p"); + reload_button = new Button.with_mnemonic("_Reload"); + restart_button = new Button.with_mnemonic("Res_tart"); + + start_button.clicked.connect(on_start); + stop_button.clicked.connect(on_stop); + reload_button.clicked.connect(on_reload); + restart_button.clicked.connect(on_restart); + + bbox.pack_start(start_button, false, true, 0); + bbox.pack_start(stop_button, false, true, 0); + bbox.pack_start(restart_button, false, true, 0); + bbox.pack_start(reload_button, false, true, 0); + + bbox = new HButtonBox(); + bbox.set_layout(ButtonBoxStyle.START); + bbox.set_spacing(6); + job_vbox.pack_start(bbox, false, true, 0); + + cancel_button = new Button.with_mnemonic("_Cancel"); + + cancel_button.clicked.connect(on_cancel); + + bbox.pack_start(cancel_button, false, true, 0); + + bus = Bus.get(BusType.SESSION); + + manager = bus.get_object ( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1") as Manager; + + clear_unit(); + populate_unit_model(); + populate_job_model(); + } + + public void populate_unit_model() throws DBus.Error { + unit_model.clear(); + + var list = manager.list_units(); + + foreach (var i in list) { + TreeIter iter; + + unit_model.append(out iter); + unit_model.set(iter, + 0, i.id, + 1, i.description, + 2, i.load_state, + 3, i.active_state, + 4, i.job_type != "" ? "→ %s".printf(i.job_type) : "", + 5, i.unit_path); + } + } + + public void populate_job_model() throws DBus.Error { + job_model.clear(); + + var list = manager.list_jobs(); + + foreach (var i in list) { + TreeIter iter; + + job_model.append(out iter); + job_model.set(iter, + 0, "%u".printf(i.id), + 1, i.name, + 2, "→ %s".printf(i.type), + 3, i.state, + 4, i.job_path); + } + } + + public Unit? get_current_unit() { + TreePath p; + unit_view.get_cursor(out p, null); + + if (p == null) + return null; + + TreeIter iter; + string path; + + unit_model.get_iter(out iter, p); + unit_model.get(iter, 5, out path); + + return bus.get_object ( + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Unit") as Unit; + } + + public void unit_changed() { + Unit u = get_current_unit(); + + if (u == null) + clear_unit(); + else + show_unit(u); + } + + public void clear_unit() { + start_button.set_sensitive(false); + stop_button.set_sensitive(false); + reload_button.set_sensitive(false); + restart_button.set_sensitive(false); + + unit_id_label.set_text_or_na(); + unit_description_label.set_text_or_na(); + unit_load_state_label.set_text_or_na(); + unit_active_state_label.set_text_or_na(); + unit_load_path_label.set_text_or_na(); + unit_active_enter_timestamp_label.set_text_or_na(); + unit_active_exit_timestamp_label.set_text_or_na(); + unit_can_reload_label.set_text_or_na(); + unit_can_start_label.set_text_or_na(); + } + + public void show_unit(Unit unit) { + unit_id_label.set_text_or_na(unit.id); + unit_description_label.set_text_or_na(unit.description); + unit_load_state_label.set_text_or_na(unit.load_state); + unit_active_state_label.set_text_or_na(unit.active_state); + unit_load_path_label.set_text_or_na(unit.load_path); + + uint64 t = unit.active_enter_timestamp; + if (t > 0) { + TimeVal tv = { (long) (t / 1000000), (long) (t % 1000000) }; + unit_active_enter_timestamp_label.set_text_or_na(tv.to_iso8601()); + } else + unit_active_enter_timestamp_label.set_text_or_na(); + + t = unit.active_exit_timestamp; + if (t > 0) { + TimeVal tv = { (long) (t / 1000000), (long) (t % 1000000) }; + unit_active_exit_timestamp_label.set_text_or_na(tv.to_iso8601()); + } else + unit_active_exit_timestamp_label.set_text_or_na(); + + bool b = unit.can_start; + start_button.set_sensitive(b); + stop_button.set_sensitive(b); + restart_button.set_sensitive(b); + unit_can_start_label.set_text_or_na(b ? "Yes" : "No"); + + b = unit.can_reload; + reload_button.set_sensitive(b); + unit_can_reload_label.set_text_or_na(b ? "Yes" : "No"); + } + + public Job? get_current_job() { + TreePath p; + job_view.get_cursor(out p, null); + + if (p == null) + return null; + + TreeIter iter; + string path; + + job_model.get_iter(out iter, p); + job_model.get(iter, 4, out path); + + return bus.get_object ( + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Job") as Job; + } + + public void job_changed() { + Job j = get_current_job(); + + if (j == null) + clear_job(); + else + show_job(j); + } + + public void clear_job() { + job_id_label.set_text_or_na(); + job_state_label.set_text_or_na(); + job_type_label.set_text_or_na(); + } + + public void show_job(Job job) { + job_id_label.set_text_or_na("%u".printf(job.id)); + job_state_label.set_text_or_na(job.state); + job_type_label.set_text_or_na(job.job_type); + } + + public void on_start() { + Unit u = get_current_unit(); + + if (u == null) + return; + + try { + u.start("replace"); + } catch (DBus.Error e) { + message("%s", e.message); + } + } + + public void on_stop() { + Unit u = get_current_unit(); + + if (u == null) + return; + + try { + u.stop("replace"); + } catch (DBus.Error e) { + message("%s", e.message); + } + } + + public void on_reload() { + Unit u = get_current_unit(); + + if (u == null) + return; + + try { + u.reload("replace"); + } catch (DBus.Error e) { + message("%s", e.message); + } + } + + public void on_restart() { + Unit u = get_current_unit(); + + if (u == null) + return; + + try { + u.restart("replace"); + } catch (DBus.Error e) { + message("%s", e.message); + } + } + + public void on_cancel() { + Job j = get_current_job(); + + if (j == null) + return; + + try { + j.cancel(); + } catch (DBus.Error e) { + message("%s", e.message); + } + } +} + +int main (string[] args) { + Gtk.init(ref args); + + try { + MainWindow window = new MainWindow(); + window.show_all(); + } catch (DBus.Error e) { + message("%s", e.message); + } + + Gtk.main(); + return 0; +} diff --git a/systemctl.vala b/systemctl.vala index abeb0ac7a9..cbde75dc7f 100644 --- a/systemctl.vala +++ b/systemctl.vala @@ -1,45 +1,15 @@ using DBus; using GLib; -[DBus (name = "org.freedesktop.systemd1")] -public interface Manager : DBus.Object { - - public struct UnitInfo { - string id; - string description; - string load_state; - string active_state; - ObjectPath unit_path; - uint32 job_id; - string job_type; - ObjectPath job_path; - } - - public struct JobInfo { - uint32 id; - string name; - string type; - string state; - ObjectPath job_path; - ObjectPath unit_path; - } - - public abstract UnitInfo[] ListUnits() throws DBus.Error; - public abstract JobInfo[] ListJobs() throws DBus.Error; - - public abstract ObjectPath LoadUnit(string name) throws DBus.Error; - - public abstract void ClearJobs() throws DBus.Error; -} - static string type = null; static bool all = false; +static bool replace = false; public static int job_info_compare(void* key1, void* key2) { Manager.JobInfo *j1 = (Manager.JobInfo*) key1; Manager.JobInfo *j2 = (Manager.JobInfo*) key2; - return Posix.strcmp(j1->name, j2->name); + return j1->id < j2->id ? -1 : (j1->id > j2->id ? 1 : 0); } public static int unit_info_compare(void* key1, void* key2) { @@ -54,8 +24,9 @@ public static int unit_info_compare(void* key1, void* key2) { } static const OptionEntry entries[] = { - { "type", 't', 0, OptionArg.STRING, out type, "List only particular type of units", "TYPE" }, - { "all", 'a', 0, OptionArg.NONE, out all, "Show all units, including dead ones", null }, + { "type", 't', 0, OptionArg.STRING, out type, "List only particular type of units", "TYPE" }, + { "all", 'a', 0, OptionArg.NONE, out all, "Show all units, including dead ones", null }, + { "replace", 0, 0, OptionArg.NONE, out replace, "When installing a new job, replace existing conflicting ones.", null }, { null } }; @@ -79,7 +50,7 @@ int main (string[] args) { "org.freedesktop.systemd1") as Manager; if (args[1] == "list-units" || args.length <= 1) { - var list = manager.ListUnits(); + var list = manager.list_units(); uint n = 0; Posix.qsort(list, list.length, sizeof(Manager.UnitInfo), unit_info_compare); @@ -109,19 +80,19 @@ int main (string[] args) { } else if (args[1] == "list-jobs") { - var list = manager.ListJobs(); + var list = manager.list_jobs(); Posix.qsort(list, list.length, sizeof(Manager.JobInfo), job_info_compare); - stdout.printf("%-45s %-17s %-7s\n", "UNIT", "TYPE", "STATE"); + stdout.printf("%4s %-45s %-17s %-7s\n", "JOB", "UNIT", "TYPE", "STATE"); foreach (var i in list) - stdout.printf("%-45s → %-15s %-7s\n", i.name, i.type, i.state); + stdout.printf("%4u %-45s → %-15s %-7s\n", i.id, i.name, i.type, i.state); stdout.printf("\n%u jobs listed.\n", list.length); } else if (args[1] == "clear-jobs") { - manager.ClearJobs(); + manager.clear_jobs(); } else if (args[1] == "load") { @@ -130,7 +101,65 @@ int main (string[] args) { return 1; } - manager.LoadUnit(args[2]); + for (uint i = 2; i < args.length; i++) + manager.load_unit(args[i]); + + } else if (args[1] == "cancel") { + + if (args.length < 3) { + stderr.printf("Missing argument.\n"); + return 1; + } + + for (uint i = 2; i < args.length; i++) { + uint32 id; + + if (args[i].scanf("%u", out id) != 1) { + stderr.printf("Failed to parse argument.\n"); + return 1; + } + + ObjectPath p = manager.get_job(id); + + Job j = bus.get_object ( + "org.freedesktop.systemd1", + p, + "org.freedesktop.systemd1.Job") as Job; + + j.cancel(); + } + + } else if (args[1] == "start" || + args[1] == "stop" || + args[1] == "reload" || + args[1] == "restart") { + + if (args.length < 3) { + stderr.printf("Missing argument.\n"); + return 1; + } + + for (uint i = 2; i < args.length; i++) { + + ObjectPath p = manager.get_unit(args[i]); + + Unit u = bus.get_object( + "org.freedesktop.systemd1", + p, + "org.freedesktop.systemd1.Unit") as Unit; + + string mode = replace ? "replace" : "fail"; + + if (args[1] == "start") + u.start(mode); + else if (args[1] == "stop") + u.stop(mode); + else if (args[1] == "restart") + u.restart(mode); + else if (args[1] == "reload") + u.reload(mode); + } + } else { stderr.printf("Unknown command %s.\n", args[1]); return 1; diff --git a/systemd-interfaces.vala b/systemd-interfaces.vala new file mode 100644 index 0000000000..bc54617ca0 --- /dev/null +++ b/systemd-interfaces.vala @@ -0,0 +1,61 @@ +using DBus; + +[DBus (name = "org.freedesktop.systemd1")] +public interface Manager : DBus.Object { + + public struct UnitInfo { + string id; + string description; + string load_state; + string active_state; + ObjectPath unit_path; + uint32 job_id; + string job_type; + ObjectPath job_path; + } + + public struct JobInfo { + uint32 id; + string name; + string type; + string state; + ObjectPath job_path; + ObjectPath unit_path; + } + + public abstract UnitInfo[] list_units() throws DBus.Error; + public abstract JobInfo[] list_jobs() throws DBus.Error; + + public abstract ObjectPath get_unit(string name) throws DBus.Error; + public abstract ObjectPath load_unit(string name) throws DBus.Error; + public abstract ObjectPath get_job(uint32 id) throws DBus.Error; + + public abstract void clear_jobs() throws DBus.Error; +} + +[DBus (name = "org.freedesktop.systemd1.Unit")] +public interface Unit : DBus.Object { + public abstract string id { owned get; } + public abstract string description { owned get; } + public abstract string load_state { owned get; } + public abstract string active_state { owned get; } + public abstract string load_path { owned get; } + public abstract uint64 active_enter_timestamp { owned get; } + public abstract uint64 active_exit_timestamp { owned get; } + public abstract bool can_reload { owned get; } + public abstract bool can_start { owned get; } + + public abstract ObjectPath start(string mode) throws DBus.Error; + public abstract ObjectPath stop(string mode) throws DBus.Error; + public abstract ObjectPath restart(string mode) throws DBus.Error; + public abstract ObjectPath reload(string mode) throws DBus.Error; +} + +[DBus (name = "org.freedesktop.systemd1.Job")] +public interface Job : DBus.Object { + public abstract uint32 id { owned get; } + public abstract string state { owned get; } + public abstract string job_type { owned get; } + + public abstract void cancel() throws DBus.Error; +} -- cgit v1.2.3-54-g00ecf From 47be870bd83fb3719dffc3ee9348a409ab762a14 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 3 Feb 2010 14:21:48 +0100 Subject: build: basic autoconfization --- .gitignore | 17 +++ Makefile | 53 ---------- Makefile.am | 112 ++++++++++++++++++++ bootstrap.sh | 80 ++++++++++++++ configure.ac | 75 ++++++++++++++ dbus-job.c | 2 +- dbus-manager.c | 44 ++++---- execute.c | 2 +- job.h | 2 + load-fragment.c | 26 ++--- logger.c | 8 +- m4/attributes.m4 | 311 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ ratelimit.h | 30 +++--- service.c | 22 ++-- service.h | 2 +- socket-util.c | 1 - timer.h | 2 +- unit.c | 10 +- util.c | 12 +-- 19 files changed, 677 insertions(+), 134 deletions(-) delete mode 100644 Makefile create mode 100644 Makefile.am create mode 100755 bootstrap.sh create mode 100644 configure.ac create mode 100644 m4/attributes.m4 (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 9c5193d27e..a1956d5d72 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,20 @@ systemctl.c systemd-interfaces.c systemadm systemadm.c +.deps/ +Makefile.in +aclocal.m4 +*.cache +compile +config.guess +config.h +config.h.in +config.log +config.status +config.sub +configure +depcomp +install-sh +missing +stamp-* +*.stamp diff --git a/Makefile b/Makefile deleted file mode 100644 index 41df4e29e3..0000000000 --- a/Makefile +++ /dev/null @@ -1,53 +0,0 @@ -CFLAGS=-Wall -Wextra -O0 -g -pipe -D_GNU_SOURCE -fdiagnostics-show-option -Wno-unused-parameter -DUNIT_PATH=\"/tmp/does/not/exist\" `pkg-config --cflags libudev dbus-1` -LIBS=-lrt -lcap `pkg-config --libs libudev dbus-1` - -COMMON= \ - unit.o \ - util.o \ - set.o \ - hashmap.o \ - strv.o \ - job.o \ - manager.o \ - conf-parser.o \ - load-fragment.o \ - socket-util.o \ - log.o \ - service.o \ - automount.o \ - mount.o \ - device.o \ - target.o \ - snapshot.o \ - socket.o \ - timer.o \ - load-dropin.o \ - execute.o \ - ratelimit.o \ - dbus.o \ - dbus-manager.o \ - dbus-unit.o \ - dbus-job.o - -all: systemd test-engine test-job-type systemd-logger systemctl systemadm - -systemd: main.o $(COMMON) - $(CC) $(CFLAGS) -o $@ $^ $(LIBS) - -systemd-logger: logger.o $(COMMON) - $(CC) $(CFLAGS) -o $@ $^ $(LIBS) - -test-engine: test-engine.o $(COMMON) - $(CC) $(CFLAGS) -o $@ $^ $(LIBS) - -test-job-type: test-job-type.o $(COMMON) - $(CC) $(CFLAGS) -o $@ $^ $(LIBS) - -systemctl: systemctl.vala - valac -g --save-temps systemctl.vala systemd-interfaces.vala --pkg=dbus-glib-1 --pkg=posix - -systemadm: systemadm.vala - valac -g --save-temps systemadm.vala systemd-interfaces.vala --pkg=dbus-glib-1 --pkg=posix --pkg gee-1.0 --pkg gtk+-2.0 - -clean: - rm -f *.o systemd test-engine systemctl systemadm diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000000..8ac2c2da4c --- /dev/null +++ b/Makefile.am @@ -0,0 +1,112 @@ +# This file is part of systemd. +# +# Copyright 2010 Lennart Poettering +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with systemd; If not, see . + +ACLOCAL_AMFLAGS = -I m4 + +AM_CPPFLAGS = \ + -include $(top_builddir)/config.h \ + -DUNIT_PATH=\"/tmp/does/not/exist\" + +sbin_PROGRAMS = \ + systemd + +bin_PROGRAMS = \ + systemctl \ + systemadm \ + systemd-logger + +noinst_PROGRAMS = \ + test-engine \ + test-job-type + +BASIC_SOURCES= \ + util.c \ + hashmap.c \ + set.c \ + strv.c \ + conf-parser.c \ + socket-util.c \ + log.c \ + ratelimit.c + +COMMON_SOURCES= \ + $(BASIC_SOURCES) \ + unit.c \ + job.c \ + manager.c \ + load-fragment.c \ + service.c \ + automount.c \ + mount.c \ + device.c \ + target.c \ + snapshot.c \ + socket.c \ + timer.c \ + load-dropin.c \ + execute.c \ + dbus.c \ + dbus-manager.c \ + dbus-unit.c \ + dbus-job.c + +systemd_SOURCES = \ + $(COMMON_SOURCES) \ + main.c + +systemd_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + $(DBUS_CFLAGS) \ + $(UDEV_CFLAGS) + +systemd_LDADD = \ + $(DBUS_LIBS) \ + $(UDEV_LIBS) + +test_engine_SOURCES = \ + $(COMMON_SOURCES) \ + test-engine.c + +test_engine_CPPFLAGS = $(systemd_CPPFLAGS) +test_engine_LDADD = $(systemd_LDADD) + +test_job_type_SOURCES = \ + $(COMMON_SOURCES) \ + test-engine.c + +test_job_type_CPPFLAGS = $(systemd_CPPFLAGS) +test_job_type_LDADD = $(systemd_LDADD) + +systemd_logger_SOURCES = \ + $(BASIC_SOURCES) \ + logger.c + +VALAFLAGS = -g --save-temps --pkg=dbus-glib-1 --pkg=posix --pkg gee-1.0 --pkg gtk+-2.0 + +systemctl_SOURCES = \ + systemctl.vala \ + systemd-interfaces.vala + +systemctl_CPPFLAGS = $(AM_CPPFLAGS) $(DBUSGLIB_CFLAGS) +systemctl_LDADD = $(DBUSGLIB_LIBS) + +systemadm_SOURCES = \ + systemadm.vala \ + systemd-interfaces.vala + +systemadm_CPPFLAGS = $(AM_CPPFLAGS) $(DBUSGLIB_CFLAGS) $(GTK_CFLAGS) +systemadm_LDADD = $(DBUSGLIB_LIBS) $(GTK_LIBS) diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 0000000000..73243ed440 --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +# This file is part of systemd. +# +# Copyright 2010 Lennart Poettering +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with systemd; If not, see . + +AM_VERSION=1.11 +AC_VERSION=2.63 + +run_versioned() { + local P + local V + + V=$(echo "$2" | sed -e 's,\.,,g') + + if [ -e "`which $1$V 2> /dev/null`" ] ; then + P="$1$V" + else + if [ -e "`which $1-$2 2> /dev/null`" ] ; then + P="$1-$2" + else + P="$1" + fi + fi + + shift 2 + "$P" "$@" +} + +set -ex + +if [ -f .git/hooks/pre-commit.sample -a ! -f .git/hooks/pre-commit ] ; then + cp -p .git/hooks/pre-commit.sample .git/hooks/pre-commit && \ + chmod +x .git/hooks/pre-commit && \ + echo "Activated pre-commit hook." +fi + +# We check for this here, because if pkg-config is not found in the +# system, it's likely that the pkg.m4 macro file is also not present, +# which will make PKG_PROG_PKG_CONFIG be undefined and the generated +# configure file faulty. +if ! pkg-config --version &>/dev/null; then + echo "pkg-config is required to bootstrap this program" &>/dev/null + exit 1 +fi + +if type -p colorgcc > /dev/null ; then + export CC=colorgcc +fi + +if [ "x$1" = "xam" ] ; then + run_versioned automake "$VERSION" -a -c --foreign + ./config.status +else + rm -rf autom4te.cache + rm -f config.cache + + run_versioned aclocal "$AM_VERSION" -I m4 + run_versioned autoconf "$AC_VERSION" -Wall + run_versioned autoheader "$AC_VERSION" + run_versioned automake "$AM_VERSION" --copy --foreign --add-missing + + if [ "x$1" != "xac" ]; then + CFLAGS="$CFLAGS -g -O0" ./configure --sysconfdir=/etc --localstatedir=/var "$@" + make clean + fi +fi diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000000..95a9ef5ca4 --- /dev/null +++ b/configure.ac @@ -0,0 +1,75 @@ +# This file is part of systemd. +# +# Copyright 2010 Lennart Poettering +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with systemd; If not, see . + +AC_PREREQ(2.63) + +AC_INIT([systemd],[0],[mzflfgrzq (at) 0pointer (dot) net]) +AC_CONFIG_SRCDIR([main.c]) +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_HEADERS([config.h]) + +AM_INIT_AUTOMAKE([foreign 1.11 -Wall -Wno-portability silent-rules tar-pax]) + +AC_SUBST(PACKAGE_URL, [http://git.0pointer.de/?p=systemd.git]) + +AC_CANONICAL_HOST + +AM_SILENT_RULES([yes]) + +AC_CHECK_PROG([STOW], [stow], [yes], [no]) + +AS_IF([test "x$STOW" = "xyes" && test -d /usr/local/stow], [ + AC_MSG_NOTICE([*** Found /usr/local/stow: default install prefix set to /usr/local/stow/${PACKAGE_NAME}-${PACKAGE_VERSION} ***]) + ac_default_prefix="/usr/local/stow/${PACKAGE_NAME}-${PACKAGE_VERSION}" +]) + +AC_PROG_CC +AC_PROG_CC_C99 +AM_PROG_CC_C_O +AC_PROG_GCC_TRADITIONAL +AC_USE_SYSTEM_EXTENSIONS + +CC_CHECK_CFLAGS_APPEND([-Wall -W -Wextra -pipe -Wno-long-long -Winline -Wvla -Wno-overlength-strings -Wunsafe-loop-optimizations -Wundef -Wformat=2 -Wlogical-op -Wsign-compare -Wformat-security -Wmissing-include-dirs -Wformat-nonliteral -Wold-style-definition -Wpointer-arith -Winit-self -Wdeclaration-after-statement -Wfloat-equal -Wmissing-prototypes -Wstrict-prototypes -Wredundant-decls -Wmissing-declarations -Wmissing-noreturn -Wshadow -Wendif-labels -Wcast-align -Wstrict-aliasing=2 -Wwrite-strings -Wno-unused-parameter -ffast-math -Wp,-D_FORTIFY_SOURCE=2 -fno-common -fdiagnostics-show-option -Wno-missing-field-initializers]) + +AC_SEARCH_LIBS([clock_gettime], [rt], [], [AC_MSG_ERROR([*** POSIX RT library not found])]) +AC_SEARCH_LIBS([cap_init], [cap], [], [AC_MSG_ERROR([*** POSIX caps library not found])]) +AC_CHECK_HEADERS([sys/capability.h], [], [AC_MSG_ERROR([*** POSIX caps headers not found])]) + +PKG_CHECK_MODULES(UDEV, [ libudev ]) +AC_SUBST(UDEV_CFLAGS) +AC_SUBST(UDEV_LIBS) + +PKG_CHECK_MODULES(DBUS, [ dbus-1 ]) +AC_SUBST(DBUS_CFLAGS) +AC_SUBST(DBUS_LIBS) + +PKG_CHECK_MODULES(DBUSGLIB, [ dbus-glib-1 ]) +AC_SUBST(DBUSGLIB_CFLAGS) +AC_SUBST(DBUSGLIB_LIBS) + +PKG_CHECK_MODULES(GEE, [ gee-1.0 ]) +AC_SUBST(GEE_CFLAGS) +AC_SUBST(GEE_LIBS) + +PKG_CHECK_MODULES(GTK, [ gtk+-2.0 ]) +AC_SUBST(GTK_CFLAGS) +AC_SUBST(GTK_LIBS) + +AM_PROG_VALAC() +AC_SUBST(VAPIDIR) + +AC_OUTPUT([Makefile]) diff --git a/dbus-job.c b/dbus-job.c index 70dbfd88eb..544868fc76 100644 --- a/dbus-job.c +++ b/dbus-job.c @@ -142,7 +142,7 @@ oom: return DBUS_HANDLER_RESULT_NEED_MEMORY; } -DBusHandlerResult bus_job_message_handler(DBusConnection *connection, DBusMessage *message, void *data) { +static DBusHandlerResult bus_job_message_handler(DBusConnection *connection, DBusMessage *message, void *data) { Manager *m = data; Job *j; int r; diff --git a/dbus-manager.c b/dbus-manager.c index 03a39300df..bdc82df070 100644 --- a/dbus-manager.c +++ b/dbus-manager.c @@ -54,7 +54,7 @@ #define INTROSPECTION_END \ "" -DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBusMessage *message, void *data) { +static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBusMessage *message, void *data) { int r; Manager *m = data; DBusError error; @@ -172,7 +172,7 @@ DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBusM goto oom; HASHMAP_FOREACH_KEY(u, k, m->units, i) { - char *unit_path, *job_path; + char *u_path, *j_path; const char *id, *description, *load_state, *active_state, *job_type; DBusMessageIter sub2; uint32_t job_id; @@ -188,21 +188,21 @@ DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBusM load_state = unit_load_state_to_string(u->meta.load_state); active_state = unit_active_state_to_string(unit_active_state(u)); - if (!(unit_path = unit_dbus_path(u))) + if (!(u_path = unit_dbus_path(u))) goto oom; if (u->meta.job) { job_id = (uint32_t) u->meta.job->id; - if (!(job_path = job_dbus_path(u->meta.job))) { - free(unit_path); + if (!(j_path = job_dbus_path(u->meta.job))) { + free(u_path); goto oom; } job_type = job_type_to_string(u->meta.job->type); } else { job_id = 0; - job_path = unit_path; + j_path = u_path; job_type = ""; } @@ -210,19 +210,19 @@ DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBusM !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &load_state) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &active_state) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_path) || + !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &u_path) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &job_id) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &job_type) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &job_path)) { - free(unit_path); + !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path)) { + free(u_path); if (u->meta.job) - free(job_path); + free(j_path); goto oom; } - free(unit_path); + free(u_path); if (u->meta.job) - free(job_path); + free(j_path); if (!dbus_message_iter_close_container(&sub, &sub2)) goto oom; @@ -245,7 +245,7 @@ DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBusM goto oom; HASHMAP_FOREACH(j, m->jobs, i) { - char *unit_path, *job_path; + char *u_path, *j_path; const char *unit, *state, *type; uint32_t id; DBusMessageIter sub2; @@ -258,11 +258,11 @@ DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBusM state = job_state_to_string(j->state); type = job_type_to_string(j->type); - if (!(job_path = job_dbus_path(j))) + if (!(j_path = job_dbus_path(j))) goto oom; - if (!(unit_path = unit_dbus_path(j->unit))) { - free(job_path); + if (!(u_path = unit_dbus_path(j->unit))) { + free(j_path); goto oom; } @@ -270,15 +270,15 @@ DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBusM !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &unit) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &type) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &state) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &job_path) || - !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_path)) { - free(job_path); - free(unit_path); + !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &j_path) || + !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &u_path)) { + free(j_path); + free(u_path); goto oom; } - free(job_path); - free(unit_path); + free(j_path); + free(u_path); if (!dbus_message_iter_close_container(&sub, &sub2)) goto oom; diff --git a/execute.c b/execute.c index 94866ebfa3..7bdf1b13b8 100644 --- a/execute.c +++ b/execute.c @@ -269,7 +269,7 @@ static int setup_output(const ExecContext *context, const char *ident) { } } -int setup_input(const ExecContext *context) { +static int setup_input(const ExecContext *context) { int r; assert(context); diff --git a/job.h b/job.h index 28ed07dbe4..554f9fc986 100644 --- a/job.h +++ b/job.h @@ -124,6 +124,8 @@ bool job_type_is_mergeable(JobType a, JobType b); bool job_type_is_superset(JobType a, JobType b); bool job_type_is_conflicting(JobType a, JobType b); +bool job_is_runnable(Job *j); + void job_schedule_run(Job *j); int job_run_and_invalidate(Job *j); int job_finish_and_invalidate(Job *j, bool success); diff --git a/load-fragment.c b/load-fragment.c index 3f1e578f3e..10972e43cd 100644 --- a/load-fragment.c +++ b/load-fragment.c @@ -472,7 +472,7 @@ static int config_parse_service_restart( return 0; } -int config_parse_bindtodevice( +static int config_parse_bindtodevice( const char *filename, unsigned line, const char *section, @@ -501,7 +501,7 @@ int config_parse_bindtodevice( return 0; } -int config_parse_output( +static int config_parse_output( const char *filename, unsigned line, const char *section, @@ -527,7 +527,7 @@ int config_parse_output( return 0; } -int config_parse_input( +static int config_parse_input( const char *filename, unsigned line, const char *section, @@ -553,7 +553,7 @@ int config_parse_input( return 0; } -int config_parse_facility( +static int config_parse_facility( const char *filename, unsigned line, const char *section, @@ -583,7 +583,7 @@ int config_parse_facility( return 0; } -int config_parse_level( +static int config_parse_level( const char *filename, unsigned line, const char *section, @@ -612,7 +612,7 @@ int config_parse_level( return 0; } -int config_parse_io_class( +static int config_parse_io_class( const char *filename, unsigned line, const char *section, @@ -643,7 +643,7 @@ int config_parse_io_class( return 0; } -int config_parse_io_priority( +static int config_parse_io_priority( const char *filename, unsigned line, const char *section, @@ -671,7 +671,7 @@ int config_parse_io_priority( return 0; } -int config_parse_cpu_sched_policy( +static int config_parse_cpu_sched_policy( const char *filename, unsigned line, const char *section, @@ -703,7 +703,7 @@ int config_parse_cpu_sched_policy( return 0; } -int config_parse_cpu_sched_prio( +static int config_parse_cpu_sched_prio( const char *filename, unsigned line, const char *section, @@ -732,7 +732,7 @@ int config_parse_cpu_sched_prio( return 0; } -int config_parse_cpu_affinity( +static int config_parse_cpu_affinity( const char *filename, unsigned line, const char *section, @@ -775,7 +775,7 @@ int config_parse_cpu_affinity( return 0; } -int config_parse_capabilities( +static int config_parse_capabilities( const char *filename, unsigned line, const char *section, @@ -807,7 +807,7 @@ int config_parse_capabilities( return 0; } -int config_parse_secure_bits( +static int config_parse_secure_bits( const char *filename, unsigned line, const char *section, @@ -848,7 +848,7 @@ int config_parse_secure_bits( return 0; } -int config_parse_bounding_set( +static int config_parse_bounding_set( const char *filename, unsigned line, const char *section, diff --git a/logger.c b/logger.c index 311458bd26..af3293bd7e 100644 --- a/logger.c +++ b/logger.c @@ -155,7 +155,7 @@ static int stream_log(Stream *s, char *p, usec_t timestamp) { IOVEC_SET_STRING(iovec[1], s->process); IOVEC_SET_STRING(iovec[2], header_pid); IOVEC_SET_STRING(iovec[3], p); - IOVEC_SET_STRING(iovec[4], "\n"); + IOVEC_SET_STRING(iovec[4], (char*) "\n"); if (writev(s->server->kmsg_fd, iovec, ELEMENTSOF(iovec)) < 0) return -errno; @@ -548,9 +548,9 @@ int main(int argc, char *argv[]) { for (;;) { struct epoll_event event; - int n; + int k; - if ((n = epoll_wait(server.epoll_fd, + if ((k = epoll_wait(server.epoll_fd, &event, 1, server.n_streams <= 0 ? TIMEOUT : -1)) < 0) { @@ -561,7 +561,7 @@ int main(int argc, char *argv[]) { goto fail; } - if (n <= 0) + if (k <= 0) break; if ((r = process_event(&server, &event)) < 0) diff --git a/m4/attributes.m4 b/m4/attributes.m4 new file mode 100644 index 0000000000..3bf96a304b --- /dev/null +++ b/m4/attributes.m4 @@ -0,0 +1,311 @@ +dnl Macros to check the presence of generic (non-typed) symbols. +dnl Copyright (c) 2006-2008 Diego Pettenò +dnl Copyright (c) 2006-2008 xine project +dnl +dnl This program is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 2, or (at your option) +dnl any later version. +dnl +dnl This program is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program; if not, write to the Free Software +dnl Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +dnl 02110-1301, USA. +dnl +dnl As a special exception, the copyright owners of the +dnl macro gives unlimited permission to copy, distribute and modify the +dnl configure scripts that are the output of Autoconf when processing the +dnl Macro. You need not follow the terms of the GNU General Public +dnl License when using or distributing such scripts, even though portions +dnl of the text of the Macro appear in them. The GNU General Public +dnl License (GPL) does govern all other use of the material that +dnl constitutes the Autoconf Macro. +dnl +dnl This special exception to the GPL applies to versions of the +dnl Autoconf Macro released by this project. When you make and +dnl distribute a modified version of the Autoconf Macro, you may extend +dnl this special exception to the GPL to apply to your modified version as +dnl well. + +dnl Check if the flag is supported by compiler +dnl CC_CHECK_CFLAGS_SILENT([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND]) + +AC_DEFUN([CC_CHECK_CFLAGS_SILENT], [ + AC_CACHE_VAL(AS_TR_SH([cc_cv_cflags_$1]), + [ac_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $1" + AC_COMPILE_IFELSE([int a;], + [eval "AS_TR_SH([cc_cv_cflags_$1])='yes'"], + [eval "AS_TR_SH([cc_cv_cflags_$1])='no'"]) + CFLAGS="$ac_save_CFLAGS" + ]) + + AS_IF([eval test x$]AS_TR_SH([cc_cv_cflags_$1])[ = xyes], + [$2], [$3]) +]) + +dnl Check if the flag is supported by compiler (cacheable) +dnl CC_CHECK_CFLAGS([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND]) + +AC_DEFUN([CC_CHECK_CFLAGS], [ + AC_CACHE_CHECK([if $CC supports $1 flag], + AS_TR_SH([cc_cv_cflags_$1]), + CC_CHECK_CFLAGS_SILENT([$1]) dnl Don't execute actions here! + ) + + AS_IF([eval test x$]AS_TR_SH([cc_cv_cflags_$1])[ = xyes], + [$2], [$3]) +]) + +dnl CC_CHECK_CFLAG_APPEND(FLAG, [action-if-found], [action-if-not-found]) +dnl Check for CFLAG and appends them to CFLAGS if supported +AC_DEFUN([CC_CHECK_CFLAG_APPEND], [ + AC_CACHE_CHECK([if $CC supports $1 flag], + AS_TR_SH([cc_cv_cflags_$1]), + CC_CHECK_CFLAGS_SILENT([$1]) dnl Don't execute actions here! + ) + + AS_IF([eval test x$]AS_TR_SH([cc_cv_cflags_$1])[ = xyes], + [CFLAGS="$CFLAGS $1"; DEBUG_CFLAGS="$DEBUG_CFLAGS $1"; $2], [$3]) +]) + +dnl CC_CHECK_CFLAGS_APPEND([FLAG1 FLAG2], [action-if-found], [action-if-not]) +AC_DEFUN([CC_CHECK_CFLAGS_APPEND], [ + for flag in $1; do + CC_CHECK_CFLAG_APPEND($flag, [$2], [$3]) + done +]) + +dnl Check if the flag is supported by linker (cacheable) +dnl CC_CHECK_LDFLAGS([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND]) + +AC_DEFUN([CC_CHECK_LDFLAGS], [ + AC_CACHE_CHECK([if $CC supports $1 flag], + AS_TR_SH([cc_cv_ldflags_$1]), + [ac_save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $1" + AC_LINK_IFELSE([int main() { return 1; }], + [eval "AS_TR_SH([cc_cv_ldflags_$1])='yes'"], + [eval "AS_TR_SH([cc_cv_ldflags_$1])="]) + LDFLAGS="$ac_save_LDFLAGS" + ]) + + AS_IF([eval test x$]AS_TR_SH([cc_cv_ldflags_$1])[ = xyes], + [$2], [$3]) +]) + +dnl define the LDFLAGS_NOUNDEFINED variable with the correct value for +dnl the current linker to avoid undefined references in a shared object. +AC_DEFUN([CC_NOUNDEFINED], [ + dnl We check $host for which systems to enable this for. + AC_REQUIRE([AC_CANONICAL_HOST]) + + case $host in + dnl FreeBSD (et al.) does not complete linking for shared objects when pthreads + dnl are requested, as different implementations are present; to avoid problems + dnl use -Wl,-z,defs only for those platform not behaving this way. + *-freebsd* | *-openbsd*) ;; + *) + dnl First of all check for the --no-undefined variant of GNU ld. This allows + dnl for a much more readable commandline, so that people can understand what + dnl it does without going to look for what the heck -z defs does. + for possible_flags in "-Wl,--no-undefined" "-Wl,-z,defs"; do + CC_CHECK_LDFLAGS([$possible_flags], [LDFLAGS_NOUNDEFINED="$possible_flags"]) + break + done + ;; + esac + + AC_SUBST([LDFLAGS_NOUNDEFINED]) +]) + +dnl Check for a -Werror flag or equivalent. -Werror is the GCC +dnl and ICC flag that tells the compiler to treat all the warnings +dnl as fatal. We usually need this option to make sure that some +dnl constructs (like attributes) are not simply ignored. +dnl +dnl Other compilers don't support -Werror per se, but they support +dnl an equivalent flag: +dnl - Sun Studio compiler supports -errwarn=%all +AC_DEFUN([CC_CHECK_WERROR], [ + AC_CACHE_CHECK( + [for $CC way to treat warnings as errors], + [cc_cv_werror], + [CC_CHECK_CFLAGS_SILENT([-Werror], [cc_cv_werror=-Werror], + [CC_CHECK_CFLAGS_SILENT([-errwarn=%all], [cc_cv_werror=-errwarn=%all])]) + ]) +]) + +AC_DEFUN([CC_CHECK_ATTRIBUTE], [ + AC_REQUIRE([CC_CHECK_WERROR]) + AC_CACHE_CHECK([if $CC supports __attribute__(( ifelse([$2], , [$1], [$2]) ))], + AS_TR_SH([cc_cv_attribute_$1]), + [ac_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $cc_cv_werror" + AC_COMPILE_IFELSE([$3], + [eval "AS_TR_SH([cc_cv_attribute_$1])='yes'"], + [eval "AS_TR_SH([cc_cv_attribute_$1])='no'"]) + CFLAGS="$ac_save_CFLAGS" + ]) + + AS_IF([eval test x$]AS_TR_SH([cc_cv_attribute_$1])[ = xyes], + [AC_DEFINE( + AS_TR_CPP([SUPPORT_ATTRIBUTE_$1]), 1, + [Define this if the compiler supports __attribute__(( ifelse([$2], , [$1], [$2]) ))] + ) + $4], + [$5]) +]) + +AC_DEFUN([CC_ATTRIBUTE_CONSTRUCTOR], [ + CC_CHECK_ATTRIBUTE( + [constructor],, + [void __attribute__((constructor)) ctor() { int a; }], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_FORMAT], [ + CC_CHECK_ATTRIBUTE( + [format], [format(printf, n, n)], + [void __attribute__((format(printf, 1, 2))) printflike(const char *fmt, ...) { fmt = (void *)0; }], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_FORMAT_ARG], [ + CC_CHECK_ATTRIBUTE( + [format_arg], [format_arg(printf)], + [char *__attribute__((format_arg(1))) gettextlike(const char *fmt) { fmt = (void *)0; }], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_VISIBILITY], [ + CC_CHECK_ATTRIBUTE( + [visibility_$1], [visibility("$1")], + [void __attribute__((visibility("$1"))) $1_function() { }], + [$2], [$3]) +]) + +AC_DEFUN([CC_ATTRIBUTE_NONNULL], [ + CC_CHECK_ATTRIBUTE( + [nonnull], [nonnull()], + [void __attribute__((nonnull())) some_function(void *foo, void *bar) { foo = (void*)0; bar = (void*)0; }], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_UNUSED], [ + CC_CHECK_ATTRIBUTE( + [unused], , + [void some_function(void *foo, __attribute__((unused)) void *bar);], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_SENTINEL], [ + CC_CHECK_ATTRIBUTE( + [sentinel], , + [void some_function(void *foo, ...) __attribute__((sentinel));], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_DEPRECATED], [ + CC_CHECK_ATTRIBUTE( + [deprecated], , + [void some_function(void *foo, ...) __attribute__((deprecated));], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_ALIAS], [ + CC_CHECK_ATTRIBUTE( + [alias], [weak, alias], + [void other_function(void *foo) { } + void some_function(void *foo) __attribute__((weak, alias("other_function")));], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_MALLOC], [ + CC_CHECK_ATTRIBUTE( + [malloc], , + [void * __attribute__((malloc)) my_alloc(int n);], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_PACKED], [ + CC_CHECK_ATTRIBUTE( + [packed], , + [struct astructure { char a; int b; long c; void *d; } __attribute__((packed));], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_CONST], [ + CC_CHECK_ATTRIBUTE( + [const], , + [int __attribute__((const)) twopow(int n) { return 1 << n; } ], + [$1], [$2]) +]) + +AC_DEFUN([CC_FLAG_VISIBILITY], [ + AC_REQUIRE([CC_CHECK_WERROR]) + AC_CACHE_CHECK([if $CC supports -fvisibility=hidden], + [cc_cv_flag_visibility], + [cc_flag_visibility_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $cc_cv_werror" + CC_CHECK_CFLAGS_SILENT([-fvisibility=hidden], + cc_cv_flag_visibility='yes', + cc_cv_flag_visibility='no') + CFLAGS="$cc_flag_visibility_save_CFLAGS"]) + + AS_IF([test "x$cc_cv_flag_visibility" = "xyes"], + [AC_DEFINE([SUPPORT_FLAG_VISIBILITY], 1, + [Define this if the compiler supports the -fvisibility flag]) + $1], + [$2]) +]) + +AC_DEFUN([CC_FUNC_EXPECT], [ + AC_REQUIRE([CC_CHECK_WERROR]) + AC_CACHE_CHECK([if compiler has __builtin_expect function], + [cc_cv_func_expect], + [ac_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $cc_cv_werror" + AC_COMPILE_IFELSE( + [int some_function() { + int a = 3; + return (int)__builtin_expect(a, 3); + }], + [cc_cv_func_expect=yes], + [cc_cv_func_expect=no]) + CFLAGS="$ac_save_CFLAGS" + ]) + + AS_IF([test "x$cc_cv_func_expect" = "xyes"], + [AC_DEFINE([SUPPORT__BUILTIN_EXPECT], 1, + [Define this if the compiler supports __builtin_expect() function]) + $1], + [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_ALIGNED], [ + AC_REQUIRE([CC_CHECK_WERROR]) + AC_CACHE_CHECK([highest __attribute__ ((aligned ())) supported], + [cc_cv_attribute_aligned], + [ac_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $cc_cv_werror" + for cc_attribute_align_try in 64 32 16 8 4 2; do + AC_COMPILE_IFELSE([ + int main() { + static char c __attribute__ ((aligned($cc_attribute_align_try))) = 0; + return c; + }], [cc_cv_attribute_aligned=$cc_attribute_align_try; break]) + done + CFLAGS="$ac_save_CFLAGS" + ]) + + if test "x$cc_cv_attribute_aligned" != "x"; then + AC_DEFINE_UNQUOTED([ATTRIBUTE_ALIGNED_MAX], [$cc_cv_attribute_aligned], + [Define the highest alignment supported]) + fi +]) diff --git a/ratelimit.h b/ratelimit.h index 963b3675bc..f61a10a91d 100644 --- a/ratelimit.h +++ b/ratelimit.h @@ -31,23 +31,23 @@ typedef struct RateLimit { usec_t begin; } RateLimit; -#define RATELIMIT_DEFINE(_name, _interval, _burst) \ - RateLimit _name = { \ - .interval = (_interval), \ - .burst = (_burst), \ - .n_printed = 0, \ - .n_missed = 0, \ - .begin = 0 \ +#define RATELIMIT_DEFINE(_name, _interval, _burst) \ + RateLimit _name = { \ + .interval = (_interval), \ + .burst = (_burst), \ + .n_printed = 0, \ + .n_missed = 0, \ + .begin = 0 \ } -#define RATELIMIT_INIT(v, _interval, _burst) \ - do { \ - RateLimit *r = &(v); \ - r->interval = (_interval); \ - r->burst = (_burst); \ - r->n_printed = 0; \ - r->n_missed = 0; \ - r->begin = 0; \ +#define RATELIMIT_INIT(v, _interval, _burst) \ + do { \ + RateLimit *_r = &(v); \ + _r->interval = (_interval); \ + _r->burst = (_burst); \ + _r->n_printed = 0; \ + _r->n_missed = 0; \ + _r->begin = 0; \ } while (false); bool ratelimit_test(RateLimit *r); diff --git a/service.c b/service.c index 2427f20973..67950d4e64 100644 --- a/service.c +++ b/service.c @@ -130,13 +130,13 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { ServiceExecCommand c; Service *s = SERVICE(u); - char *prefix2; + const char *prefix2; + char *p2; assert(s); - prefix2 = strappend(prefix, "\t"); - if (!prefix2) - prefix2 = ""; + p2 = strappend(prefix, "\t"); + prefix2 = p2 ? p2 : prefix; fprintf(f, "%sService State: %s\n", @@ -161,7 +161,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { exec_command_dump_list(s->exec_command[c], f, prefix2); } - free(prefix2); + free(p2); } static int service_load_pid_file(Service *s) { @@ -243,7 +243,7 @@ fail: static int service_notify_sockets(Service *s) { Iterator i; Set *set; - Socket *socket; + Socket *sock; int r; assert(s); @@ -253,8 +253,8 @@ static int service_notify_sockets(Service *s) { if ((r = service_get_sockets(s, &set)) < 0) return r; - SET_FOREACH(socket, set, i) - socket_notify_service_dead(socket); + SET_FOREACH(sock, set, i) + socket_notify_service_dead(sock); set_free(set); @@ -338,7 +338,7 @@ static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) { int *rfds = NULL; unsigned rn_fds = 0; Set *set; - Socket *socket; + Socket *sock; assert(s); assert(fds); @@ -347,11 +347,11 @@ static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) { if ((r = service_get_sockets(s, &set)) < 0) return r; - SET_FOREACH(socket, set, i) { + SET_FOREACH(sock, set, i) { int *cfds; unsigned cn_fds; - if ((r = socket_collect_fds(socket, &cfds, &cn_fds)) < 0) + if ((r = socket_collect_fds(sock, &cfds, &cn_fds)) < 0) goto fail; if (!cfds) diff --git a/service.h b/service.h index c8e41ba264..fef100bad9 100644 --- a/service.h +++ b/service.h @@ -102,7 +102,7 @@ struct Service { RateLimit ratelimit; }; -const UnitVTable service_vtable; +extern const UnitVTable service_vtable; const char* service_state_to_string(ServiceState i); ServiceState service_state_from_string(const char *s); diff --git a/socket-util.c b/socket-util.c index c8e8e5975b..0f5140ec5b 100644 --- a/socket-util.c +++ b/socket-util.c @@ -103,7 +103,6 @@ int socket_address_parse(SocketAddress *a, const char *s) { } else { if ((e = strchr(s, ':'))) { - int r; if ((r = safe_atou(e+1, &u)) < 0) return r; diff --git a/timer.h b/timer.h index 5182020125..0ec3e0d9bb 100644 --- a/timer.h +++ b/timer.h @@ -44,6 +44,6 @@ struct Timer { Service *service; }; -const UnitVTable timer_vtable; +extern const UnitVTable timer_vtable; #endif diff --git a/unit.c b/unit.c index bdbcb45c50..a14e18ef5b 100644 --- a/unit.c +++ b/unit.c @@ -351,15 +351,15 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { char *t; UnitDependency d; Iterator i; - char *prefix2; + char *p2; + const char *prefix2; assert(u); if (!prefix) prefix = ""; - prefix2 = strappend(prefix, "\t"); - if (!prefix2) - prefix2 = ""; + p2 = strappend(prefix, "\t"); + prefix2 = p2 ? p2 : prefix; fprintf(f, "%s→ Unit %s:\n" @@ -397,7 +397,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { if (u->meta.job) job_dump(u->meta.job, f, prefix2); - free(prefix2); + free(p2); } /* Common implementation for multiple backends */ diff --git a/util.c b/util.c index 33b143d3bb..bf74695854 100644 --- a/util.c +++ b/util.c @@ -36,10 +36,10 @@ #include "ioprio.h" #include "missing.h" -usec_t now(clockid_t clock) { +usec_t now(clockid_t clock_id) { struct timespec ts; - assert_se(clock_gettime(clock, &ts) == 0); + assert_se(clock_gettime(clock_id, &ts) == 0); return timespec_load(&ts); } @@ -824,11 +824,11 @@ char *xescape(const char *s, const char *bad) { } char *bus_path_escape(const char *s) { - assert(s); - char *r, *t; const char *f; + assert(s); + /* Escapes all chars that D-Bus' object path cannot deal * with. Can be reverse with bus_path_unescape() */ @@ -853,11 +853,11 @@ char *bus_path_escape(const char *s) { } char *bus_path_unescape(const char *s) { - assert(s); - char *r, *t; const char *f; + assert(s); + if (!(r = new(char, strlen(s)+1))) return NULL; -- cgit v1.2.3-54-g00ecf From 380bc8790dd284bbf967add222fa90d117ed50ea Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 13 Feb 2010 01:17:29 +0100 Subject: git: ignore Makefile --- .gitignore | 1 + 1 file changed, 1 insertion(+) (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index a1956d5d72..581edaed97 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ install-sh missing stamp-* *.stamp +Makefile -- cgit v1.2.3-54-g00ecf From 8e27452380193a5f81bfd08a59aab8b07008ba0b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 31 Mar 2010 16:29:55 +0200 Subject: cgroup: add cgroupsification --- .gitignore | 1 + Makefile.am | 64 +++++- cgroup.c | 523 ++++++++++++++++++++++++++++++++++++++++++++++++ cgroup.h | 83 ++++++++ cgroups-agent.c | 72 +++++++ configure.ac | 4 + dbus.c | 137 +++++++++++-- execute.c | 15 +- execute.h | 6 +- load-fragment.c | 35 +++- main.c | 4 +- manager.c | 38 +++- manager.h | 10 +- mount-setup.c | 112 +++++++++++ mount-setup.h | 27 +++ service.c | 55 ++++- service.h | 1 + socket.c | 12 +- test-engine.c | 2 +- test1/exec-demo.service | 6 +- unit.c | 140 ++++++++++++- unit.h | 11 + util.c | 2 +- 23 files changed, 1314 insertions(+), 46 deletions(-) create mode 100644 cgroup.c create mode 100644 cgroup.h create mode 100644 cgroups-agent.c create mode 100644 mount-setup.c create mode 100644 mount-setup.h (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 581edaed97..06ef3d0323 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-cgroups-agent systemd *.o test-engine diff --git a/Makefile.am b/Makefile.am index 3fc221c7f0..57a2dd98dd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -25,15 +25,19 @@ AM_CPPFLAGS = \ -DSYSTEM_DATA_UNIT_PATH=\"$(pkgdatadir)/system\" \ -DSYSTEM_SYSVINIT_PATH=\"$(sysconfdir)/init.d\" \ -DSESSION_CONFIG_UNIT_PATH=\"$(pkgsysconfdir)/session\" \ - -DSESSION_DATA_UNIT_PATH=\"$(pkgdatadir)/session\" + -DSESSION_DATA_UNIT_PATH=\"$(pkgdatadir)/session\" \ + -DCGROUP_AGENT_PATH=\"$(pkglibexecdir)/systemd-cgroups-agent\" sbin_PROGRAMS = \ systemd bin_PROGRAMS = \ systemctl \ - systemadm \ - systemd-logger + systemadm + +pkglibexec_PROGRAMS = \ + systemd-logger \ + systemd-cgroups-agent noinst_PROGRAMS = \ test-engine \ @@ -41,34 +45,64 @@ noinst_PROGRAMS = \ BASIC_SOURCES= \ util.c \ + util.h \ hashmap.c \ + hashmap.h \ set.c \ + set.h \ strv.c \ + strv.h \ conf-parser.c \ + conf-parser.h \ socket-util.c \ + socket-util.h \ log.c \ - ratelimit.c + log.h \ + ratelimit.c \ + ratelimit.h COMMON_SOURCES= \ $(BASIC_SOURCES) \ unit.c \ + unit.h \ job.c \ + job.h \ manager.c \ + manager.h \ load-fragment.c \ + load-fragment.h \ service.c \ + service.h \ automount.c \ + automount.h \ mount.c \ + mount.h \ device.c \ + device.h \ target.c \ + target.h \ snapshot.c \ + snapshot.h \ socket.c \ + socket.h \ timer.c \ + timer.h \ load-dropin.c \ + load-dropin.h \ execute.c \ + execute.h \ dbus.c \ + dbus.h \ dbus-manager.c \ + dbus-manager.h \ dbus-unit.c \ - dbus-job.c + dbus-unit.h \ + dbus-job.c \ + dbus-job.h \ + cgroup.c \ + cgroup.h \ + mount-setup.c \ + mount-setup.h systemd_SOURCES = \ $(COMMON_SOURCES) \ @@ -77,11 +111,13 @@ systemd_SOURCES = \ systemd_CPPFLAGS = \ $(AM_CPPFLAGS) \ $(DBUS_CFLAGS) \ - $(UDEV_CFLAGS) + $(UDEV_CFLAGS) \ + $(CGROUP_CFLAGS) systemd_LDADD = \ $(DBUS_LIBS) \ - $(UDEV_LIBS) + $(UDEV_LIBS) \ + $(CGROUP_LIBS) test_engine_SOURCES = \ $(COMMON_SOURCES) \ @@ -101,6 +137,17 @@ systemd_logger_SOURCES = \ $(BASIC_SOURCES) \ logger.c +systemd_cgroups_agent_SOURCES = \ + $(BASIC_SOURCES) \ + cgroups-agent.c + +systemd_cgroups_agent_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + $(DBUS_CFLAGS) + +systemd_cgroups_agent_LDADD = \ + $(DBUS_LIBS) + VALAFLAGS = -g --save-temps --pkg=dbus-glib-1 --pkg=posix --pkg gee-1.0 --pkg gtk+-2.0 systemctl_SOURCES = \ @@ -120,4 +167,5 @@ systemadm_LDADD = $(DBUSGLIB_LIBS) $(GTK_LIBS) CLEANFILES = \ systemd-interfaces.c \ systemctl.c \ - systemadm.c + systemadm.c \ + systemd-cgroups-agent diff --git a/cgroup.c b/cgroup.c new file mode 100644 index 0000000000..04736accea --- /dev/null +++ b/cgroup.c @@ -0,0 +1,523 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include "cgroup.h" +#include "log.h" + +static int translate_error(int error, int _errno) { + + switch (error) { + + case ECGROUPNOTCOMPILED: + case ECGROUPNOTMOUNTED: + case ECGROUPNOTEXIST: + case ECGROUPNOTCREATED: + return -ENOENT; + + case ECGINVAL: + return -EINVAL; + + case ECGROUPNOTALLOWED: + return -EPERM; + + case ECGOTHER: + return -_errno; + } + + return -EIO; +} + +int cgroup_bonding_realize(CGroupBonding *b) { + int r; + + assert(b); + assert(b->path); + assert(b->controller); + + if (b->cgroup) + return 0; + + if (!(b->cgroup = cgroup_new_cgroup(b->path))) + return -ENOMEM; + + if (!cgroup_add_controller(b->cgroup, b->controller)) { + r = -ENOMEM; + goto fail; + } + + if (b->inherit) + r = cgroup_create_cgroup_from_parent(b->cgroup, true); + else + r = cgroup_create_cgroup(b->cgroup, true); + + if (r != 0) { + r = translate_error(r, errno); + goto fail; + } + + return 0; + +fail: + cgroup_free(&b->cgroup); + b->cgroup = NULL; + return r; +} + +int cgroup_bonding_realize_list(CGroupBonding *first) { + CGroupBonding *b; + + LIST_FOREACH(by_unit, b, first) { + int r; + + if ((r = cgroup_bonding_realize(b)) < 0) + return r; + } + + return 0; +} + +void cgroup_bonding_free(CGroupBonding *b) { + assert(b); + + if (b->unit) { + CGroupBonding *f; + + LIST_REMOVE(CGroupBonding, by_unit, b->unit->meta.cgroup_bondings, b); + + assert_se(f = hashmap_get(b->unit->meta.manager->cgroup_bondings, b->path)); + LIST_REMOVE(CGroupBonding, by_path, f, b); + + if (f) + hashmap_replace(b->unit->meta.manager->cgroup_bondings, b->path, f); + else + hashmap_remove(b->unit->meta.manager->cgroup_bondings, b->path); + } + + free(b->controller); + free(b->path); + + if (b->cgroup) { + + if (b->only_us && b->clean_up) + cgroup_delete_cgroup(b->cgroup, true); + + cgroup_free(&b->cgroup); + } + + free(b); +} + +void cgroup_bonding_free_list(CGroupBonding *first) { + CGroupBonding *b, *n; + + LIST_FOREACH_SAFE(by_unit, b, n, first) + cgroup_bonding_free(b); +} + +int cgroup_bonding_install(CGroupBonding *b, pid_t pid) { + int r; + + assert(b); + assert(pid >= 0); + + if (pid == 0) + pid = getpid(); + + if (!b->cgroup) + return -ENOENT; + + if ((r = cgroup_attach_task_pid(b->cgroup, pid))) + return translate_error(r, errno); + + return 0; +} + +int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid) { + CGroupBonding *b; + + LIST_FOREACH(by_unit, b, first) { + int r; + + if ((r = cgroup_bonding_install(b, pid)) < 0) + return r; + } + + return 0; +} + +int cgroup_bonding_kill(CGroupBonding *b, int sig) { + int r; + Set *s; + bool done; + + assert(b); + assert(sig > 0); + + if (!(s = set_new(trivial_hash_func, trivial_compare_func))) + return -ENOMEM; + + do { + void *iterator; + pid_t pid; + + done = true; + + if ((r = cgroup_get_task_begin(b->path, b->controller, &iterator, &pid)) != 0) { + if (r == ECGEOF) { + r = 0; + goto kill_done; + } else { + r = translate_error(r, errno); + break; + } + } + + for (;;) { + if (set_get(s, INT_TO_PTR(pid)) != INT_TO_PTR(pid)) { + + /* If we haven't killed this process + * yet, kill it */ + + if (kill(pid, sig) < 0 && errno != ESRCH) { + r = -errno; + break; + } + + done = false; + + if ((r = set_put(s, INT_TO_PTR(pid))) < 0) + break; + } + + if ((r = cgroup_get_task_next(&iterator, &pid)) != 0) { + + if (r == ECGEOF) + r = 0; + else + r = translate_error(r, errno); + + break; + } + } + + kill_done: + assert_se(cgroup_get_task_end(&iterator) == 0); + + /* To avoid racing against processes which fork + * quicker than we can kill them we repeat this until + * no new pids need to be killed. */ + + } while (!done && r >= 0); + + set_free(s); + return r; +} + +int cgroup_bonding_kill_list(CGroupBonding *first, int sig) { + CGroupBonding *b; + + LIST_FOREACH(by_unit, b, first) { + int r; + + if ((r = cgroup_bonding_kill(b, sig)) < 0) + return r; + } + + return 0; +} + +/* Returns 1 if the group is empty, 0 if it is not, -EAGAIN if we + * cannot know */ +int cgroup_bonding_is_empty(CGroupBonding *b) { + void *iterator; + pid_t pid; + int r; + + assert(b); + + r = cgroup_get_task_begin(b->path, b->controller, &iterator, &pid); + + if (r == 0 || r == ECGEOF) + cgroup_get_task_end(&iterator); + + /* Hmm, no PID in this group? Then it is definitely empty */ + if (r == ECGEOF) + return 1; + + /* Some error? Let's return it */ + if (r != 0) + return translate_error(r, errno); + + /* It's not empty, and we are the only user, then it is + * definitely not empty */ + if (b->only_us) + return 0; + + /* There are PIDs in the group but we aren't the only users, + * hence we cannot say */ + return -EAGAIN; +} + +int cgroup_bonding_is_empty_list(CGroupBonding *first) { + CGroupBonding *b; + + LIST_FOREACH(by_unit, b, first) { + int r; + + if ((r = cgroup_bonding_is_empty(b)) < 0) { + /* If this returned -EAGAIN, then we don't know if the + * group is empty, so let's see if another group can + * tell us */ + + if (r != -EAGAIN) + return r; + } else + return r; + } + + return -EAGAIN; +} + +static int install_release_agent(Manager *m, const char *mount_point) { + char *p, *c, *sc; + int r; + + assert(m); + assert(mount_point); + + if (asprintf(&p, "%s/release_agent", mount_point) < 0) + return -ENOMEM; + + if ((r = read_one_line_file(p, &c)) < 0) { + free(p); + return r; + } + + sc = strstrip(c); + + if (sc[0] == 0) { + if ((r = write_one_line_file(p, CGROUP_AGENT_PATH "\n" )) < 0) { + free(p); + free(c); + return r; + } + } else if (!streq(sc, CGROUP_AGENT_PATH)) { + free(p); + free(c); + return -EEXIST; + } + + free(c); + free(p); + + if (asprintf(&p, "%s/notify_on_release", mount_point) < 0) + return -ENOMEM; + + if ((r = read_one_line_file(p, &c)) < 0) { + free(p); + return r; + } + + sc = strstrip(c); + + if (streq(sc, "0")) { + if ((r = write_one_line_file(p, "1\n")) < 0) { + free(p); + free(c); + return r; + } + } else if (!streq(sc, "1")) { + free(p); + free(c); + return -EIO; + } + + return 0; +} + +static int create_hierarchy_cgroup(Manager *m) { + struct cgroup *cg; + int r; + + assert(m); + + if (!(cg = cgroup_new_cgroup(m->cgroup_hierarchy))) + return -ENOMEM; + + if (!(cgroup_add_controller(cg, m->cgroup_controller))) { + r = -ENOMEM; + goto finish; + } + + if ((r = cgroup_create_cgroup(cg, true)) != 0) { + log_error("Failed to create cgroup hierarchy group: %s", cgroup_strerror(r)); + r = translate_error(r, errno); + goto finish; + } + + if ((r = cgroup_attach_task(cg)) != 0) { + log_error("Failed to add ourselves to hierarchy group: %s", cgroup_strerror(r)); + r = translate_error(r, errno); + goto finish; + } + + r = 0; + +finish: + cgroup_free(&cg); + return r; +} + +int manager_setup_cgroup(Manager *m) { + char *mp, *cp; + int r; + pid_t pid; + + assert(m); + + if ((r = cgroup_init()) != 0) { + log_error("Failed to initialize libcg: %s", cgroup_strerror(r)); + return translate_error(r, errno); + } + + free(m->cgroup_controller); + if (!(m->cgroup_controller = strdup("debug"))) + return -ENOMEM; + + if ((r = cgroup_get_subsys_mount_point(m->cgroup_controller, &mp))) + return translate_error(r, errno); + + pid = getpid(); + + if ((r = cgroup_get_current_controller_path(pid, m->cgroup_controller, &cp))) { + free(mp); + return translate_error(r, errno); + } + + free(m->cgroup_hierarchy); + m->cgroup_hierarchy = NULL; + if (asprintf(&m->cgroup_hierarchy, "%s/systemd-%llu", strcmp(cp, "/") == 0 ? "" : cp, (unsigned long long) pid) < 0) { + free(cp); + free(mp); + return -ENOMEM; + } + + log_info("Using cgroup controller <%s>, hierarchy mounted at <%s>, using root group <%s>.", + m->cgroup_controller, + mp, + m->cgroup_hierarchy); + + if ((r = install_release_agent(m, mp)) < 0) + log_warning("Failed to install release agent, ignoring: %s", strerror(-r)); + else + log_info("Installed release agent, or already installed."); + + free(mp); + free(cp); + + if ((r = create_hierarchy_cgroup(m)) < 0) + log_error("Failed to create root cgroup hierarchy: %s", strerror(-r)); + else + log_info("Created root group."); + + return r; +} + +int manager_shutdown_cgroup(Manager *m) { + struct cgroup *cg; + int r; + + assert(m); + + if (!m->cgroup_hierarchy) + return 0; + + if (!(cg = cgroup_new_cgroup(m->cgroup_hierarchy))) + return -ENOMEM; + + if (!(cgroup_add_controller(cg, m->cgroup_controller))) { + r = -ENOMEM; + goto finish; + } + + if ((r = cgroup_delete_cgroup_ext(cg, CGFLAG_DELETE_IGNORE_MIGRATION|CGFLAG_DELETE_RECURSIVE)) != 0) { + log_error("Failed to delete cgroup hierarchy group: %s", cgroup_strerror(r)); + r = translate_error(r, errno); + goto finish; + } + r = 0; + +finish: + cgroup_free(&cg); + return r; + +} + +int cgroup_notify_empty(Manager *m, const char *group) { + CGroupBonding *l, *b; + + assert(m); + assert(group); + + if (!(l = hashmap_get(m->cgroup_bondings, group))) + return 0; + + LIST_FOREACH(by_path, b, l) { + int t; + + if (!b->unit) + continue; + + if ((t = cgroup_bonding_is_empty_list(b)) < 0) { + + /* If we don't know, we don't know */ + if (t != -EAGAIN) + log_warning("Failed to check whether cgroup is empty: %s", strerror(errno)); + + continue; + } + + if (t > 0) + if (UNIT_VTABLE(b->unit)->cgroup_notify_empty) + UNIT_VTABLE(b->unit)->cgroup_notify_empty(b->unit); + } + + return 0; +} + +CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller) { + CGroupBonding *b; + + assert(controller); + + LIST_FOREACH(by_unit, b, first) + if (streq(b->controller, controller)) + return b; + + return NULL; +} diff --git a/cgroup.h b/cgroup.h new file mode 100644 index 0000000000..66ddb9579a --- /dev/null +++ b/cgroup.h @@ -0,0 +1,83 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#ifndef foocgrouphfoo +#define foocgrouphfoo + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include + +typedef struct CGroupBonding CGroupBonding; + +#include "unit.h" + +/* Binds a cgroup to a name */ +struct CGroupBonding { + char *controller; + char *path; + + Unit *unit; + + struct cgroup *cgroup; + + /* When shutting down, kill all tasks? */ + bool kill_all:1; + + /* When shutting down, remove cgroup? */ + bool clean_up:1; + + /* When our tasks are the only ones in this group */ + bool only_us:1; + + /* Inherit parameters from parent group */ + bool inherit:1; + + /* For the Unit::cgroup_bondings list */ + LIST_FIELDS(CGroupBonding, by_unit); + + /* For the Manager::cgroup_bondings hashmap */ + LIST_FIELDS(CGroupBonding, by_path); +}; + +int cgroup_bonding_realize(CGroupBonding *b); +int cgroup_bonding_realize_list(CGroupBonding *first); + +void cgroup_bonding_free(CGroupBonding *b); +void cgroup_bonding_free_list(CGroupBonding *first); + +int cgroup_bonding_install(CGroupBonding *b, pid_t pid); +int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid); + +int cgroup_bonding_kill(CGroupBonding *b, int sig); +int cgroup_bonding_kill_list(CGroupBonding *first, int sig); + +int cgroup_bonding_is_empty(CGroupBonding *b); +int cgroup_bonding_is_empty_list(CGroupBonding *first); + +CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller); + +#include "manager.h" + +int manager_setup_cgroup(Manager *m); +int manager_shutdown_cgroup(Manager *m); + +int cgroup_notify_empty(Manager *m, const char *group); + +#endif diff --git a/cgroups-agent.c b/cgroups-agent.c new file mode 100644 index 0000000000..232b63e2da --- /dev/null +++ b/cgroups-agent.c @@ -0,0 +1,72 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include + +#include "log.h" + +int main(int argc, char *argv[]) { + DBusError error; + DBusConnection *bus = NULL; + DBusMessage *m = NULL; + int r = 1; + + dbus_error_init(&error); + + if (argc != 2) { + log_error("Incorrect number of arguments."); + goto finish; + } + + if (!(bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error))) { + log_error("Failed to get D-Bus connection: %s", error.message); + goto finish; + } + + if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1/agent", "org.freedesktop.systemd1.Agent", "Released"))) { + log_error("Could not allocate signal message."); + goto finish; + } + + if (!dbus_message_append_args(m, + DBUS_TYPE_STRING, &argv[1], + DBUS_TYPE_INVALID)) { + log_error("Could not attach group information to signal message."); + goto finish; + } + + if (!dbus_connection_send(bus, m, NULL)) { + log_error("Failed to send signal message."); + goto finish; + } + + r = 0; + +finish: + if (bus) + dbus_connection_unref(bus); + + if (m) + dbus_message_unref(m); + + dbus_error_free(&error); + return r; +} diff --git a/configure.ac b/configure.ac index 95a9ef5ca4..f0beb1fc15 100644 --- a/configure.ac +++ b/configure.ac @@ -69,6 +69,10 @@ PKG_CHECK_MODULES(GTK, [ gtk+-2.0 ]) AC_SUBST(GTK_CFLAGS) AC_SUBST(GTK_LIBS) +PKG_CHECK_MODULES(CGROUP, [ libcgroup ]) +AC_SUBST(CGROUP_CFLAGS) +AC_SUBST(CGROUP_LIBS) + AM_PROG_VALAC() AC_SUBST(VAPIDIR) diff --git a/dbus.c b/dbus.c index bef8bb885f..0227844e69 100644 --- a/dbus.c +++ b/dbus.c @@ -29,16 +29,28 @@ #include "dbus.h" #include "log.h" #include "strv.h" +#include "cgroup.h" static void bus_dispatch_status(DBusConnection *bus, DBusDispatchStatus status, void *data) { Manager *m = data; assert(bus); assert(m); + assert(m->bus == bus); m->request_bus_dispatch = status != DBUS_DISPATCH_COMPLETE; } +static void system_bus_dispatch_status(DBusConnection *bus, DBusDispatchStatus status, void *data) { + Manager *m = data; + + assert(bus); + assert(m); + assert(m->system_bus == bus); + + m->request_system_bus_dispatch = status != DBUS_DISPATCH_COMPLETE; +} + static uint32_t bus_flags_to_events(DBusWatch *bus_watch) { unsigned flags; uint32_t events = 0; @@ -81,7 +93,7 @@ void bus_watch_event(Manager *m, Watch *w, int events) { /* This is called by the event loop whenever there is * something happening on D-Bus' file handles. */ - if (!(dbus_watch_get_enabled(w->data.bus_watch))) + if (!dbus_watch_get_enabled(w->data.bus_watch)) return; dbus_watch_handle(w->data.bus_watch, events_to_bus_flags(events)); @@ -315,16 +327,57 @@ static DBusHandlerResult bus_message_filter(DBusConnection *connection, DBusMes return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } +static DBusHandlerResult system_bus_message_filter(DBusConnection *connection, DBusMessage *message, void *data) { + Manager *m = data; + DBusError error; + + assert(connection); + assert(message); + assert(m); + + dbus_error_init(&error); + + /* log_debug("Got D-Bus request: %s.%s() on %s", */ + /* dbus_message_get_interface(message), */ + /* dbus_message_get_member(message), */ + /* dbus_message_get_path(message)); */ + + if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) { + log_error("Warning! D-Bus connection terminated."); + + /* FIXME: we probably should restart D-Bus here */ + + } if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) { + const char *cgroup; + + if (!dbus_message_get_args(message, &error, + DBUS_TYPE_STRING, &cgroup, + DBUS_TYPE_INVALID)) + log_error("Failed to parse Released message: %s", error.message); + else + cgroup_notify_empty(m, cgroup); + } + + dbus_error_free(&error); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + unsigned bus_dispatch(Manager *m) { assert(m); - if (!m->request_bus_dispatch) - return 0; + if (m->request_bus_dispatch) + if (dbus_connection_dispatch(m->bus) == DBUS_DISPATCH_COMPLETE) { + m->request_bus_dispatch = false; + return 1; + } - if (dbus_connection_dispatch(m->bus) == DBUS_DISPATCH_COMPLETE) - m->request_bus_dispatch = false; + if (m->request_system_bus_dispatch) + if (dbus_connection_dispatch(m->system_bus) == DBUS_DISPATCH_COMPLETE) { + m->request_system_bus_dispatch = false; + return 1; + } - return 1; + return 0; } static int request_name(Manager *m) { @@ -362,6 +415,18 @@ static int request_name(Manager *m) { return 0; } +static int bus_setup_loop(Manager *m, DBusConnection *bus) { + assert(m); + assert(bus); + + dbus_connection_set_exit_on_disconnect(bus, FALSE); + if (!dbus_connection_set_watch_functions(bus, bus_add_watch, bus_remove_watch, bus_toggle_watch, m, NULL) || + !dbus_connection_set_timeout_functions(bus, bus_add_timeout, bus_remove_timeout, bus_toggle_timeout, m, NULL)) + return -ENOMEM; + + return 0; +} + int bus_init(Manager *m) { DBusError error; char *id; @@ -381,17 +446,39 @@ int bus_init(Manager *m) { if (!(m->bus = dbus_bus_get_private(m->running_as == MANAGER_SESSION ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &error))) { log_error("Failed to get D-Bus connection: %s", error.message); dbus_error_free(&error); + bus_done(m); return -ECONNREFUSED; } - dbus_connection_set_exit_on_disconnect(m->bus, FALSE); + if ((r = bus_setup_loop(m, m->bus)) < 0) { + bus_done(m); + return r; + } + dbus_connection_set_dispatch_status_function(m->bus, bus_dispatch_status, m, NULL); - if (!dbus_connection_set_watch_functions(m->bus, bus_add_watch, bus_remove_watch, bus_toggle_watch, m, NULL) || - !dbus_connection_set_timeout_functions(m->bus, bus_add_timeout, bus_remove_timeout, bus_toggle_timeout, m, NULL) || - !dbus_connection_register_object_path(m->bus, "/org/freedesktop/systemd1", &bus_manager_vtable, m) || + + if (m->running_as == MANAGER_SESSION) { + if (!(m->system_bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error))) { + log_error("Failed to get D-Bus connection: %s", error.message); + dbus_error_free(&error); + bus_done(m); + return -ECONNREFUSED; + } + + if ((r = bus_setup_loop(m, m->system_bus)) < 0) { + bus_done(m); + return r; + } + + dbus_connection_set_dispatch_status_function(m->system_bus, system_bus_dispatch_status, m, NULL); + } else + m->system_bus = m->bus; + + if (!dbus_connection_register_object_path(m->bus, "/org/freedesktop/systemd1", &bus_manager_vtable, m) || !dbus_connection_register_fallback(m->bus, "/org/freedesktop/systemd1/unit", &bus_unit_vtable, m) || !dbus_connection_register_fallback(m->bus, "/org/freedesktop/systemd1/job", &bus_job_vtable, m) || - !dbus_connection_add_filter(m->bus, bus_message_filter, m, NULL)) { + !dbus_connection_add_filter(m->bus, bus_message_filter, m, NULL) || + !dbus_connection_add_filter(m->system_bus, system_bus_message_filter, m, NULL)) { bus_done(m); return -ENOMEM; } @@ -406,7 +493,6 @@ int bus_init(Manager *m) { if (dbus_error_is_set(&error)) { log_error("Failed to register match: %s", error.message); dbus_error_free(&error); - bus_done(m); return -ENOMEM; } @@ -415,12 +501,31 @@ int bus_init(Manager *m) { return r; } + dbus_bus_add_match(m->system_bus, + "type='signal'," + "interface='org.freedesktop.systemd1.Agent'," + "path='/org/freedesktop/systemd1/agent'", + &error); + + if (dbus_error_is_set(&error)) { + log_error("Failed to register match: %s", error.message); + dbus_error_free(&error); + bus_done(m); + return -ENOMEM; + } + log_debug("Successfully connected to D-Bus bus %s as %s", strnull((id = dbus_connection_get_server_id(m->bus))), strnull(dbus_bus_get_unique_name(m->bus))); dbus_free(id); + log_debug("Successfully connected to system D-Bus bus %s as %s", + strnull((id = dbus_connection_get_server_id(m->system_bus))), + strnull(dbus_bus_get_unique_name(m->system_bus))); + dbus_free(id); + m->request_bus_dispatch = true; + m->request_system_bus_dispatch = true; return 0; } @@ -428,6 +533,12 @@ int bus_init(Manager *m) { void bus_done(Manager *m) { assert(m); + if (m->system_bus && m->system_bus != m->bus) { + dbus_connection_close(m->system_bus); + dbus_connection_unref(m->system_bus); + m->system_bus = NULL; + } + if (m->bus) { dbus_connection_close(m->bus); dbus_connection_unref(m->bus); @@ -571,8 +682,6 @@ oom: return DBUS_HANDLER_RESULT_NEED_MEMORY; } - - static const char *error_to_dbus(int error) { switch(error) { diff --git a/execute.c b/execute.c index 85d9873918..dd36038c4a 100644 --- a/execute.c +++ b/execute.c @@ -42,6 +42,7 @@ #include "log.h" #include "ioprio.h" #include "securebits.h" +#include "cgroup.h" static int close_fds(int except[], unsigned n_except) { DIR *d; @@ -508,9 +509,11 @@ int exec_spawn(const ExecCommand *command, int *fds, unsigned n_fds, bool apply_permissions, bool apply_chroot, + CGroupBonding *cgroup_bondings, pid_t *ret) { pid_t pid; + int r; assert(command); assert(context); @@ -519,11 +522,15 @@ int exec_spawn(const ExecCommand *command, log_debug("About to execute %s", command->path); + if (cgroup_bondings) + if ((r = cgroup_bonding_realize_list(cgroup_bondings))) + return r; + if ((pid = fork()) < 0) return -errno; if (pid == 0) { - int i, r; + int i; sigset_t ss; const char *username = NULL, *home = NULL; uid_t uid = (uid_t) -1; @@ -556,6 +563,12 @@ int exec_spawn(const ExecCommand *command, goto fail; } + if (cgroup_bondings) + if ((r = cgroup_bonding_install_list(cgroup_bondings, 0)) < 0) { + r = EXIT_CGROUP; + goto fail; + } + if (context->oom_adjust_set) { char t[16]; diff --git a/execute.h b/execute.h index d8073e14b0..cb69bb3bd8 100644 --- a/execute.h +++ b/execute.h @@ -33,6 +33,8 @@ typedef struct ExecContext ExecContext; #include #include +struct CGroupBonding; + #include "list.h" #include "util.h" @@ -145,7 +147,8 @@ typedef enum ExitStatus { EXIT_CPUAFFINITY, EXIT_GROUP, EXIT_USER, - EXIT_CAPABILITIES + EXIT_CAPABILITIES, + EXIT_CGROUP } ExitStatus; int exec_spawn(const ExecCommand *command, @@ -153,6 +156,7 @@ int exec_spawn(const ExecCommand *command, int *fds, unsigned n_fds, bool apply_permissions, bool apply_chroot, + struct CGroupBonding *cgroup_bondings, pid_t *ret); void exec_command_free_list(ExecCommand *c); diff --git a/load-fragment.c b/load-fragment.c index 0b43c81194..bf17111482 100644 --- a/load-fragment.c +++ b/load-fragment.c @@ -943,6 +943,37 @@ static int config_parse_limit( return 0; } +static int config_parse_cgroup( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + Unit *u = userdata; + char *w; + size_t l; + char *state; + + FOREACH_WORD(w, l, rvalue, state) { + char *t; + int r; + + if (!(t = strndup(w, l))) + return -ENOMEM; + + r = unit_add_cgroup_from_text(u, t); + free(t); + + if (r < 0) + return r; + } + + return 0; +} + #define FOLLOW_MAX 8 static int open_follow(char **filename, FILE **_f, Set *names, char **_id) { @@ -1067,7 +1098,8 @@ static int load_from_path(Unit *u, const char *path) { { "LimitNICE", config_parse_limit, &(context).rlimit[RLIMIT_NICE], section }, \ { "LimitRTPRIO", config_parse_limit, &(context).rlimit[RLIMIT_RTPRIO], section }, \ { "LimitRTTIME", config_parse_limit, &(context).rlimit[RLIMIT_RTTIME], section }, \ - { "NonBlocking", config_parse_bool, &(context).non_blocking, section } + { "NonBlocking", config_parse_bool, &(context).non_blocking, section }, \ + { "ControlGroup", config_parse_cgroup, u, section } \ const ConfigItem items[] = { { "Names", config_parse_names, u, "Meta" }, @@ -1096,6 +1128,7 @@ static int load_from_path(Unit *u, const char *path) { { "Restart", config_parse_service_restart, &u->service, "Service" }, { "PermissionsStartOnly", config_parse_bool, &u->service.permissions_start_only, "Service" }, { "RootDirectoryStartOnly", config_parse_bool, &u->service.root_directory_start_only, "Service" }, + { "ValidNoProcess", config_parse_bool, &u->service.valid_no_process, "Service" }, EXEC_CONTEXT_CONFIG_ITEMS(u->service.exec_context, "Service"), { "ListenStream", config_parse_listen, &u->socket, "Socket" }, diff --git a/main.c b/main.c index 830a9a43eb..4456bde638 100644 --- a/main.c +++ b/main.c @@ -37,8 +37,8 @@ int main(int argc, char *argv[]) { assert_se(set_unit_path("test1") >= 0); - if (!(m = manager_new())) { - log_error("Failed to allocate manager object: %s", strerror(ENOMEM)); + if ((r = manager_new(&m)) < 0) { + log_error("Failed to allocate manager object: %s", strerror(-r)); goto finish; } diff --git a/manager.c b/manager.c index 4f38979355..e9bd366904 100644 --- a/manager.c +++ b/manager.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "manager.h" #include "hashmap.h" @@ -39,6 +40,8 @@ #include "log.h" #include "util.h" #include "ratelimit.h" +#include "cgroup.h" +#include "mount-setup.h" static int manager_setup_signals(Manager *m) { sigset_t mask; @@ -254,11 +257,14 @@ static int manager_find_paths(Manager *m) { return 0; } -Manager* manager_new(void) { +int manager_new(Manager **_m) { Manager *m; + int r = -ENOMEM; + + assert(_m); if (!(m = new0(Manager, 1))) - return NULL; + return -ENOMEM; m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = -1; m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */ @@ -275,6 +281,9 @@ Manager* manager_new(void) { if (!(m->watch_pids = hashmap_new(trivial_hash_func, trivial_compare_func))) goto fail; + if (!(m->cgroup_bondings = hashmap_new(string_hash_func, string_compare_func))) + goto fail; + if ((m->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) goto fail; @@ -287,7 +296,7 @@ Manager* manager_new(void) { log_debug("systemd running in %s mode.", manager_running_as_to_string(m->running_as)); - if (manager_find_paths(m) < 0) + if ((r = manager_find_paths(m)) < 0) goto fail; if (chdir("/") < 0) @@ -296,18 +305,25 @@ Manager* manager_new(void) { /* Become a session leader if we aren't one yet. */ setsid(); - if (manager_setup_signals(m) < 0) + if ((r = manager_setup_signals(m)) < 0) + goto fail; + + if ((r = mount_setup()) < 0) + goto fail; + + if ((r = manager_setup_cgroup(m)) < 0) goto fail; /* FIXME: this should be called only when the D-Bus bus daemon is running */ - if (bus_init(m) < 0) + if ((r = bus_init(m)) < 0) goto fail; - return m; + *_m = m; + return 0; fail: manager_free(m); - return NULL; + return r; } void manager_free(Manager *m) { @@ -327,6 +343,8 @@ void manager_free(Manager *m) { if (unit_vtable[c]->shutdown) unit_vtable[c]->shutdown(m); + manager_shutdown_cgroup(m); + bus_done(m); hashmap_free(m->units); @@ -342,6 +360,12 @@ void manager_free(Manager *m) { strv_free(m->unit_path); strv_free(m->sysvinit_path); + free(m->cgroup_controller); + free(m->cgroup_hierarchy); + + assert(hashmap_isempty(m->cgroup_bondings)); + hashmap_free(m->cgroup_bondings); + free(m); } diff --git a/manager.h b/manager.h index 870db97bc0..8d46323e0e 100644 --- a/manager.h +++ b/manager.h @@ -133,6 +133,7 @@ struct Manager { bool dispatching_dbus_queue:1; bool request_bus_dispatch:1; + bool request_system_bus_dispatch:1; Hashmap *watch_pids; /* pid => Unit object n:1 */ @@ -153,11 +154,16 @@ struct Manager { Watch mount_watch; /* Data specific to the D-Bus subsystem */ - DBusConnection *bus; + DBusConnection *bus, *system_bus; Set *subscribed; + + /* Data specific to the cgroup subsystem */ + Hashmap *cgroup_bondings; /* path string => CGroupBonding object 1:n */ + char *cgroup_controller; + char *cgroup_hierarchy; }; -Manager* manager_new(void); +int manager_new(Manager **m); void manager_free(Manager *m); int manager_coldplug(Manager *m); diff --git a/mount-setup.c b/mount-setup.c new file mode 100644 index 0000000000..4a5e2cc760 --- /dev/null +++ b/mount-setup.c @@ -0,0 +1,112 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "mount-setup.h" +#include "log.h" + +enum { + MOUNT_WHAT, + MOUNT_WHERE, + MOUNT_TYPE, + MOUNT_OPTIONS, + MOUNT_SKIP +}; + +static const char *table[] = { + "/proc", "/proc", "proc", "rw", + "/sys", "/sys", "sysfs", "rw", + "cgroup", "/cgroup/debug", "cgroup", "debug", + NULL +}; + +static int is_mount_point(const char *t) { + struct stat a, b; + char *copy; + + if (lstat(t, &a) < 0) { + + if (errno == ENOENT) + return 0; + + return -errno; + } + + if (!(copy = strdup(t))) + return -ENOMEM; + + if (lstat(dirname(copy), &b) < 0) { + free(copy); + return -errno; + } + + free(copy); + + return a.st_dev != b.st_dev; + +} + +static int mount_one(const char *t[]) { + int r; + + assert(t); + + if ((r = is_mount_point(t[MOUNT_WHERE])) < 0) + return r; + + if (r > 0) + return 0; + + log_debug("Mounting %s to %s of type %s with options %s.", + t[MOUNT_WHAT], + t[MOUNT_WHERE], + t[MOUNT_TYPE], + t[MOUNT_OPTIONS]); + + if (mount(t[MOUNT_WHAT], + t[MOUNT_WHERE], + t[MOUNT_TYPE], + 0, + t[MOUNT_OPTIONS]) < 0) { + log_error("Failed to mount %s: %s", t[MOUNT_WHERE], strerror(errno)); + return -errno; + } + + return 0; +} + +int mount_setup(void) { + int r; + const char **t; + + for (t = table; *t; t += MOUNT_SKIP) + if ((r = mount_one(t)) < 0) + return r; + + return 0; +} diff --git a/mount-setup.h b/mount-setup.h new file mode 100644 index 0000000000..df768de94f --- /dev/null +++ b/mount-setup.h @@ -0,0 +1,27 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#ifndef foomountsetuphfoo +#define foomountsetuphfoo + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +int mount_setup(void); + +#endif diff --git a/service.c b/service.c index a693c33392..2841689467 100644 --- a/service.c +++ b/service.c @@ -581,6 +581,12 @@ static int service_init(Unit *u) { return r; } + /* Add default cgroup */ + if ((r = unit_add_default_cgroup(u)) < 0) { + service_done(u); + return r; + } + return 0; } @@ -599,10 +605,12 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { fprintf(f, "%sService State: %s\n" "%sPermissionsStartOnly: %s\n" - "%sRootDirectoryStartOnly: %s\n", + "%sRootDirectoryStartOnly: %s\n" + "%sValidNoProcess: %s\n", prefix, service_state_to_string(s->state), prefix, yes_no(s->permissions_start_only), - prefix, yes_no(s->root_directory_start_only)); + prefix, yes_no(s->root_directory_start_only), + prefix, yes_no(s->valid_no_process)); if (s->pid_file) fprintf(f, @@ -898,6 +906,7 @@ static int service_spawn( fds, n_fds, apply_permissions, apply_chroot, + UNIT(s)->meta.cgroup_bondings, &pid)) < 0) goto fail; @@ -1336,7 +1345,7 @@ static int main_pid_good(Service *s) { return s->main_pid > 0; /* We don't know the pid */ - return -1; + return -EAGAIN; } static bool control_pid_good(Service *s) { @@ -1345,6 +1354,15 @@ static bool control_pid_good(Service *s) { return s->control_pid > 0; } +static int cgroup_good(Service *s) { + assert(s); + + if (s->valid_no_process) + return -EAGAIN; + + return cgroup_bonding_is_empty_list(UNIT(s)->meta.cgroup_bondings); +} + static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { Service *s = SERVICE(u); bool success; @@ -1477,7 +1495,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { case SERVICE_RELOAD: if (success) { - if (main_pid_good(s) != 0) + if (main_pid_good(s) != 0 && cgroup_good(s) != 0) service_set_state(s, SERVICE_RUNNING); else service_enter_stop(s, true); @@ -1580,6 +1598,33 @@ static void service_timer_event(Unit *u, uint64_t elapsed, Watch* w) { } } +static void service_cgroup_notify_event(Unit *u) { + Service *s = SERVICE(u); + + assert(u); + + log_debug("%s: cgroup is empty", unit_id(u)); + + switch (s->state) { + + /* Waiting for SIGCHLD is usually more interesting, + * because it includes return codes/signals. Which is + * why we ignore the cgroup events for most cases, + * except when we don't know pid which to expect the + * SIGCHLD for. */ + + case SERVICE_RUNNING: + + if (!s->valid_no_process && main_pid_good(s) <= 0) + service_enter_stop(s, true); + + break; + + default: + ; + } +} + static int service_enumerate(Manager *m) { static const char * const rcnd[] = { @@ -1753,5 +1798,7 @@ const UnitVTable service_vtable = { .sigchld_event = service_sigchld_event, .timer_event = service_timer_event, + .cgroup_notify_empty = service_cgroup_notify_event, + .enumerate = service_enumerate }; diff --git a/service.h b/service.h index 632e5eae4d..5ffdde19cb 100644 --- a/service.h +++ b/service.h @@ -90,6 +90,7 @@ struct Service { bool permissions_start_only; bool root_directory_start_only; + bool valid_no_process; ServiceState state; diff --git a/socket.c b/socket.c index aec0d392a6..79933347d0 100644 --- a/socket.c +++ b/socket.c @@ -129,6 +129,10 @@ static int socket_init(Unit *u) { if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(s->service))) < 0) goto fail; + /* Add default cgroup */ + if ((r = unit_add_default_cgroup(u)) < 0) + goto fail; + return 0; fail: @@ -394,7 +398,13 @@ static int socket_spawn(Socket *s, ExecCommand *c, bool timeout, pid_t *_pid) { } else unit_unwatch_timer(UNIT(s), &s->timer_watch); - if ((r = exec_spawn(c, &s->exec_context, NULL, 0, true, true, &pid)) < 0) + if ((r = exec_spawn(c, + &s->exec_context, + NULL, 0, + true, + true, + UNIT(s)->meta.cgroup_bondings, + &pid)) < 0) goto fail; if ((r = unit_watch_pid(UNIT(s), pid)) < 0) diff --git a/test-engine.c b/test-engine.c index 5914f5f193..43a68fcdbd 100644 --- a/test-engine.c +++ b/test-engine.c @@ -33,7 +33,7 @@ int main(int argc, char *argv[]) { assert_se(set_unit_path("test2") >= 0); - assert_se(m = manager_new()); + assert_se(manager_new(&m) >= 0); printf("Load1:\n"); assert_se(manager_load_unit(m, "a.service", &a) == 0); diff --git a/test1/exec-demo.service b/test1/exec-demo.service index b772518e3e..5802e37697 100644 --- a/test1/exec-demo.service +++ b/test1/exec-demo.service @@ -2,6 +2,8 @@ Description=Simple Execution Demo [Service] -ExecStart=/bin/cat /etc/hosts -Type=simple +ExecStartPre=/bin/ps -eo pid,uid,args,cgroup +ExecStartPre=/bin/cat /etc/hosts +ExecStart=/bin/bash -c '/bin/sleep 5 &' +Type=forking Output=syslog diff --git a/unit.c b/unit.c index 39c9e5e7e8..673aac184f 100644 --- a/unit.c +++ b/unit.c @@ -260,6 +260,8 @@ void unit_free(Unit *u) { /* Detach from next 'bigger' objects */ + cgroup_bonding_free_list(u->meta.cgroup_bondings); + SET_FOREACH(t, u->meta.names, i) hashmap_remove_value(u->meta.manager->units, t, u); @@ -369,12 +371,12 @@ const char *unit_description(Unit *u) { } void unit_dump(Unit *u, FILE *f, const char *prefix) { - char *t; UnitDependency d; Iterator i; char *p2; const char *prefix2; + CGroupBonding *b; assert(u); @@ -413,6 +415,10 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { fprintf(f, "%s\t%s: %s\n", prefix, unit_dependency_to_string(d), unit_id(other)); } + LIST_FOREACH(by_unit, b, u->meta.cgroup_bondings) + fprintf(f, "%s\tControlGroup: %s:%s\n", + prefix, b->controller, b->path); + if (UNIT_VTABLE(u)->dump) UNIT_VTABLE(u)->dump(u, f, prefix2); @@ -1060,6 +1066,138 @@ char *unit_dbus_path(Unit *u) { return p; } +int unit_add_cgroup(Unit *u, CGroupBonding *b) { + CGroupBonding *l; + int r; + + assert(u); + assert(b); + assert(b->path); + + /* Ensure this hasn't been added yet */ + assert(!b->unit); + + l = hashmap_get(u->meta.manager->cgroup_bondings, b->path); + LIST_PREPEND(CGroupBonding, by_path, l, b); + + if ((r = hashmap_replace(u->meta.manager->cgroup_bondings, b->path, l)) < 0) { + LIST_REMOVE(CGroupBonding, by_path, l, b); + return r; + } + + LIST_PREPEND(CGroupBonding, by_unit, u->meta.cgroup_bondings, b); + b->unit = u; + + return 0; +} + +int unit_add_cgroup_from_text(Unit *u, const char *name) { + size_t n; + const char *p; + char *controller; + CGroupBonding *b; + int r; + + assert(u); + assert(name); + + /* Detect controller name */ + n = strcspn(name, ":/"); + + /* Only controller name, no path? No path? */ + if (name[n] == 0) + return -EINVAL; + + if (n > 0) { + if (name[n] != ':') + return -EINVAL; + + p = name+n+1; + } else + p = name; + + /* Insist in absolute paths */ + if (p[0] != '/') + return -EINVAL; + + if (!(controller = strndup(name, n))) + return -ENOMEM; + + if (cgroup_bonding_find_list(u->meta.cgroup_bondings, controller)) { + free(controller); + return -EEXIST; + } + + if (!(b = new0(CGroupBonding, 1))) { + free(controller); + return -ENOMEM; + } + + b->controller = controller; + + if (!(b->path = strdup(p))) { + r = -ENOMEM; + goto fail; + } + + b->only_us = false; + b->clean_up = false; + + if ((r = unit_add_cgroup(u, b)) < 0) + goto fail; + + return 0; + +fail: + free(b->path); + free(b->controller); + free(b); + + return r; +} + +int unit_add_default_cgroup(Unit *u) { + CGroupBonding *b; + int r = -ENOMEM; + + assert(u); + + /* Adds in the default cgroup data, if it wasn't specified yet */ + + if (unit_get_default_cgroup(u)) + return 0; + + if (!(b = new0(CGroupBonding, 1))) + return -ENOMEM; + + if (!(b->controller = strdup(u->meta.manager->cgroup_controller))) + goto fail; + + if (asprintf(&b->path, "%s/%s", u->meta.manager->cgroup_hierarchy, unit_id(u)) < 0) + goto fail; + + b->clean_up = true; + b->only_us = true; + + if ((r = unit_add_cgroup(u, b)) < 0) + goto fail; + + return 0; + +fail: + free(b->path); + free(b->controller); + free(b); + + return r; +} + +CGroupBonding* unit_get_default_cgroup(Unit *u) { + assert(u); + + return cgroup_bonding_find_list(u->meta.cgroup_bondings, u->meta.manager->cgroup_controller); +} + static const char* const unit_type_table[_UNIT_TYPE_MAX] = { [UNIT_SERVICE] = "service", [UNIT_TIMER] = "timer", diff --git a/unit.h b/unit.h index f5eac2916d..4b3b6fb644 100644 --- a/unit.h +++ b/unit.h @@ -112,6 +112,7 @@ enum UnitDependency { #include "manager.h" #include "job.h" +#include "cgroup.h" struct Meta { Manager *manager; @@ -143,6 +144,9 @@ struct Meta { usec_t active_enter_timestamp; usec_t active_exit_timestamp; + /* Counterparts in the cgroup filesystem */ + CGroupBonding *cgroup_bondings; + /* Load queue */ LIST_FIELDS(Meta, load_queue); @@ -197,6 +201,8 @@ struct UnitVTable { void (*sigchld_event)(Unit *u, pid_t pid, int code, int status); void (*timer_event)(Unit *u, uint64_t n_elapsed, Watch *w); + void (*cgroup_notify_empty)(Unit *u); + /* This is called for each unit type and should be used to * enumerate existing devices and load them. However, * everything that is loaded here should still stay in @@ -244,6 +250,11 @@ int unit_add_name(Unit *u, const char *name); int unit_add_dependency(Unit *u, UnitDependency d, Unit *other); int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name); +int unit_add_cgroup(Unit *u, CGroupBonding *b); +int unit_add_cgroup_from_text(Unit *u, const char *name); +int unit_add_default_cgroup(Unit *u); +CGroupBonding* unit_get_default_cgroup(Unit *u); + int unit_choose_id(Unit *u, const char *name); int unit_set_description(Unit *u, const char *description); diff --git a/util.c b/util.c index e31649861d..d2b73125c0 100644 --- a/util.c +++ b/util.c @@ -585,7 +585,7 @@ int reset_all_signal_handlers(void) { return -errno; } - return 0; + return 0; } char *strstrip(char *s) { -- cgit v1.2.3-54-g00ecf From 0b7964b804e093d31c9adc34ba1917017c7f4d60 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 4 Apr 2010 22:53:42 +0200 Subject: sysv: implement /dev/initctl compatibility --- .gitignore | 1 + Makefile.am | 14 +- initctl.c | 437 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ initreq.h | 77 +++++++++++ 4 files changed, 528 insertions(+), 1 deletion(-) create mode 100644 initctl.c create mode 100644 initreq.h (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 06ef3d0323..ac80a41582 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ systemd-cgroups-agent +systemd-initctl systemd *.o test-engine diff --git a/Makefile.am b/Makefile.am index 57a2dd98dd..0c0e4def24 100644 --- a/Makefile.am +++ b/Makefile.am @@ -37,7 +37,8 @@ bin_PROGRAMS = \ pkglibexec_PROGRAMS = \ systemd-logger \ - systemd-cgroups-agent + systemd-cgroups-agent \ + systemd-initctl noinst_PROGRAMS = \ test-engine \ @@ -137,6 +138,17 @@ systemd_logger_SOURCES = \ $(BASIC_SOURCES) \ logger.c +systemd_initctl_SOURCES = \ + $(BASIC_SOURCES) \ + initctl.c + +systemd_initctl_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + $(DBUS_CFLAGS) + +systemd_initctl_LDADD = \ + $(DBUS_LIBS) + systemd_cgroups_agent_SOURCES = \ $(BASIC_SOURCES) \ cgroups-agent.c diff --git a/initctl.c b/initctl.c new file mode 100644 index 0000000000..a4356759c2 --- /dev/null +++ b/initctl.c @@ -0,0 +1,437 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "util.h" +#include "log.h" +#include "list.h" +#include "initreq.h" + +#define SERVER_FD_START 3 +#define SERVER_FD_MAX 16 +#define TIMEOUT ((int) (10*MSEC_PER_SEC)) + +typedef struct Fifo Fifo; + +typedef struct Server { + int epoll_fd; + + LIST_HEAD(Fifo, fifos); + unsigned n_fifos; + + DBusConnection *bus; +} Server; + +struct Fifo { + Server *server; + + int fd; + + struct init_request buffer; + size_t bytes_read; + + LIST_FIELDS(Fifo, fifo); +}; + +static const char *translate_runlevel(int runlevel) { + + switch (runlevel) { + + case '0': + return "halt.target"; + + case '1': + case 's': + case 'S': + return "rescue.target"; + + case '2': + return "runlevel2.target"; + + case '3': + return "runlevel3.target"; + + case '4': + return "runlevel4.target"; + + case '5': + return "runlevel5.target"; + + case '6': + return "reboot.target"; + + default: + return NULL; + } +} + +static void change_runlevel(Server *s, int runlevel) { + const char *target; + DBusMessage *m = NULL, *reply = NULL; + DBusError error; + const char *path, *replace = "replace"; + + assert(s); + + dbus_error_init(&error); + + if (!(target = translate_runlevel(runlevel))) { + log_warning("Got request for unknown runlevel %c, ignoring.", runlevel); + goto finish; + } + + log_debug("Running request %s", target); + + if (!(m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1", "GetUnit"))) { + log_error("Could not allocate message."); + goto finish; + } + + if (!dbus_message_append_args(m, + DBUS_TYPE_STRING, &target, + DBUS_TYPE_INVALID)) { + log_error("Could not attach group information to signal message."); + goto finish; + } + + if (!(reply = dbus_connection_send_with_reply_and_block(s->bus, m, -1, &error))) { + log_error("Failed to get unit path: %s", error.message); + goto finish; + } + + if (!dbus_message_get_args(reply, &error, + DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID)) { + log_error("Failed to parse unit path: %s", error.message); + goto finish; + } + + dbus_message_unref(m); + if (!(m = dbus_message_new_method_call("org.freedesktop.systemd1", path, "org.freedesktop.systemd1.Unit", "Start"))) { + log_error("Could not allocate message."); + goto finish; + } + + if (!dbus_message_append_args(m, + DBUS_TYPE_STRING, &replace, + DBUS_TYPE_INVALID)) { + log_error("Could not attach group information to signal message."); + goto finish; + } + + dbus_message_unref(reply); + if (!(reply = dbus_connection_send_with_reply_and_block(s->bus, m, -1, &error))) { + log_error("Failed to start unit: %s", error.message); + goto finish; + } + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); +} + +static void request_process(Server *s, const struct init_request *req) { + assert(s); + assert(req); + + if (req->magic != INIT_MAGIC) { + log_error("Got initctl request with invalid magic. Ignoring."); + return; + } + + switch (req->cmd) { + + case INIT_CMD_RUNLVL: + if (!isprint(req->runlevel)) + log_error("Got invalid runlevel. Ignoring."); + else + change_runlevel(s, req->runlevel); + return; + + case INIT_CMD_POWERFAIL: + case INIT_CMD_POWERFAILNOW: + case INIT_CMD_POWEROK: + log_warning("Received UPS/power initctl request. This is not implemented in systemd. Upgrade your UPS daemon!"); + return; + + case INIT_CMD_CHANGECONS: + log_warning("Received console change initctl request. This is not implemented in systemd."); + return; + + case INIT_CMD_SETENV: + case INIT_CMD_UNSETENV: + log_warning("Received environment initctl request. This is not implemented in systemd."); + return; + + default: + log_warning("Received unknown initctl request. Ignoring."); + return; + } +} + +static int fifo_process(Fifo *f) { + ssize_t l; + + assert(f); + + errno = EIO; + if ((l = read(f->fd, ((uint8_t*) &f->buffer) + f->bytes_read, sizeof(f->buffer) - f->bytes_read)) <= 0) { + + if (errno == EAGAIN) + return 0; + + log_warning("Failed to read from fifo: %s", strerror(errno)); + return -1; + } + + f->bytes_read += l; + assert(f->bytes_read <= sizeof(f->buffer)); + + if (f->bytes_read == sizeof(f->buffer)) { + request_process(f->server, &f->buffer); + f->bytes_read = 0; + } + + return 0; +} + +static void fifo_free(Fifo *f) { + assert(f); + + if (f->server) { + assert(f->server->n_fifos > 0); + f->server->n_fifos--; + LIST_REMOVE(Fifo, fifo, f->server->fifos, f); + } + + if (f->fd >= 0) { + if (f->server) + epoll_ctl(f->server->epoll_fd, EPOLL_CTL_DEL, f->fd, NULL); + + assert_se(close_nointr(f->fd) == 0); + } + + free(f); +} + +static int verify_environment(unsigned *n_sockets) { + unsigned long long pid; + const char *e; + int r; + unsigned ns; + + assert_se(n_sockets); + + if (!(e = getenv("LISTEN_PID"))) { + log_error("Missing $LISTEN_PID environment variable."); + return -ENOENT; + } + + if ((r = safe_atollu(e, &pid)) < 0) { + log_error("Failed to parse $LISTEN_PID: %s", strerror(-r)); + return r; + } + + if (pid != (unsigned long long) getpid()) { + log_error("Socket nor for me."); + return -ENOENT; + } + + if (!(e = getenv("LISTEN_FDS"))) { + log_error("Missing $LISTEN_FDS environment variable."); + return -ENOENT; + } + + if ((r = safe_atou(e, &ns)) < 0) { + log_error("Failed to parse $LISTEN_FDS: %s", strerror(-r)); + return -E2BIG; + } + + if (ns <= 0 || ns > SERVER_FD_MAX) { + log_error("Wrong number of file descriptors passed: %s", e); + return -E2BIG; + } + + *n_sockets = ns; + + return 0; +} + +static void server_done(Server *s) { + assert(s); + + while (s->fifos) + fifo_free(s->fifos); + + if (s->epoll_fd >= 0) + assert_se(close_nointr(s->epoll_fd) == 0); + + if (s->bus) + dbus_connection_unref(s->bus); +} + +static int server_init(Server *s, unsigned n_sockets) { + int r; + unsigned i; + DBusError error; + + assert(s); + assert(n_sockets > 0); + + dbus_error_init(&error); + + zero(*s); + + if ((s->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) { + r = -errno; + log_error("Failed to create epoll object: %s", strerror(errno)); + goto fail; + } + + for (i = 0; i < n_sockets; i++) { + struct epoll_event ev; + Fifo *f; + + if (!(f = new0(Fifo, 1))) { + r = -ENOMEM; + log_error("Failed to create fifo object: %s", strerror(errno)); + goto fail; + } + + f->fd = -1; + + zero(ev); + ev.events = EPOLLIN; + ev.data.ptr = f; + if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, SERVER_FD_START+i, &ev) < 0) { + r = -errno; + fifo_free(f); + log_error("Failed to add fifo fd to epoll object: %s", strerror(errno)); + goto fail; + } + + f->fd = SERVER_FD_START+i; + LIST_PREPEND(Fifo, fifo, s->fifos, f); + f->server = s; + s->n_fifos ++; + } + + if (!(s->bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error))) { + log_error("Failed to get D-Bus connection: %s", error.message); + goto fail; + } + + return 0; + +fail: + server_done(s); + + dbus_error_free(&error); + return r; +} + +static int process_event(Server *s, struct epoll_event *ev) { + int r; + Fifo *f; + + assert(s); + + if (!(ev->events & EPOLLIN)) { + log_info("Got invalid event from epoll. (3)"); + return -EIO; + } + + f = (Fifo*) ev->data.ptr; + + if ((r = fifo_process(f)) < 0) { + log_info("Got error on fifo: %s", strerror(-r)); + fifo_free(f); + return r; + } + + return 0; +} + +int main(int argc, char *argv[]) { + Server server; + int r = 3; + unsigned n; + + log_info("systemd-initctl running as pid %llu", (unsigned long long) getpid()); + + if (verify_environment(&n) < 0) + return 1; + + if (server_init(&server, n) < 0) + return 2; + + for (;;) { + struct epoll_event event; + int k; + + if ((k = epoll_wait(server.epoll_fd, + &event, 1, + TIMEOUT)) < 0) { + + if (errno == EINTR) + continue; + + log_error("epoll_wait() failed: %s", strerror(errno)); + goto fail; + } + + if (k <= 0) + break; + + if ((k = process_event(&server, &event)) < 0) + goto fail; + } + r = 0; + +fail: + server_done(&server); + + log_info("systemd-initctl stopped as pid %llu", (unsigned long long) getpid()); + + dbus_shutdown(); + + return r; +} diff --git a/initreq.h b/initreq.h new file mode 100644 index 0000000000..6f6547bcb9 --- /dev/null +++ b/initreq.h @@ -0,0 +1,77 @@ +/* + * initreq.h Interface to talk to init through /dev/initctl. + * + * Copyright (C) 1995-2004 Miquel van Smoorenburg + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * Version: @(#)initreq.h 1.28 31-Mar-2004 MvS + * + */ +#ifndef _INITREQ_H +#define _INITREQ_H + +#include + +#if defined(__FreeBSD_kernel__) +# define INIT_FIFO "/etc/.initctl" +#else +# define INIT_FIFO "/dev/initctl" +#endif + +#define INIT_MAGIC 0x03091969 +#define INIT_CMD_START 0 +#define INIT_CMD_RUNLVL 1 +#define INIT_CMD_POWERFAIL 2 +#define INIT_CMD_POWERFAILNOW 3 +#define INIT_CMD_POWEROK 4 +#define INIT_CMD_BSD 5 +#define INIT_CMD_SETENV 6 +#define INIT_CMD_UNSETENV 7 + +#define INIT_CMD_CHANGECONS 12345 + +#ifdef MAXHOSTNAMELEN +# define INITRQ_HLEN MAXHOSTNAMELEN +#else +# define INITRQ_HLEN 64 +#endif + +/* + * This is what BSD 4.4 uses when talking to init. + * Linux doesn't use this right now. + */ +struct init_request_bsd { + char gen_id[8]; /* Beats me.. telnetd uses "fe" */ + char tty_id[16]; /* Tty name minus /dev/tty */ + char host[INITRQ_HLEN]; /* Hostname */ + char term_type[16]; /* Terminal type */ + int signal; /* Signal to send */ + int pid; /* Process to send to */ + char exec_name[128]; /* Program to execute */ + char reserved[128]; /* For future expansion. */ +}; + + +/* + * Because of legacy interfaces, "runlevel" and "sleeptime" + * aren't in a seperate struct in the union. + * + * The weird sizes are because init expects the whole + * struct to be 384 bytes. + */ +struct init_request { + int magic; /* Magic number */ + int cmd; /* What kind of request */ + int runlevel; /* Runlevel to change to */ + int sleeptime; /* Time between TERM and KILL */ + union { + struct init_request_bsd bsd; + char data[368]; + } i; +}; + +#endif -- cgit v1.2.3-54-g00ecf From 70fcff314feff469a8e61dbe5017ed74f5e0a09d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 13 Apr 2010 05:18:12 +0200 Subject: units: install a few basic units by default --- .gitignore | 2 ++ Makefile.am | 32 +++++++++++++++++++++++++++----- units/emergency.service | 10 ++++++++++ units/systemd-initctl.service.in | 6 ++++++ units/systemd-initctl.socket | 6 ++++++ units/systemd-logger.service.in | 6 ++++++ units/systemd-logger.socket | 5 +++++ 7 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 units/emergency.service create mode 100644 units/systemd-initctl.service.in create mode 100644 units/systemd-initctl.socket create mode 100644 units/systemd-logger.service.in create mode 100644 units/systemd-logger.socket (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index ac80a41582..f994578e9d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +systemd-initctl.service +systemd-logger.service systemd-cgroups-agent systemd-initctl systemd diff --git a/Makefile.am b/Makefile.am index 18f27c4251..361786983d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -21,14 +21,17 @@ pkgsysconfdir=$(sysconfdir)/systemd dbuspolicydir=$(sysconfdir)/dbus-1/system.d udevrulesdir=/lib/udev/rules.d +systemunitdir=$(pkgdatadir)/system +sessionunitdir=$(pkgdatadir)/system + AM_CPPFLAGS = \ -include $(top_builddir)/config.h \ -DSYSTEM_CONFIG_UNIT_PATH=\"$(pkgsysconfdir)/system\" \ - -DSYSTEM_DATA_UNIT_PATH=\"$(pkgdatadir)/system\" \ + -DSYSTEM_DATA_UNIT_PATH=\"$(systemunitdir)\" \ -DSYSTEM_SYSVINIT_PATH=\"$(SYSTEM_SYSVINIT_PATH)\" \ -DSYSTEM_SYSVRCND_PATH=\"$(SYSTEM_SYSVRCND_PATH)\" \ -DSESSION_CONFIG_UNIT_PATH=\"$(pkgsysconfdir)/session\" \ - -DSESSION_DATA_UNIT_PATH=\"$(pkgdatadir)/session\" \ + -DSESSION_DATA_UNIT_PATH=\"$(sessionunitdir)\" \ -DCGROUP_AGENT_PATH=\"$(pkglibexecdir)/systemd-cgroups-agent\" sbin_PROGRAMS = \ @@ -53,9 +56,21 @@ dbuspolicy_DATA = \ udevrules_DATA = \ 99-systemd.rules +systemunit_DATA = \ + units/emergency.service \ + systemd-initctl.service \ + units/systemd-initctl.socket \ + systemd-logger.service \ + units/systemd-logger.socket + EXTRA_DIST = \ - org.freedesktop.systemd1.conf - 99-systemd.rules + org.freedesktop.systemd1.conf \ + 99-systemd.rules \ + units/emergency.service \ + units/systemd-initctl.service.in \ + units/systemd-initctl.socket \ + units/systemd-logger.service.in \ + units/systemd-logger.socket BASIC_SOURCES= \ util.c \ @@ -193,8 +208,15 @@ systemadm_SOURCES = \ systemadm_CPPFLAGS = $(AM_CPPFLAGS) $(DBUSGLIB_CFLAGS) $(GTK_CFLAGS) systemadm_LDADD = $(DBUSGLIB_LIBS) $(GTK_LIBS) +systemd-initctl.service: units/systemd-initctl.service.in Makefile + sed -e 's,@libexecdir\@,$(libexecdir),g' < $< > $@ + +systemd-logger.service: units/systemd-logger.service.in Makefile + sed -e 's,@libexecdir\@,$(libexecdir),g' < $< > $@ + CLEANFILES = \ systemd-interfaces.c \ systemctl.c \ systemadm.c \ - systemd-cgroups-agent + systemd-initctl.service \ + systemd-logger.service diff --git a/units/emergency.service b/units/emergency.service new file mode 100644 index 0000000000..2e9d049f2d --- /dev/null +++ b/units/emergency.service @@ -0,0 +1,10 @@ +[Meta] +Description=Emergency Shell + +[Service] +ExecStart=/bin/sh +Type=simple +StandardInput=tty +Restart=restart-always +RestartSec=0 +KillMode=process diff --git a/units/systemd-initctl.service.in b/units/systemd-initctl.service.in new file mode 100644 index 0000000000..512392e4d4 --- /dev/null +++ b/units/systemd-initctl.service.in @@ -0,0 +1,6 @@ +[Meta] +Description=systemd /dev/initctl Compatibility + +[Service] +ExecStart=@libexecdir@/systemd-initctl +Type=simple diff --git a/units/systemd-initctl.socket b/units/systemd-initctl.socket new file mode 100644 index 0000000000..86e7f40276 --- /dev/null +++ b/units/systemd-initctl.socket @@ -0,0 +1,6 @@ +[Meta] +Description=systemd /dev/initctl Compatibility + +[Socket] +ListenFIFO=/dev/initctl +SocketMode=0600 diff --git a/units/systemd-logger.service.in b/units/systemd-logger.service.in new file mode 100644 index 0000000000..6723500e0e --- /dev/null +++ b/units/systemd-logger.service.in @@ -0,0 +1,6 @@ +[Meta] +Description=systemd Logging Daemon + +[Service] +ExecStart=@libexecdir@/systemd-logger +Type=simple diff --git a/units/systemd-logger.socket b/units/systemd-logger.socket new file mode 100644 index 0000000000..eb012c8dde --- /dev/null +++ b/units/systemd-logger.socket @@ -0,0 +1,5 @@ +[Meta] +Description=systemd Logging Socket + +[Socket] +ListenStream=@/org/freedesktop/systemd1/logger -- cgit v1.2.3-54-g00ecf From 15ae422b7471cf6f41ccf450243d8afd8ea0a054 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 21 Apr 2010 22:15:06 +0200 Subject: execute: support basic filesystem namespacing --- .gitignore | 1 + Makefile.am | 15 ++- conf-parser.c | 66 ++++++++++- conf-parser.h | 1 + execute.c | 67 ++++++++++-- execute.h | 4 + load-fragment.c | 45 +++++++- main.c | 1 - missing.h | 27 +++++ namespace.c | 334 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ namespace.h | 34 ++++++ strv.h | 3 + test-ns.c | 57 ++++++++++ util.c | 33 ++++++ util.h | 1 + 15 files changed, 675 insertions(+), 14 deletions(-) create mode 100644 namespace.c create mode 100644 namespace.h create mode 100644 test-ns.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index f994578e9d..ec58ed7667 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +test-ns systemd-initctl.service systemd-logger.service systemd-cgroups-agent diff --git a/Makefile.am b/Makefile.am index 567490ea63..7afa2f1576 100644 --- a/Makefile.am +++ b/Makefile.am @@ -52,7 +52,8 @@ pkglibexec_PROGRAMS = \ noinst_PROGRAMS = \ test-engine \ - test-job-type + test-job-type \ + test-ns dbuspolicy_DATA = \ org.freedesktop.systemd1.conf @@ -161,7 +162,9 @@ COMMON_SOURCES= \ unit-name.c \ unit-name.h \ fdset.c \ - fdset.h + fdset.h \ + namespace.h \ + namespace.c systemd_SOURCES = \ $(COMMON_SOURCES) \ @@ -192,6 +195,14 @@ test_job_type_SOURCES = \ test_job_type_CPPFLAGS = $(systemd_CPPFLAGS) test_job_type_LDADD = $(systemd_LDADD) +test_ns_SOURCES = \ + $(BASIC_SOURCES) \ + test-ns.c \ + namespace.c + +test_ns_CPPFLAGS = $(systemd_CPPFLAGS) +test_ns_LDADD = $(systemd_LDADD) + systemd_logger_SOURCES = \ $(BASIC_SOURCES) \ logger.c diff --git a/conf-parser.c b/conf-parser.c index 2cf90f2def..6994211b10 100644 --- a/conf-parser.c +++ b/conf-parser.c @@ -329,7 +329,7 @@ int config_parse_path( assert(rvalue); assert(data); - if (*rvalue != '/') { + if (!path_is_absolute(rvalue)) { log_error("[%s:%u] Not an absolute path: %s", filename, line, rvalue); return -EINVAL; } @@ -394,3 +394,67 @@ fail: return -ENOMEM; } + +int config_parse_path_strv( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + char*** sv = data; + char **n; + char *w; + unsigned k; + size_t l; + char *state; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + k = strv_length(*sv); + FOREACH_WORD_QUOTED(w, l, rvalue, state) + k++; + + if (!(n = new(char*, k+1))) + return -ENOMEM; + + k = 0; + if (*sv) + for (; (*sv)[k]; k++) + n[k] = (*sv)[k]; + + FOREACH_WORD_QUOTED(w, l, rvalue, state) { + if (!(n[k] = strndup(w, l))) { + r = -ENOMEM; + goto fail; + } + + if (!path_is_absolute(n[k])) { + log_error("[%s:%u] Not an absolute path: %s", filename, line, rvalue); + r = -EINVAL; + goto fail; + } + + k++; + } + + n[k] = NULL; + free(*sv); + *sv = n; + + return 0; + +fail: + free(n[k]); + for (; k > 0; k--) + free(n[k-1]); + free(n); + + return r; +} diff --git a/conf-parser.h b/conf-parser.h index 33ceed8b3f..bea2a8e9be 100644 --- a/conf-parser.h +++ b/conf-parser.h @@ -50,5 +50,6 @@ int config_parse_bool(const char *filename, unsigned line, const char *section, int config_parse_string(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata); int config_parse_path(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata); int config_parse_strv(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata); +int config_parse_path_strv(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata); #endif diff --git a/execute.c b/execute.c index 38547677cf..fe3dc8b251 100644 --- a/execute.c +++ b/execute.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "execute.h" #include "strv.h" @@ -43,6 +44,7 @@ #include "ioprio.h" #include "securebits.h" #include "cgroup.h" +#include "namespace.h" /* This assumes there is a 'tty' group */ #define TTY_MODE 0620 @@ -794,8 +796,6 @@ int exec_spawn(ExecCommand *command, goto fail; } - umask(context->umask); - if (confirm_spawn) { char response; @@ -900,6 +900,19 @@ int exec_spawn(ExecCommand *command, goto fail; } + if (strv_length(context->read_write_dirs) > 0 || + strv_length(context->read_only_dirs) > 0 || + strv_length(context->inaccessible_dirs) > 0 || + context->mount_flags != MS_SHARED || + context->private_tmp) + if ((r = setup_namespace( + context->read_write_dirs, + context->read_only_dirs, + context->inaccessible_dirs, + context->private_tmp, + context->mount_flags)) < 0) + goto fail; + if (context->user) { username = context->user; if (get_user_creds(&username, &uid, &gid, &home) < 0) { @@ -920,6 +933,8 @@ int exec_spawn(ExecCommand *command, goto fail; } + umask(context->umask); + if (apply_chroot) { if (context->root_directory) if (chroot(context->root_directory) < 0) { @@ -1066,6 +1081,7 @@ void exec_context_init(ExecContext *c) { c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 0); c->cpu_sched_policy = SCHED_OTHER; c->syslog_priority = LOG_DAEMON|LOG_INFO; + c->mount_flags = MS_SHARED; } void exec_context_done(ExecContext *c) { @@ -1105,6 +1121,15 @@ void exec_context_done(ExecContext *c) { cap_free(c->capabilities); c->capabilities = NULL; } + + strv_free(c->read_only_dirs); + c->read_only_dirs = NULL; + + strv_free(c->read_write_dirs); + c->read_write_dirs = NULL; + + strv_free(c->inaccessible_dirs); + c->inaccessible_dirs = NULL; } void exec_command_done(ExecCommand *c) { @@ -1143,6 +1168,15 @@ void exec_command_free_array(ExecCommand **c, unsigned n) { } } +static void strv_fprintf(FILE *f, char **l) { + char **g; + + assert(f); + + STRV_FOREACH(g, l) + fprintf(f, " %s", *g); +} + void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { char ** e; unsigned i; @@ -1157,11 +1191,13 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { "%sUMask: %04o\n" "%sWorkingDirectory: %s\n" "%sRootDirectory: %s\n" - "%sNonBlocking: %s\n", + "%sNonBlocking: %s\n" + "%sPrivateTmp: %s\n", prefix, c->umask, prefix, c->working_directory ? c->working_directory : "/", prefix, c->root_directory ? c->root_directory : "/", - prefix, yes_no(c->non_blocking)); + prefix, yes_no(c->non_blocking), + prefix, yes_no(c->private_tmp)); if (c->environment) for (e = c->environment; *e; e++) @@ -1269,14 +1305,27 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { if (c->group) fprintf(f, "%sGroup: %s", prefix, c->group); - if (c->supplementary_groups) { - char **g; - + if (strv_length(c->supplementary_groups) > 0) { fprintf(f, "%sSupplementaryGroups:", prefix); + strv_fprintf(f, c->supplementary_groups); + fputs("\n", f); + } - STRV_FOREACH(g, c->supplementary_groups) - fprintf(f, " %s", *g); + if (strv_length(c->read_write_dirs) > 0) { + fprintf(f, "%sReadWriteDirs:", prefix); + strv_fprintf(f, c->read_write_dirs); + fputs("\n", f); + } + + if (strv_length(c->read_only_dirs) > 0) { + fprintf(f, "%sReadOnlyDirs:", prefix); + strv_fprintf(f, c->read_only_dirs); + fputs("\n", f); + } + if (strv_length(c->inaccessible_dirs) > 0) { + fprintf(f, "%sInaccessibleDirs:", prefix); + strv_fprintf(f, c->inaccessible_dirs); fputs("\n", f); } } diff --git a/execute.h b/execute.h index cafaf6b631..f820d56cb8 100644 --- a/execute.h +++ b/execute.h @@ -109,6 +109,9 @@ struct ExecContext { char *group; char **supplementary_groups; + char **read_write_dirs, **read_only_dirs, **inaccessible_dirs; + unsigned long mount_flags; + uint64_t capability_bounding_set_drop; cap_t capabilities; @@ -116,6 +119,7 @@ struct ExecContext { bool cpu_sched_reset_on_fork; bool non_blocking; + bool private_tmp; bool oom_adjust_set:1; bool nice_set:1; diff --git a/load-fragment.c b/load-fragment.c index 03205f14b4..680f04171f 100644 --- a/load-fragment.c +++ b/load-fragment.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "unit.h" #include "strv.h" @@ -909,6 +910,43 @@ static int config_parse_sysv_priority( DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode"); +static int config_parse_mount_flags( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + ExecContext *c = data; + char *w; + size_t l; + char *state; + unsigned long flags = 0; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + FOREACH_WORD(w, l, rvalue, state) { + if (strncmp(w, "shared", l) == 0) + flags |= MS_SHARED; + else if (strncmp(w, "slave", l) == 0) + flags |= MS_SLAVE; + else if (strncmp(w, "private", l) == 0) + flags |= MS_PRIVATE; + else { + log_error("[%s:%u] Failed to parse mount flags: %s", filename, line, rvalue); + return -EINVAL; + } + } + + c->mount_flags = flags; + return 0; +} + #define FOLLOW_MAX 8 static int open_follow(char **filename, FILE **_f, Set *names, char **_final) { @@ -1149,7 +1187,12 @@ static int load_from_path(Unit *u, const char *path) { { "LimitNICE", config_parse_limit, &(context).rlimit[RLIMIT_NICE], section }, \ { "LimitRTPRIO", config_parse_limit, &(context).rlimit[RLIMIT_RTPRIO], section }, \ { "LimitRTTIME", config_parse_limit, &(context).rlimit[RLIMIT_RTTIME], section }, \ - { "ControlGroup", config_parse_cgroup, u, section } + { "ControlGroup", config_parse_cgroup, u, section }, \ + { "ReadWriteDirectories", config_parse_path_strv, &(context).read_write_dirs, section }, \ + { "ReadOnlyDirectories", config_parse_path_strv, &(context).read_only_dirs, section }, \ + { "InaccessibleDirectories",config_parse_path_strv, &(context).inaccessible_dirs, section }, \ + { "PrivateTmp", config_parse_bool, &(context).private_tmp, section }, \ + { "MountFlags", config_parse_mount_flags, &(context), section } const ConfigItem items[] = { { "Names", config_parse_names, u, "Unit" }, diff --git a/main.c b/main.c index 29be801125..06ad42959b 100644 --- a/main.c +++ b/main.c @@ -701,7 +701,6 @@ int main(int argc, char *argv[]) { break; case MANAGER_REEXECUTE: - if (prepare_reexecute(m, &serialization, &fds) < 0) goto finish; diff --git a/missing.h b/missing.h index 6f1e5998ec..a8b5e80b07 100644 --- a/missing.h +++ b/missing.h @@ -1,12 +1,39 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + #ifndef foomissinghfoo #define foomissinghfoo +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + /* Missing glibc definitions to access certain kernel APIs */ #include +#include #ifndef RLIMIT_RTTIME #define RLIMIT_RTTIME 15 #endif +static inline int pivot_root(const char *new_root, const char *put_old) { + return syscall(SYS_pivot_root, new_root, put_old); +} + + #endif diff --git a/namespace.c b/namespace.c new file mode 100644 index 0000000000..570b4ce387 --- /dev/null +++ b/namespace.c @@ -0,0 +1,334 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "strv.h" +#include "util.h" +#include "namespace.h" +#include "missing.h" + +typedef enum PathMode { + /* This is ordered by priority! */ + INACCESSIBLE, + READONLY, + PRIVATE, + READWRITE +} PathMode; + +typedef struct Path { + const char *path; + PathMode mode; +} Path; + +static int append_paths(Path **p, char **strv, PathMode mode) { + char **i; + + STRV_FOREACH(i, strv) { + + if (!path_is_absolute(*i)) + return -EINVAL; + + (*p)->path = *i; + (*p)->mode = mode; + (*p)++; + } + + return 0; +} + +static int path_compare(const void *a, const void *b) { + const Path *p = a, *q = b; + + if (path_equal(p->path, q->path)) { + + /* If the paths are equal, check the mode */ + if (p->mode < q->mode) + return -1; + + if (p->mode > q->mode) + return 1; + + return 0; + } + + /* If the paths are not equal, then order prefixes first */ + if (path_startswith(p->path, q->path)) + return 1; + + if (path_startswith(q->path, p->path)) + return -1; + + return 0; +} + +static void drop_duplicates(Path *p, unsigned *n, bool *need_inaccessible, bool *need_private) { + Path *f, *t, *previous; + + assert(p); + assert(n); + assert(need_inaccessible); + assert(need_private); + + for (f = p, t = p, previous = NULL; f < p+*n; f++) { + + if (previous && path_equal(f->path, previous->path)) + continue; + + t->path = f->path; + t->mode = f->mode; + + if (t->mode == PRIVATE) + *need_private = true; + + if (t->mode == INACCESSIBLE) + *need_inaccessible = true; + + previous = t; + + t++; + } + + *n = t - p; +} + +static int apply_mount(Path *p, const char *root_dir, const char *inaccessible_dir, const char *private_dir, unsigned long flags) { + const char *what; + char *where; + int r; + bool read_only = false; + + assert(p); + assert(root_dir); + assert(inaccessible_dir); + assert(private_dir); + + if (!(where = strappend(root_dir, p->path))) + return -ENOMEM; + + switch (p->mode) { + + case INACCESSIBLE: + what = inaccessible_dir; + read_only = true; + break; + + case READONLY: + read_only = true; + /* Fall through */ + + case READWRITE: + what = p->path; + break; + + case PRIVATE: + what = private_dir; + break; + } + + if ((r = mount(what, where, NULL, MS_BIND|MS_REC, NULL)) >= 0) { + log_debug("Successfully mounted %s to %s", what, where); + + /* The bind mount will always inherit the original + * flags. If we want to set any flag we need + * to do so in a second indepdant step. */ + if (flags) + r = mount(NULL, where, NULL, MS_REMOUNT|MS_REC|flags, NULL); + + /* Avoid expontial growth of trees */ + if (r >= 0 && path_equal(p->path, "/")) + r = mount(NULL, where, NULL, MS_REMOUNT|MS_UNBINDABLE, NULL); + + if (r >= 0 && read_only) + r = mount(NULL, where, NULL, MS_REMOUNT|MS_RDONLY, NULL); + + if (r < 0) { + r = -errno; + umount2(where, MNT_DETACH); + } + } + + free(where); + return r; +} + +int setup_namespace( + char **writable, + char **readable, + char **inaccessible, + bool private_tmp, + unsigned long flags) { + + char + tmp_dir[] = "/tmp/systemd-namespace-XXXXXX", + root_dir[] = "/tmp/systemd-namespace-XXXXXX/root", + old_root_dir[] = "/tmp/systemd-namespace-XXXXXX/root/tmp/old-root-XXXXXX", + inaccessible_dir[] = "/tmp/systemd-namespace-XXXXXX/inaccessible", + private_dir[] = "/tmp/systemd-namespace-XXXXXX/private"; + + Path *paths, *p; + unsigned n; + bool need_private = false, need_inaccessible = false; + bool remove_tmp = false, remove_root = false, remove_old_root = false, remove_inaccessible = false, remove_private = false; + int r; + const char *t; + + n = + strv_length(writable) + + strv_length(readable) + + strv_length(inaccessible) + + (private_tmp ? 2 : 1); + + if (!(paths = new(Path, n))) + return -ENOMEM; + + p = paths; + if ((r = append_paths(&p, writable, READWRITE)) < 0 || + (r = append_paths(&p, readable, READONLY)) < 0 || + (r = append_paths(&p, inaccessible, INACCESSIBLE)) < 0) + goto fail; + + if (private_tmp) { + p->path = "/tmp"; + p->mode = PRIVATE; + p++; + } + + p->path = "/"; + p->mode = READWRITE; + p++; + + assert(paths + n == p); + + qsort(paths, n, sizeof(Path), path_compare); + drop_duplicates(paths, &n, &need_inaccessible, &need_private); + + if (!mkdtemp(tmp_dir)) { + r = -errno; + goto fail; + } + remove_tmp = true; + + memcpy(root_dir, tmp_dir, sizeof(tmp_dir)-1); + if (mkdir(root_dir, 0777) < 0) { + r = -errno; + goto fail; + } + remove_root = true; + + if (need_inaccessible) { + memcpy(inaccessible_dir, tmp_dir, sizeof(tmp_dir)-1); + if (mkdir(inaccessible_dir, 0) < 0) { + r = -errno; + goto fail; + } + remove_inaccessible = true; + } + + if (need_private) { + memcpy(private_dir, tmp_dir, sizeof(tmp_dir)-1); + if (mkdir(private_dir, 0777 + S_ISVTX) < 0) { + r = -errno; + goto fail; + } + remove_private = true; + } + + if (unshare(CLONE_NEWNS) < 0) { + r = -errno; + goto fail; + } + + /* We assume that by default mount events from us won't be + * propagated to the root namespace. */ + + for (p = paths; p < paths + n; p++) + if ((r = apply_mount(p, root_dir, inaccessible_dir, private_dir, flags)) < 0) + goto undo_mounts; + + memcpy(old_root_dir, tmp_dir, sizeof(tmp_dir)-1); + if (!mkdtemp(old_root_dir)) { + r = -errno; + goto undo_mounts; + } + remove_old_root = true; + + if (chdir(root_dir) < 0) { + r = -errno; + goto undo_mounts; + } + + if (pivot_root(root_dir, old_root_dir) < 0) { + r = -errno; + goto undo_mounts; + } + + t = old_root_dir + sizeof(root_dir) - 1; + if (umount2(t, MNT_DETACH) < 0) + /* At this point it's too late to turn anything back, + * since we are already in the new root. */ + return -errno; + + if (rmdir(t) < 0) + return -errno; + + return 0; + +undo_mounts: + + for (p--; p >= paths; p--) { + char full_path[PATH_MAX]; + + snprintf(full_path, sizeof(full_path), "%s%s", root_dir, p->path); + char_array_0(full_path); + + umount2(full_path, MNT_DETACH); + } + +fail: + if (remove_old_root) + rmdir(old_root_dir); + + if (remove_inaccessible) + rmdir(inaccessible_dir); + + if (remove_private) + rmdir(private_dir); + + if (remove_root) + rmdir(root_dir); + + if (remove_tmp) + rmdir(tmp_dir); + + free(paths); + + return r; +} diff --git a/namespace.h b/namespace.h new file mode 100644 index 0000000000..6128646391 --- /dev/null +++ b/namespace.h @@ -0,0 +1,34 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#ifndef foonamespacehfoo +#define foonamespacehfoo + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include + +int setup_namespace( + char **writable, + char **readable, + char **inaccessible, + bool private_tmp, + unsigned long flags); + +#endif diff --git a/strv.h b/strv.h index 7ee9a95a8f..603e0e7282 100644 --- a/strv.h +++ b/strv.h @@ -22,6 +22,9 @@ along with systemd; If not, see . ***/ +#include +#include + #include "macro.h" char *strv_find(char **l, const char *name); diff --git a/test-ns.c b/test-ns.c new file mode 100644 index 0000000000..baf42f6d44 --- /dev/null +++ b/test-ns.c @@ -0,0 +1,57 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "namespace.h" +#include "log.h" + +int main(int argc, char *argv[]) { + const char * const writable[] = { + "/home", + NULL + }; + + const char * const readable[] = { + "/var", + NULL + }; + + const char * const inaccessible[] = { + "/home/lennart/projects", + NULL + }; + + int r; + + if ((r = setup_namespace((char**) writable, (char**) readable, (char**) inaccessible, true, MS_SHARED)) < 0) { + log_error("Failed to setup namespace: %s", strerror(-r)); + return 1; + } + + execl("/bin/sh", "/bin/sh", NULL); + log_error("execl(): %m"); + + return 1; +} diff --git a/util.c b/util.c index 49f5b4be13..e9f7813b8b 100644 --- a/util.c +++ b/util.c @@ -1140,6 +1140,39 @@ bool path_startswith(const char *path, const char *prefix) { } } +bool path_equal(const char *a, const char *b) { + assert(a); + assert(b); + + if ((a[0] == '/') != (b[0] == '/')) + return false; + + for (;;) { + size_t j, k; + + a += strspn(a, "/"); + b += strspn(b, "/"); + + if (*a == 0 && *b == 0) + return true; + + if (*a == 0 || *b == 0) + return false; + + j = strcspn(a, "/"); + k = strcspn(b, "/"); + + if (j != k) + return false; + + if (memcmp(a, b, j) != 0) + return false; + + a += j; + b += k; + } +} + char *ascii_strlower(char *t) { char *p; diff --git a/util.h b/util.h index 6f87894d15..0ef3df6d57 100644 --- a/util.h +++ b/util.h @@ -157,6 +157,7 @@ char *cunescape(const char *s); char *path_kill_slashes(char *path); bool path_startswith(const char *path, const char *prefix); +bool path_equal(const char *a, const char *b); char *ascii_strlower(char *path); -- cgit v1.2.3-54-g00ecf From 58c16653226a627935a845f19276172f95d10c13 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 6 May 2010 22:12:16 +0200 Subject: gitignore: ignore files ending in ~ --- .gitignore | 1 + 1 file changed, 1 insertion(+) (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index ec58ed7667..d2ce04d301 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +*~ test-ns systemd-initctl.service systemd-logger.service -- cgit v1.2.3-54-g00ecf From af5bc85dc1297079edc9890861aaa38de0ec30df Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 9 May 2010 18:02:38 +0200 Subject: loopback: configure lo device on bootup --- .gitignore | 1 + Makefile.am | 13 ++- loopback-setup.c | 276 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ loopback-setup.h | 27 ++++++ main.c | 5 +- test-loopback.c | 35 +++++++ 6 files changed, 355 insertions(+), 2 deletions(-) create mode 100644 loopback-setup.c create mode 100644 loopback-setup.h create mode 100644 test-loopback.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index d2ce04d301..b9cb51c0e7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *~ test-ns +test-loopback systemd-initctl.service systemd-logger.service systemd-cgroups-agent diff --git a/Makefile.am b/Makefile.am index c98ad0b8b9..c38c7d575c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -53,7 +53,8 @@ pkglibexec_PROGRAMS = \ noinst_PROGRAMS = \ test-engine \ test-job-type \ - test-ns + test-ns \ + test-loopback dbuspolicy_DATA = \ org.freedesktop.systemd1.conf @@ -155,6 +156,8 @@ COMMON_SOURCES = \ mount-setup.h \ hostname-setup.c \ hostname-setup.h \ + loopback-setup.c \ + loopback-setup.h \ utmp-wtmp.c \ utmp-wtmp.h \ specifier.c \ @@ -203,6 +206,14 @@ test_ns_SOURCES = \ test_ns_CPPFLAGS = $(systemd_CPPFLAGS) test_ns_LDADD = $(systemd_LDADD) +test_loopback_SOURCES = \ + $(BASIC_SOURCES) \ + test-loopback.c \ + loopback-setup.c + +test_loopback_CPPFLAGS = $(systemd_CPPFLAGS) +test_loopback_LDADD = $(systemd_LDADD) + systemd_logger_SOURCES = \ $(BASIC_SOURCES) \ logger.c diff --git a/loopback-setup.c b/loopback-setup.c new file mode 100644 index 0000000000..e37bf4190b --- /dev/null +++ b/loopback-setup.c @@ -0,0 +1,276 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "macro.h" +#include "loopback-setup.h" + +enum { + REQUEST_NONE = 0, + REQUEST_ADDRESS_IPV4 = 1, + REQUEST_ADDRESS_IPV6 = 2, + REQUEST_FLAGS = 4, + REQUEST_ALL = 7 +}; + +#define NLMSG_TAIL(nmsg) \ + ((struct rtattr *) (((uint8_t*) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) + +static int add_rtattr(struct nlmsghdr *n, size_t max_length, int type, const void *data, size_t data_length) { + size_t length; + struct rtattr *rta; + + length = RTA_LENGTH(data_length); + + if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(length) > max_length) + return -E2BIG; + + rta = NLMSG_TAIL(n); + rta->rta_type = type; + rta->rta_len = length; + memcpy(RTA_DATA(rta), data, data_length); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(length); + + return 0; +} + +static ssize_t sendto_loop(int fd, const void *buf, size_t buf_len, int flags, const struct sockaddr *sa, socklen_t sa_len) { + + for (;;) { + ssize_t l; + + if ((l = sendto(fd, buf, buf_len, flags, sa, sa_len)) >= 0) + return l; + + if (errno != EINTR) + return -errno; + } +} + +static ssize_t recvfrom_loop(int fd, void *buf, size_t buf_len, int flags, struct sockaddr *sa, socklen_t *sa_len) { + + for (;;) { + ssize_t l; + + if ((l = recvfrom(fd, buf, buf_len, flags, sa, sa_len)) >= 0) + return l; + + if (errno != EINTR) + return -errno; + } +} + +static int add_adresses(int fd, int if_loopback) { + union { + struct sockaddr sa; + struct sockaddr_nl nl; + } sa; + union { + struct nlmsghdr header; + uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + + NLMSG_ALIGN(sizeof(struct ifaddrmsg)) + + RTA_LENGTH(sizeof(struct in6_addr))]; + } request; + + struct ifaddrmsg *ifaddrmsg; + uint32_t ipv4_address = htonl(INADDR_LOOPBACK); + int r; + + zero(request); + + request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + request.header.nlmsg_type = RTM_NEWADDR; + request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_ACK; + request.header.nlmsg_seq = REQUEST_ADDRESS_IPV4; + + ifaddrmsg = NLMSG_DATA(&request.header); + ifaddrmsg->ifa_family = AF_INET; + ifaddrmsg->ifa_prefixlen = 8; + ifaddrmsg->ifa_flags = IFA_F_PERMANENT; + ifaddrmsg->ifa_scope = RT_SCOPE_HOST; + ifaddrmsg->ifa_index = if_loopback; + + if ((r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &ipv4_address, sizeof(ipv4_address))) < 0) + return r; + + zero(sa); + sa.nl.nl_family = AF_NETLINK; + + if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0) + return -errno; + + request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + request.header.nlmsg_seq = REQUEST_ADDRESS_IPV6; + + ifaddrmsg->ifa_family = AF_INET6; + ifaddrmsg->ifa_prefixlen = 128; + + if ((r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &in6addr_loopback, sizeof(in6addr_loopback))) < 0) + return r; + + if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0) + return -errno; + + return 0; +} + +static int start_interface(int fd, int if_loopback) { + union { + struct sockaddr sa; + struct sockaddr_nl nl; + } sa; + union { + struct nlmsghdr header; + uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + + NLMSG_ALIGN(sizeof(struct ifinfomsg))]; + } request; + + struct ifinfomsg *ifinfomsg; + + zero(request); + + request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + request.header.nlmsg_type = RTM_NEWLINK; + request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; + request.header.nlmsg_seq = REQUEST_FLAGS; + + ifinfomsg = NLMSG_DATA(&request.header); + ifinfomsg->ifi_family = AF_UNSPEC; + ifinfomsg->ifi_index = if_loopback; + ifinfomsg->ifi_flags = IFF_UP; + ifinfomsg->ifi_change = IFF_UP; + + zero(sa); + sa.nl.nl_family = AF_NETLINK; + + if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0) + return -errno; + + return 0; +} + +static int read_response(int fd) { + union { + struct sockaddr sa; + struct sockaddr_nl nl; + } sa; + union { + struct nlmsghdr header; + uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + + NLMSG_ALIGN(sizeof(struct nlmsgerr))]; + } response; + + ssize_t l; + socklen_t sa_len = sizeof(sa); + struct nlmsgerr *nlmsgerr; + + if ((l = recvfrom_loop(fd, &response, sizeof(response), 0, &sa.sa, &sa_len)) < 0) + return -errno; + + if (sa_len != sizeof(sa.nl) || + sa.nl.nl_family != AF_NETLINK) + return -EIO; + + if (sa.nl.nl_pid != 0) + return 0; + + if ((size_t) l < sizeof(struct nlmsghdr)) + return -EIO; + + if (response.header.nlmsg_type != NLMSG_ERROR || + (pid_t) response.header.nlmsg_pid != getpid() || + response.header.nlmsg_seq >= REQUEST_ALL) + return 0; + + if ((size_t) l < NLMSG_LENGTH(sizeof(struct nlmsgerr)) || + response.header.nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) + return -EIO; + + nlmsgerr = NLMSG_DATA(&response.header); + + if (nlmsgerr->error < 0 && nlmsgerr->error != -EEXIST) + return nlmsgerr->error; + + return response.header.nlmsg_seq; +} + +int loopback_setup(void) { + int r, if_loopback; + union { + struct sockaddr sa; + struct sockaddr_nl nl; + struct sockaddr_storage storage; + } sa; + int requests = REQUEST_NONE; + + int fd; + + errno = 0; + if ((if_loopback = (int) if_nametoindex("lo")) <= 0) + return errno ? -errno : -ENODEV; + + if ((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) + return -errno; + + zero(sa); + sa.nl.nl_family = AF_NETLINK; + + if (bind(fd, &sa.sa, sizeof(sa)) < 0) { + r = -errno; + goto finish; + } + + if ((r = add_adresses(fd, if_loopback)) < 0) + goto finish; + + if ((r = start_interface(fd, if_loopback)) < 0) + goto finish; + + do { + if ((r = read_response(fd)) < 0) + goto finish; + + requests |= r; + + } while (requests != REQUEST_ALL); + + r = 0; + +finish: + if (r < 0) + log_error("Failed to configure loopback device: %s", strerror(-r)); + + if (fd >= 0) + close_nointr_nofail(fd); + + return r; +} diff --git a/loopback-setup.h b/loopback-setup.h new file mode 100644 index 0000000000..b4614fa5bd --- /dev/null +++ b/loopback-setup.h @@ -0,0 +1,27 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#ifndef fooloopbacksetuphfoo +#define fooloopbacksetuphfoo + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +int loopback_setup(void); + +#endif diff --git a/main.c b/main.c index 0128787d14..aa58040ff5 100644 --- a/main.c +++ b/main.c @@ -36,6 +36,7 @@ #include "log.h" #include "mount-setup.h" #include "hostname-setup.h" +#include "loopback-setup.h" #include "load-fragment.h" #include "fdset.h" @@ -641,8 +642,10 @@ int main(int argc, char *argv[]) { log_debug("systemd running in %s mode.", manager_running_as_to_string(running_as)); - if (running_as == MANAGER_INIT) + if (running_as == MANAGER_INIT) { hostname_setup(); + loopback_setup(); + } if ((r = manager_new(running_as, confirm_spawn, &m)) < 0) { log_error("Failed to allocate manager object: %s", strerror(-r)); diff --git a/test-loopback.c b/test-loopback.c new file mode 100644 index 0000000000..5cd7b41e19 --- /dev/null +++ b/test-loopback.c @@ -0,0 +1,35 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "loopback-setup.h" + +int main(int argc, char* argv[]) { + int r; + + if ((r = loopback_setup()) < 0) + fprintf(stderr, "loopback: %s\n", strerror(-r)); + + return 0; +} -- cgit v1.2.3-54-g00ecf From d1ab0ca07372649dad70a0348d75e394f254e1b6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 15 May 2010 23:06:41 +0200 Subject: man: add some initial man page work --- .gitignore | 3 + Makefile.am | 58 +++++++++---- configure.ac | 50 +++++++++++- man/systemd.service.xml | 213 ++++++++++++++++++++++++++++++++++++++++++++++++ man/systemd.unit.xml | 121 +++++++++++++++++++++++++++ 5 files changed, 429 insertions(+), 16 deletions(-) create mode 100644 man/systemd.service.xml create mode 100644 man/systemd.unit.xml (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index b9cb51c0e7..b83dd9acd7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ +*.5 +*.html *~ +*.tar.gz test-ns test-loopback systemd-initctl.service diff --git a/Makefile.am b/Makefile.am index 8ad1969bdc..e562b18a2d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -17,12 +17,12 @@ ACLOCAL_AMFLAGS = -I m4 -pkgsysconfdir=$(sysconfdir)/systemd dbuspolicydir=$(sysconfdir)/dbus-1/system.d -udevrulesdir=/lib/udev/rules.d +udevrulesdir=@udevrulesdir@ +pkgsysconfdir=$(sysconfdir)/systemd systemunitdir=$(pkgdatadir)/system -sessionunitdir=$(pkgdatadir)/system +sessionunitdir=$(pkgdatadir)/session AM_CPPFLAGS = \ -include $(top_builddir)/config.h \ @@ -35,8 +35,9 @@ AM_CPPFLAGS = \ -DCGROUP_AGENT_PATH=\"$(pkglibexecdir)/systemd-cgroups-agent\" \ -DSYSTEMD_BINARY_PATH=\"$(sbindir)/systemd\" -# -DSYSTEMD_BINARY_PATH=\"/home/lennart/projects/systemd/systemd\" -VALA_CFLAGS=-Wno-unused-variable -Wno-unused-function +VALA_CFLAGS = \ + -Wno-unused-variable \ + -Wno-unused-function sbin_PROGRAMS = \ systemd @@ -60,27 +61,24 @@ noinst_PROGRAMS = \ test-ns \ test-loopback -dbuspolicy_DATA = \ +dist_dbuspolicy_DATA = \ org.freedesktop.systemd1.conf -udevrules_DATA = \ +dist_udevrules_DATA = \ 99-systemd.rules -systemunit_DATA = \ +dist_systemunit_DATA = \ units/emergency.service \ - systemd-initctl.service \ units/systemd-initctl.socket \ - systemd-logger.service \ units/systemd-logger.socket +systemunit_DATA = \ + systemd-initctl.service \ + systemd-logger.service + EXTRA_DIST = \ - org.freedesktop.systemd1.conf \ - 99-systemd.rules \ - units/emergency.service \ units/systemd-initctl.service.in \ - units/systemd-initctl.socket \ units/systemd-logger.service.in \ - units/systemd-logger.socket \ LICENSE \ README @@ -147,6 +145,21 @@ EXTRA_DIST += \ linux/auto_dev-ioctl.h \ initreq.h +dist_man_MANS = \ + systemd.unit.5 \ + systemd.service.5 + +HTMLMANS = \ + systemd.unit.html \ + systemd.service.html + +dist_noinst_DATA = \ + $(HTMLMANS) + +EXTRA_DIST += \ + man/systemd.unit.xml \ + man/systemd.service.xml + systemd_SOURCES = \ $(COMMON_SOURCES) \ main.c @@ -251,6 +264,18 @@ CLEANFILES = \ systemd-initctl.service \ systemd-logger.service +if HAVE_XSLTPROC +%.5: man/%.xml + $(XSLTPROC) -o $@ -nonet http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $< + +%.html: man/%.xml + $(XSLTPROC) -o $@ -nonet http://docbook.sourceforge.net/release/xsl/current/xhtml-1_1/docbook.xsl $< + +CLEANFILES += \ + $(dist_man_MANS) \ + $(HTMLMANS) +endif + install-data-hook: $(MKDIR_P) -m 0755 \ $(DESTDIR)$(pkgsysconfdir)/system \ @@ -259,3 +284,6 @@ install-data-hook: $(DESTDIR)/cgroup/debug rm -f $(DESTDIR)$(sysconfdir)/xdg/systemd/session ln -sf $(DESTDIR)$(pkgsysconfdir)/session $(DESTDIR)$(sysconfdir)/xdg/systemd/session + +DISTCHECK_CONFIGURE_FLAGS = \ + --with-udevrulesdir=$$dc_install_base/$(udevrulesdir) diff --git a/configure.ac b/configure.ac index 65353d52fa..de61b2d52f 100644 --- a/configure.ac +++ b/configure.ac @@ -43,7 +43,43 @@ AM_PROG_CC_C_O AC_PROG_GCC_TRADITIONAL AC_USE_SYSTEM_EXTENSIONS -CC_CHECK_CFLAGS_APPEND([-Wall -W -Wextra -pipe -Wno-long-long -Winline -Wvla -Wno-overlength-strings -Wundef -Wformat=2 -Wlogical-op -Wsign-compare -Wformat-security -Wmissing-include-dirs -Wformat-nonliteral -Wold-style-definition -Wpointer-arith -Winit-self -Wdeclaration-after-statement -Wfloat-equal -Wmissing-prototypes -Wstrict-prototypes -Wredundant-decls -Wmissing-declarations -Wmissing-noreturn -Wshadow -Wendif-labels -Wcast-align -Wstrict-aliasing=2 -Wwrite-strings -Wno-unused-parameter -ffast-math -Wp,-D_FORTIFY_SOURCE=2 -fno-common -fdiagnostics-show-option -Wno-missing-field-initializers]) +CC_CHECK_CFLAGS_APPEND([ \ + -Wall \ + -W \ + -Wextra \ + -pipe \ + -Wno-long-long \ + -Winline \ + -Wvla \ + -Wno-overlength-strings \ + -Wundef \ + -Wformat=2 \ + -Wlogical-op \ + -Wsign-compare \ + -Wformat-security \ + -Wmissing-include-dirs \ + -Wformat-nonliteral \ + -Wold-style-definition \ + -Wpointer-arith \ + -Winit-self \ + -Wdeclaration-after-statement \ + -Wfloat-equal \ + -Wmissing-prototypes \ + -Wstrict-prototypes \ + -Wredundant-decls \ + -Wmissing-declarations \ + -Wmissing-noreturn \ + -Wshadow \ + -Wendif-labels \ + -Wcast-align \ + -Wstrict-aliasing=2 \ + -Wwrite-strings \ + -Wno-unused-parameter \ + -ffast-math \ + -Wp,-D_FORTIFY_SOURCE=2 \ + -fno-common \ + -fdiagnostics-show-option \ + -Wno-missing-field-initializers]) AC_SEARCH_LIBS([clock_gettime], [rt], [], [AC_MSG_ERROR([*** POSIX RT library not found])]) AC_SEARCH_LIBS([cap_init], [cap], [], [AC_MSG_ERROR([*** POSIX caps library not found])]) @@ -51,6 +87,7 @@ AC_CHECK_HEADERS([sys/capability.h], [], [AC_MSG_ERROR([*** POSIX caps headers n # This makes sure pkg.m4 is available. m4_pattern_forbid([^_?PKG_[A-Z_]+$],[*** pkg.m4 missing, please install pkg-config]) + PKG_CHECK_MODULES(UDEV, [ libudev ]) AC_SUBST(UDEV_CFLAGS) AC_SUBST(UDEV_LIBS) @@ -98,6 +135,9 @@ AC_SUBST(CGROUP_LIBS) AM_PROG_VALAC([0.7]) AC_SUBST(VAPIDIR) +AC_PATH_PROG([XSLTPROC], [xsltproc]) +AM_CONDITIONAL(HAVE_XSLTPROC, test x"$XSLTPROC" != x) + AC_ARG_WITH(distro, AS_HELP_STRING([--with-distro=DISTRO],[Specify the distribution to target: One of fedora, suse, debian, arch, gentoo, or none])) if test "z$with_distro" = "z"; then if test "$cross_compiling" = yes; then @@ -174,6 +214,13 @@ AM_CONDITIONAL(TARGET_GENTOO, test x"$with_distro" = xgentoo) AC_DEFINE_UNQUOTED(SPECIAL_DBUS_SERVICE, ["$special_dbus_service"], [D-Bus service name]) AC_DEFINE_UNQUOTED(SPECIAL_SYSLOG_SERVICE, ["$special_syslog_service"], [syslog service name]) +AC_ARG_WITH([udevrulesdir], + AS_HELP_STRING([--with-udevrulesdir=DIR], [Diectory for udev rules]), + [], + [with_udevrulesdir=/lib/udev/rules.d]) +AC_SUBST([udevrulesdir], [$with_udevrulesdir]) + + AC_OUTPUT([Makefile]) echo " @@ -185,4 +232,5 @@ echo " Syslog service: ${special_syslog_service} D-Bus service: ${special_dbus_service} Gtk: ${have_gtk} + udev rules dir: ${with_udevrulesdir} " diff --git a/man/systemd.service.xml b/man/systemd.service.xml new file mode 100644 index 0000000000..5285ce3d52 --- /dev/null +++ b/man/systemd.service.xml @@ -0,0 +1,213 @@ + + + + + + + + + systemd.service + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd.service + 5 + + + + systemd.service + systemd service configuration files + + + + systemd.service + + + + Description + + A configuration file ending in .service encodes + information about a process controlled and supervised + by systemd. + + This man page lists the configuration options + specific to this unit type. See + systemd.unit5 + for the common options of all unit configuration + files. + + + + Options + + + + Type= + + One of + forking, + simple, + finish, + dbus. + + If set to + forking + (the default) it is expected + that the process configured + with + ExecStart= + will start up and call + fork(). The + parent process is expected to + finish when start-up is + complete and all communication + channels set up. The child + continues to run as the main + daemon process. This is the + behaviour of traditional UNIX + daemons. If this setting is + used it is recommended to also + use the + PIDFile= + option, so that systemd can + identify the main process of + the daemon. systemd will start + follow-up units as soon as the + parent process exited. + + If set to + simple (the + recommended value) it is + expected that the process + configured with + ExecStart= + is the main process of the + daemon. In this mode + communication channels must be + available before the daemon is + started up, as systemd will + immediately start follow-up + units. + + Behaviour of + finish is + similar to + simple, + however it is expected that + the process has to exit before + systemd starts follow-up + units. ValidNoProcess= + is particularly useful for + this type of service. + + Behaviour of + dbus is + similar to + simple, + however it is expected that + the daemon acquires a name on + the D-Bus bus, as configured + by + BusName=. Follow-up + units will be started after + the name has been + acquired. + + + + ValidNoProcess= + + Takes a boolean value + that specifies whether the service + shall be considered active + even when all its processes + exited. Defaults to no. + + + + + PIDFile= + + Takes an absolute file + name pointing to the PID file + of this daemon. Use of this + option is recommended for + services where + Type= is + set to + forking. + + + + + BusName= + + Takes a D-Bus bus name + that this service is reachable + as. This option is mandatory + for services where + Type= is + set to + dbus, but + its use is otherwise + recommended as well if the + process takes a name on the + D-Bus bus. + + + + + ExecStart= + + Takes a command line + that is executed when this + service shall be started + up. The first word of the + command line must be an + absolute file name. It is + mandatory to set this option + for all services. + + + + + + + + See Also + + systemd8, + systemctl8 + systemd.unit5 + + + + diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml new file mode 100644 index 0000000000..cfa1b09499 --- /dev/null +++ b/man/systemd.unit.xml @@ -0,0 +1,121 @@ + + + + + + + + + systemd.unit + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd.unit + 5 + + + + systemd.unit + systemd unit configuration files + + + + systemd.service + systemd.socket + systemd.device + systemd.mount + systemd.automount + systemd.swap + systemd.target + + + + Description + + A unit configuration file encodes information + about a service, a socket, a mount point, an automount + point, a swap file or patition, or a start-up target + controlled and supervised by systemd. The syntax is + inspired by XDG .desktop files, + which are in turn inspired by Windows + .ini files. + + This man pages lists the common configuration + options of the various unit types. + + + + Options + + + + Names= + + Additional names for this unit. The names + listed here mus have the same suffix (i.e. type) + as the identifier name. This option may be + specified more than once. + + + + Requires= + + Dependencies on other + units. If this units get + activated the units listed + here will be activated as + well. If one of the other + units gets deactivated or its + activation fails, this unit + will be deactivated. This + option may be specified more + than once. + + + + + + + See Also + + systemd8, + systemctl8 + systemd.service5 + systemd.socket5 + systemd.device5 + systemd.mount5 + systemd.automount5 + systemd.swap5 + systemd.target5 + + + + -- cgit v1.2.3-54-g00ecf From dfac97b21e00cd3617ba817227db7b621841b5cc Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 16 May 2010 19:09:22 +0200 Subject: build-sys: generate intermediate files in subdirs --- .gitignore | 16 ++++++---------- Makefile.am | 32 ++++++++++++++++++-------------- src/.gitignore | 3 +++ units/.gitignore | 2 ++ 4 files changed, 29 insertions(+), 24 deletions(-) create mode 100644 src/.gitignore create mode 100644 units/.gitignore (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index b83dd9acd7..4efd824796 100644 --- a/.gitignore +++ b/.gitignore @@ -1,23 +1,19 @@ -*.5 -*.html -*~ -*.tar.gz test-ns test-loopback -systemd-initctl.service -systemd-logger.service systemd-cgroups-agent systemd-initctl systemd -*.o test-engine test-job-type systemd-logger systemctl -systemctl.c -systemd-interfaces.c systemadm -systemadm.c +.dirstamp +*.5 +*.html +*~ +*.tar.gz +*.o .deps/ Makefile.in aclocal.m4 diff --git a/Makefile.am b/Makefile.am index 0da02af005..35ecebaadd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -59,10 +59,10 @@ noinst_PROGRAMS = \ test-loopback dist_dbuspolicy_DATA = \ - org.freedesktop.systemd1.conf + src/org.freedesktop.systemd1.conf dist_udevrules_DATA = \ - 99-systemd.rules + src/99-systemd.rules dist_systemunit_DATA = \ units/emergency.service \ @@ -70,8 +70,8 @@ dist_systemunit_DATA = \ units/systemd-logger.socket systemunit_DATA = \ - systemd-initctl.service \ - systemd-logger.service + units/systemd-initctl.service \ + units/systemd-logger.service EXTRA_DIST = \ units/systemd-initctl.service.in \ @@ -144,12 +144,12 @@ EXTRA_DIST += \ src/sd-daemon.h dist_man_MANS = \ - systemd.unit.5 \ - systemd.service.5 + man/systemd.unit.5 \ + man/systemd.service.5 HTMLMANS = \ - systemd.unit.html \ - systemd.service.html + man/systemd.unit.html \ + man/systemd.service.html dist_noinst_DATA = \ $(HTMLMANS) @@ -260,12 +260,14 @@ systemadm_SOURCES = \ systemadm_CPPFLAGS = $(AM_CPPFLAGS) $(DBUSGLIB_CFLAGS) $(GTK_CFLAGS) $(VALA_CFLAGS) systemadm_LDADD = $(DBUSGLIB_LIBS) $(GTK_LIBS) -systemd-initctl.service: units/systemd-initctl.service.in Makefile +units/systemd-initctl.service: units/systemd-initctl.service.in Makefile + $(MKDIR_P) units $(SED) -e 's,@libexecdir\@,$(libexecdir),g' \ -e 's,@pkglibexecdir\@,$(pkglibexecdir),g' \ < $< > $@ -systemd-logger.service: units/systemd-logger.service.in Makefile +units/systemd-logger.service: units/systemd-logger.service.in Makefile + $(MKDIR_P) units $(SED) -e 's,@libexecdir\@,$(libexecdir),g' \ -e 's,@pkglibexecdir\@,$(pkglibexecdir),g' \ < $< > $@ @@ -274,14 +276,16 @@ CLEANFILES = \ src/systemd-interfaces.c \ src/systemctl.c \ src/systemadm.c \ - systemd-initctl.service \ - systemd-logger.service + units/systemd-initctl.service \ + units/systemd-logger.service if HAVE_XSLTPROC -%.5: man/%.xml +man/%.5: man/%.xml + $(MKDIR_P) man $(XSLTPROC) -o $@ -nonet http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $< -%.html: man/%.xml +man/%.html: man/%.xml + $(MKDIR_P) man $(XSLTPROC) -o $@ -nonet http://docbook.sourceforge.net/release/xsl/current/xhtml-1_1/docbook.xsl $< CLEANFILES += \ diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000000..954d7fe0fc --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,3 @@ +systemctl.c +systemd-interfaces.c +systemadm.c diff --git a/units/.gitignore b/units/.gitignore new file mode 100644 index 0000000000..88895b63a6 --- /dev/null +++ b/units/.gitignore @@ -0,0 +1,2 @@ +systemd-initctl.service +systemd-logger.service -- cgit v1.2.3-54-g00ecf From 9f23530860942a8f94b7c535ead80c38f02424b1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 16 May 2010 22:45:11 +0200 Subject: man: document a few special units --- .gitignore | 1 + Makefile.am | 11 +- man/systemd.special.xml | 325 +++++++++++++++++++++++++++++++++++++++ man/systemd.unit.xml | 4 +- src/manager.h | 6 +- units/emergency.service | 2 + units/local-fs.target | 2 + units/network.target | 2 + units/nss-lookup.target | 2 + units/remote-fs.target | 2 + units/rtc-set.target | 2 + units/shutdown.target | 2 + units/sigpwr.target | 2 + units/sockets.target | 2 + units/swap.target | 2 + units/syslog.target.in | 2 + units/systemd-initctl.service.in | 2 + units/systemd-initctl.socket | 2 + units/systemd-logger.service.in | 2 + units/systemd-logger.socket | 2 + 20 files changed, 368 insertions(+), 9 deletions(-) create mode 100644 man/systemd.special.xml (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 4efd824796..700a085ad7 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ systemctl systemadm .dirstamp *.5 +*.7 *.html *~ *.tar.gz diff --git a/Makefile.am b/Makefile.am index 780ded62fa..15da7b2439 100644 --- a/Makefile.am +++ b/Makefile.am @@ -156,18 +156,21 @@ EXTRA_DIST += \ dist_man_MANS = \ man/systemd.unit.5 \ - man/systemd.service.5 + man/systemd.service.5 \ + man/systemd.special.7 HTMLMANS = \ man/systemd.unit.html \ - man/systemd.service.html + man/systemd.service.html \ + man/systemd.special.html dist_noinst_DATA = \ $(HTMLMANS) EXTRA_DIST += \ man/systemd.unit.xml \ - man/systemd.service.xml + man/systemd.service.xml \ + man/systemd.special.xml systemd_SOURCES = \ $(COMMON_SOURCES) \ @@ -297,7 +300,7 @@ CLEANFILES = \ units/syslog.target if HAVE_XSLTPROC -man/%.5: man/%.xml +man/%.5 man/%.7: man/%.xml $(MKDIR_P) man $(XSLTPROC) -o $@ -nonet http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $< diff --git a/man/systemd.special.xml b/man/systemd.special.xml new file mode 100644 index 0000000000..3f9286d4b4 --- /dev/null +++ b/man/systemd.special.xml @@ -0,0 +1,325 @@ + + + + + + + + + systemd.special + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd.special + 7 + + + + systemd.special + special systemd units + + + + emergency.service + local-fs.target + network.target + nss-lookup.target + remote-fs.target + rpcbind.target + rtc-set.target + shutdown.target + sigpwr.target + sockets.target + swap.target + syslog.target + systemd-initctl.service + systemd-initctl.socket + systemd-logger.service + systemd-logger.socket + + + + Description + + A few units are treated specially by + systemd. They have special internal semantics and + cannot be renamed. + + + + Special System Units + + + + emergency.service + + A special service unit + that starts an emergency + shell on the main + console. This unit is supposed + to be used with the kernel + command line option + systemd.default= + and has otherwise little use. + + + + + local-fs.target + + systemd automatically + adds dependencies of type + After to all mount units that + refer to local mount + points. In addition, systemd + adds dependencies of type + Wants to those mounts listed + in + /etc/fstab + that have the + auto and + comment=systemd.mount + mount options set. + + systemd automatically + adds dependencies of type + After to this target unit for + all SysV init scripts with an + LSB header referring to the + $local_fs + facility. + + + + network.target + + systemd automatically + adds dependencies of type + After to this target unit for + all SysV init scripts with an + LSB header referring to the + $network + facility. + + + + nss-lookup.target + + systemd automatically + adds dependencies of type + After to this target unit for + all SysV init scripts with an + LSB header referring to the + $named + facility. + + + + remote-fs.target + + Similar to local-fs.target, but for remote mount points. + systemd automatically + adds dependencies of type + After to this target unit for + all SysV init scripts with an + LSB header referring to the + $remote-fs + facility. + + + + rpcbind.target + + systemd automatically + adds dependencies of type + After to this target unit for + all SysV init scripts with an + LSB header referring to the + $rpcbind + facility. + + + + rtc-set.target + + systemd automatically + adds dependencies of type + After to this target unit for + all SysV init scripts with an + LSB header referring to the + $time + facility. + + + + shutdown.target + + A special target unit + that terminates the services + on system shutdown. + + Services that shall be + terminated on system shutdown + shall add Wants dependencies + from this unit to their + service unit during + installation. + + systemd automatically + adds dependencies of type + Conflicts to this target unit + for all SysV init scripts that + shall be terminated in SysV + runlevels 0 or 6. + + + + sigpwr.target + + A special target that is + started when systemd receives + the SIGPWR process signal, + which is normally sent by the + kernel or UPS daemons when the + power fails. + + + + sockets.target + + A special target unit + that sets up all service + sockets. + + Services that can be + socket-activated shall add + Wants dependencies from this + unit to their socket unit + during installation. + + + + swap.target + + Similar to + local-fs.target, but for swap + partitions and swap + files. + + + + syslog.target + + systemd automatically + adds dependencies of type + After to this target unit for + all SysV init scripts with an + LSB header referring to the + $syslog + facility. + + Administrators should + ensure that this target pulls + in a service unit with the + name or alias of + @SPECIAL_SYSLOG_NAME@ (or a + socket unit that activates + this service). + + + + systemd-initctl.service + + This provides + compatibility with the SysV + /dev/initctl file system FIFO + for communication with the + init system. + This is a + socket-activated service, see + system-initctl.socket. + + + + systemd-initctl.socket + + Socket activation unit + for + system-initctl.service. + + + + systemd-logger.service + + This is used internally + by systemd to provide syslog + logging to started + processes. + This is a + socket-activated service, see + system-logger.socket. + + + + systemd-logger.socket + + Socket activation unit + for + system-logger.service. + + + + + + + Special Session Units + + When systemd runs as a service instance, the + following special units are available, which have + similar definitions as their system counterparts: + local-fs.target, + remote-fs.target, + shutdown.target, + sockets.target, + swap.target. + + + + See Also + + systemd.unit5 + systemd.service5 + systemd.socket5 + systemd.target5 + + + + diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index cfa1b09499..4f71ffda77 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -77,7 +77,7 @@ - Names= + Names= Additional names for this unit. The names listed here mus have the same suffix (i.e. type) @@ -86,7 +86,7 @@ - Requires= + Requires= Dependencies on other units. If this units get diff --git a/src/manager.h b/src/manager.h index 424ce879af..78923003e2 100644 --- a/src/manager.h +++ b/src/manager.h @@ -97,10 +97,10 @@ struct Watch { #define SPECIAL_SIGPWR_TARGET "sigpwr.target" #define SPECIAL_CTRL_ALT_DEL_TARGET "ctrl-alt-del.target" -#define SPECIAL_LOCAL_FS_TARGET "local-fs.target" -#define SPECIAL_REMOTE_FS_TARGET "remote-fs.target" +#define SPECIAL_LOCAL_FS_TARGET "local-fs.target" /* LSB's $local_fs */ +#define SPECIAL_REMOTE_FS_TARGET "remote-fs.target" /* LSB's $remote_fs */ #define SPECIAL_SWAP_TARGET "swap.target" -#define SPECIAL_NETWORK_TARGET "network.target" +#define SPECIAL_NETWORK_TARGET "network.target" /* LSB's $network */ #define SPECIAL_NSS_LOOKUP_TARGET "nss-lookup.target" /* LSB's $named */ #define SPECIAL_RPCBIND_TARGET "rpcbind.target" /* LSB's $portmap */ #define SPECIAL_SYSLOG_TARGET "syslog.target" /* LSB's $syslog; Should pull in syslog.socket or syslog.service */ diff --git a/units/emergency.service b/units/emergency.service index 13fca6fc60..6c4b870ec2 100644 --- a/units/emergency.service +++ b/units/emergency.service @@ -15,6 +15,8 @@ # You should have received a copy of the GNU General Public License # along with systemd; If not, see . +# See systemd.special(7) for details + [Unit] Description=Emergency Shell diff --git a/units/local-fs.target b/units/local-fs.target index 3e7124a709..81bff5a89d 100644 --- a/units/local-fs.target +++ b/units/local-fs.target @@ -15,5 +15,7 @@ # You should have received a copy of the GNU General Public License # along with systemd; If not, see . +# See systemd.special(7) for details + [Unit] Description=Local File Systems diff --git a/units/network.target b/units/network.target index cdb1b5eba6..a218c64633 100644 --- a/units/network.target +++ b/units/network.target @@ -15,5 +15,7 @@ # You should have received a copy of the GNU General Public License # along with systemd; If not, see . +# See systemd.special(7) for details + [Unit] Description=Network diff --git a/units/nss-lookup.target b/units/nss-lookup.target index d1ce0a7044..e8e659368a 100644 --- a/units/nss-lookup.target +++ b/units/nss-lookup.target @@ -15,6 +15,8 @@ # You should have received a copy of the GNU General Public License # along with systemd; If not, see . +# See systemd.special(7) for details + [Unit] Description=Name Lookups Wants=network.target diff --git a/units/remote-fs.target b/units/remote-fs.target index 0ed324edce..177a773c90 100644 --- a/units/remote-fs.target +++ b/units/remote-fs.target @@ -15,6 +15,8 @@ # You should have received a copy of the GNU General Public License # along with systemd; If not, see . +# See systemd.special(7) for details + [Unit] Description=Remote File Systems Requires=network.target diff --git a/units/rtc-set.target b/units/rtc-set.target index 626fa5a4a7..18c1ed35d8 100644 --- a/units/rtc-set.target +++ b/units/rtc-set.target @@ -15,5 +15,7 @@ # You should have received a copy of the GNU General Public License # along with systemd; If not, see . +# See systemd.special(7) for details + [Unit] Description=RTC Set diff --git a/units/shutdown.target b/units/shutdown.target index 87b1be8a20..37dca44e9f 100644 --- a/units/shutdown.target +++ b/units/shutdown.target @@ -15,5 +15,7 @@ # You should have received a copy of the GNU General Public License # along with systemd; If not, see . +# See systemd.special(7) for details + [Unit] Description=Shutdown diff --git a/units/sigpwr.target b/units/sigpwr.target index 36d4d24f8b..24f7d38dfe 100644 --- a/units/sigpwr.target +++ b/units/sigpwr.target @@ -15,5 +15,7 @@ # You should have received a copy of the GNU General Public License # along with systemd; If not, see . +# See systemd.special(7) for details + [Unit] Description=Power Failure diff --git a/units/sockets.target b/units/sockets.target index 10610303e0..00e698f457 100644 --- a/units/sockets.target +++ b/units/sockets.target @@ -15,5 +15,7 @@ # You should have received a copy of the GNU General Public License # along with systemd; If not, see . +# See systemd.special(7) for details + [Unit] Description=Sockets diff --git a/units/swap.target b/units/swap.target index aac5456195..4f6e193dbe 100644 --- a/units/swap.target +++ b/units/swap.target @@ -15,5 +15,7 @@ # You should have received a copy of the GNU General Public License # along with systemd; If not, see . +# See systemd.special(7) for details + [Unit] Description=Swap diff --git a/units/syslog.target.in b/units/syslog.target.in index bcd645305e..d21e7a72b2 100644 --- a/units/syslog.target.in +++ b/units/syslog.target.in @@ -15,6 +15,8 @@ # You should have received a copy of the GNU General Public License # along with systemd; If not, see . +# See systemd.special(7) for details + [Unit] Description=Syslog Requires=@SPECIAL_SYSLOG_SERVICE@ diff --git a/units/systemd-initctl.service.in b/units/systemd-initctl.service.in index b6d5158f12..252a01429a 100644 --- a/units/systemd-initctl.service.in +++ b/units/systemd-initctl.service.in @@ -15,6 +15,8 @@ # You should have received a copy of the GNU General Public License # along with systemd; If not, see . +# See systemd.special(7) for details + [Unit] Description=systemd /dev/initctl Compatibility Daemon diff --git a/units/systemd-initctl.socket b/units/systemd-initctl.socket index ff3dbacd30..acff32235d 100644 --- a/units/systemd-initctl.socket +++ b/units/systemd-initctl.socket @@ -15,6 +15,8 @@ # You should have received a copy of the GNU General Public License # along with systemd; If not, see . +# See systemd.special(7) for details + [Unit] Description=systemd /dev/initctl Compatibility Socket diff --git a/units/systemd-logger.service.in b/units/systemd-logger.service.in index ba6fa47943..e4606ea70c 100644 --- a/units/systemd-logger.service.in +++ b/units/systemd-logger.service.in @@ -15,6 +15,8 @@ # You should have received a copy of the GNU General Public License # along with systemd; If not, see . +# See systemd.special(7) for details + [Unit] Description=systemd Logging Daemon diff --git a/units/systemd-logger.socket b/units/systemd-logger.socket index a1d11e8a2d..8d02ab48ca 100644 --- a/units/systemd-logger.socket +++ b/units/systemd-logger.socket @@ -15,6 +15,8 @@ # You should have received a copy of the GNU General Public License # along with systemd; If not, see . +# See systemd.special(7) for details + [Unit] Description=systemd Logging Socket -- cgit v1.2.3-54-g00ecf From 4288f619215e3dda0b75113d78e4fb7ba219ed58 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 23 May 2010 03:45:33 +0200 Subject: dbus: automatically generate and install introspection files --- .gitignore | 1 + Makefile.am | 19 ++++++++ fixme | 2 + src/dbus-automount.c | 27 ++++++----- src/dbus-automount.h | 2 + src/dbus-device.c | 27 ++++++----- src/dbus-device.h | 2 + src/dbus-execute.h | 34 ++++++------- src/dbus-job.c | 35 +++++++------ src/dbus-job.h | 2 + src/dbus-manager.c | 135 ++++++++++++++++++++++++++------------------------- src/dbus-manager.h | 2 + src/dbus-mount.c | 41 +++++++++------- src/dbus-mount.h | 2 + src/dbus-service.c | 53 +++++++++++--------- src/dbus-service.h | 2 + src/dbus-snapshot.c | 29 ++++++----- src/dbus-snapshot.h | 2 + src/dbus-socket.c | 45 +++++++++-------- src/dbus-socket.h | 2 + src/dbus-swap.c | 29 ++++++----- src/dbus-swap.h | 2 + src/dbus-target.c | 25 ++++++---- src/dbus-target.h | 2 + src/dbus-unit.c | 2 + src/dbus-unit.h | 98 +++++++++++++++++++------------------ src/dbus.c | 28 +++++++++++ src/dbus.h | 34 +++++++------ src/main.c | 34 +++++++++++-- 29 files changed, 436 insertions(+), 282 deletions(-) (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 700a085ad7..de52b6fbd6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +org.freedesktop.systemd1.*.xml test-ns test-loopback systemd-cgroups-agent diff --git a/Makefile.am b/Makefile.am index ffa9ad9a82..b3e9dfde81 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,6 +23,7 @@ udevrulesdir=@udevrulesdir@ pkgsysconfdir=$(sysconfdir)/systemd systemunitdir=$(pkgdatadir)/system sessionunitdir=$(pkgdatadir)/session +interfacedir=$(datadir)/dbus-1/interfaces AM_CPPFLAGS = \ -include $(top_builddir)/config.h \ @@ -64,6 +65,19 @@ dist_dbuspolicy_DATA = \ dist_udevrules_DATA = \ src/99-systemd.rules +interface_DATA = \ + org.freedesktop.systemd1.Manager.xml \ + org.freedesktop.systemd1.Job.xml \ + org.freedesktop.systemd1.Unit.xml \ + org.freedesktop.systemd1.Service.xml \ + org.freedesktop.systemd1.Socket.xml \ + org.freedesktop.systemd1.Target.xml \ + org.freedesktop.systemd1.Device.xml \ + org.freedesktop.systemd1.Mount.xml \ + org.freedesktop.systemd1.Automount.xml \ + org.freedesktop.systemd1.Snapshot.xml \ + org.freedesktop.systemd1.Swap.xml + dist_systemunit_DATA = \ units/emergency.service \ units/getty.target \ @@ -426,6 +440,11 @@ CLEANFILES += \ man/systemd.special.html.in endif +org.freedesktop.systemd1.%.xml: systemd + $(AM_V_GEN)./systemd --introspect=${@:.xml=} > $@ + +CLEANFILES += $(interface_DATA) + install-data-hook: $(MKDIR_P) -m 0755 \ $(DESTDIR)$(systemunitdir) \ diff --git a/fixme b/fixme index b6752d9bf6..6b6f7f7d0a 100644 --- a/fixme +++ b/fixme @@ -64,6 +64,8 @@ * tcpwrap +* introduce exit.target for session instances + Regularly: * look for close() vs. close_nointr() vs. close_nointr_nofail() diff --git a/src/dbus-automount.c b/src/dbus-automount.c index 9003b74b8a..285f666da9 100644 --- a/src/dbus-automount.c +++ b/src/dbus-automount.c @@ -22,16 +22,21 @@ #include "dbus-unit.h" #include "dbus-automount.h" -static const char introspection[] = - DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE - "" - BUS_UNIT_INTERFACE - BUS_PROPERTIES_INTERFACE - " " - " " - " " - BUS_INTROSPECTABLE_INTERFACE - ""; +#define BUS_AUTOMOUNT_INTERFACE \ + " \n" \ + " \n" \ + " \n" + +#define INTROSPECTION \ + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ + "\n" \ + BUS_UNIT_INTERFACE \ + BUS_AUTOMOUNT_INTERFACE \ + BUS_PROPERTIES_INTERFACE \ + BUS_INTROSPECTABLE_INTERFACE \ + "\n" + +const char bus_automount_interface[] = BUS_AUTOMOUNT_INTERFACE; DBusHandlerResult bus_automount_message_handler(Unit *u, DBusMessage *message) { const BusProperty properties[] = { @@ -40,5 +45,5 @@ DBusHandlerResult bus_automount_message_handler(Unit *u, DBusMessage *message) { { NULL, NULL, NULL, NULL, NULL } }; - return bus_default_message_handler(u->meta.manager, message, introspection, properties); + return bus_default_message_handler(u->meta.manager, message, INTROSPECTION, properties); } diff --git a/src/dbus-automount.h b/src/dbus-automount.h index 947bf0f59f..5e0ee5107b 100644 --- a/src/dbus-automount.h +++ b/src/dbus-automount.h @@ -28,4 +28,6 @@ DBusHandlerResult bus_automount_message_handler(Unit *u, DBusMessage *message); +extern const char bus_automount_interface[]; + #endif diff --git a/src/dbus-device.c b/src/dbus-device.c index 83764783ad..0610ab873f 100644 --- a/src/dbus-device.c +++ b/src/dbus-device.c @@ -22,16 +22,21 @@ #include "dbus-unit.h" #include "dbus-device.h" -static const char introspection[] = - DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE - "" - BUS_UNIT_INTERFACE - BUS_PROPERTIES_INTERFACE - " " - " " - " " - BUS_INTROSPECTABLE_INTERFACE - ""; +#define BUS_DEVICE_INTERFACE \ + " \n" \ + " \n" \ + " \n" + +#define INTROSPECTION \ + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ + "\n" \ + BUS_UNIT_INTERFACE \ + BUS_DEVICE_INTERFACE \ + BUS_PROPERTIES_INTERFACE \ + BUS_INTROSPECTABLE_INTERFACE \ + "\n" + +const char bus_device_interface[] = BUS_DEVICE_INTERFACE; DBusHandlerResult bus_device_message_handler(Unit *u, DBusMessage *message) { const BusProperty properties[] = { @@ -40,5 +45,5 @@ DBusHandlerResult bus_device_message_handler(Unit *u, DBusMessage *message) { { NULL, NULL, NULL, NULL, NULL } }; - return bus_default_message_handler(u->meta.manager, message, introspection, properties); + return bus_default_message_handler(u->meta.manager, message, INTROSPECTION, properties); } diff --git a/src/dbus-device.h b/src/dbus-device.h index f2850a63f8..55bb8f55f3 100644 --- a/src/dbus-device.h +++ b/src/dbus-device.h @@ -28,4 +28,6 @@ DBusHandlerResult bus_device_message_handler(Unit *u, DBusMessage *message); +extern const char bus_device_interface[]; + #endif diff --git a/src/dbus-execute.h b/src/dbus-execute.h index 25ecd982a4..6abae1657c 100644 --- a/src/dbus-execute.h +++ b/src/dbus-execute.h @@ -27,23 +27,23 @@ #include "manager.h" #define BUS_EXEC_CONTEXT_INTERFACE \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" #define BUS_EXEC_CONTEXT_PROPERTIES(interface, context) \ { interface, "Environment", bus_property_append_strv, "as", (context).environment }, \ diff --git a/src/dbus-job.c b/src/dbus-job.c index 34e64fc681..7346252a18 100644 --- a/src/dbus-job.c +++ b/src/dbus-job.c @@ -25,20 +25,25 @@ #include "log.h" #include "dbus-job.h" -static const char introspection[] = - DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE - "" - " " - " " - " " - " " - " " - " " - " " - " " - BUS_PROPERTIES_INTERFACE - BUS_INTROSPECTABLE_INTERFACE - ""; +#define BUS_JOB_INTERFACE \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" + +#define INTROSPECTION \ + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ + "\n" \ + BUS_JOB_INTERFACE \ + BUS_PROPERTIES_INTERFACE \ + BUS_INTROSPECTABLE_INTERFACE \ + "\n" + +const char bus_job_interface[] = BUS_JOB_INTERFACE; static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_job_append_state, job_state, JobState); static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_job_append_type, job_type, JobType); @@ -92,7 +97,7 @@ static DBusHandlerResult bus_job_message_dispatch(Job *j, DBusMessage *message) job_free(j); } else - return bus_default_message_handler(j->manager, message, introspection, properties); + return bus_default_message_handler(j->manager, message, INTROSPECTION, properties); if (reply) { if (!dbus_connection_send(m->api_bus, reply, NULL)) diff --git a/src/dbus-job.h b/src/dbus-job.h index 4602358856..2b79423f01 100644 --- a/src/dbus-job.h +++ b/src/dbus-job.h @@ -29,4 +29,6 @@ void bus_job_send_removed_signal(Job *j, bool success); extern const DBusObjectPathVTable bus_job_vtable; +extern const char bus_job_interface[]; + #endif diff --git a/src/dbus-manager.c b/src/dbus-manager.c index 9833b6c85f..6b658d1931 100644 --- a/src/dbus-manager.c +++ b/src/dbus-manager.c @@ -26,77 +26,82 @@ #include "dbus-manager.h" #include "strv.h" +#define BUS_MANAGER_INTERFACE \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " " \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" + #define INTROSPECTION_BEGIN \ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ - "" \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ + "\n" \ + BUS_MANAGER_INTERFACE \ BUS_PROPERTIES_INTERFACE \ BUS_INTROSPECTABLE_INTERFACE #define INTROSPECTION_END \ - "" + "\n" + +const char bus_manager_interface[] = BUS_MANAGER_INTERFACE; static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_manager_append_running_as, manager_running_as, ManagerRunningAs); diff --git a/src/dbus-manager.h b/src/dbus-manager.h index 0acd2d086d..996b680e3c 100644 --- a/src/dbus-manager.h +++ b/src/dbus-manager.h @@ -26,4 +26,6 @@ extern const DBusObjectPathVTable bus_manager_vtable; +extern const char bus_manager_interface[]; + #endif diff --git a/src/dbus-mount.c b/src/dbus-mount.c index 500b773bf6..cccfa60e10 100644 --- a/src/dbus-mount.c +++ b/src/dbus-mount.c @@ -25,23 +25,28 @@ #include "dbus-mount.h" #include "dbus-execute.h" -static const char introspection[] = - DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE - "" - BUS_UNIT_INTERFACE - BUS_PROPERTIES_INTERFACE - " " - " " - " " - " " - " " - " " - BUS_EXEC_CONTEXT_INTERFACE - " " - " " - " " - BUS_INTROSPECTABLE_INTERFACE - ""; +#define BUS_MOUNT_INTERFACE \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + BUS_EXEC_CONTEXT_INTERFACE \ + " \n" \ + " \n" \ + " \n" + +#define INTROSPECTION \ + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ + "\n" \ + BUS_UNIT_INTERFACE \ + BUS_MOUNT_INTERFACE \ + BUS_PROPERTIES_INTERFACE \ + BUS_INTROSPECTABLE_INTERFACE \ + "\n" + +const char bus_mount_interface[] = BUS_MOUNT_INTERFACE; static int bus_mount_append_what(Manager *n, DBusMessageIter *i, const char *property, void *data) { Mount *m = data; @@ -130,5 +135,5 @@ DBusHandlerResult bus_mount_message_handler(Unit *u, DBusMessage *message) { { NULL, NULL, NULL, NULL, NULL } }; - return bus_default_message_handler(u->meta.manager, message, introspection, properties); + return bus_default_message_handler(u->meta.manager, message, INTROSPECTION, properties); } diff --git a/src/dbus-mount.h b/src/dbus-mount.h index b92867df28..6d8d1a9685 100644 --- a/src/dbus-mount.h +++ b/src/dbus-mount.h @@ -28,4 +28,6 @@ DBusHandlerResult bus_mount_message_handler(Unit *u, DBusMessage *message); +extern const char bus_mount_interface[]; + #endif diff --git a/src/dbus-service.c b/src/dbus-service.c index 24dd6c14f6..6286172a1b 100644 --- a/src/dbus-service.c +++ b/src/dbus-service.c @@ -25,29 +25,34 @@ #include "dbus-execute.h" #include "dbus-service.h" -static const char introspection[] = - DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE - "" - BUS_UNIT_INTERFACE - BUS_PROPERTIES_INTERFACE - " " - " " - " " - " " - " " - " " - BUS_EXEC_CONTEXT_INTERFACE - " " - " " - " " - " " - " " - " " - " " - " " - " " - BUS_INTROSPECTABLE_INTERFACE - ""; +#define BUS_SERVICE_INTERFACE \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + BUS_EXEC_CONTEXT_INTERFACE \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" + +#define INTROSPECTION \ + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ + "\n" \ + BUS_UNIT_INTERFACE \ + BUS_SERVICE_INTERFACE \ + BUS_PROPERTIES_INTERFACE \ + BUS_INTROSPECTABLE_INTERFACE \ + "\n" + +const char bus_service_interface[] = BUS_SERVICE_INTERFACE; static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_type, service_type, ServiceType); static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_service_append_restart, service_restart, ServiceRestart); @@ -74,5 +79,5 @@ DBusHandlerResult bus_service_message_handler(Unit *u, DBusMessage *message) { { NULL, NULL, NULL, NULL, NULL } }; - return bus_default_message_handler(u->meta.manager, message, introspection, properties); + return bus_default_message_handler(u->meta.manager, message, INTROSPECTION, properties); } diff --git a/src/dbus-service.h b/src/dbus-service.h index f0a468e90d..ab1e02f352 100644 --- a/src/dbus-service.h +++ b/src/dbus-service.h @@ -28,4 +28,6 @@ DBusHandlerResult bus_service_message_handler(Unit *u, DBusMessage *message); +extern const char bus_service_interface[]; + #endif diff --git a/src/dbus-snapshot.c b/src/dbus-snapshot.c index 8aeca15254..15e51f032e 100644 --- a/src/dbus-snapshot.c +++ b/src/dbus-snapshot.c @@ -22,17 +22,22 @@ #include "dbus-unit.h" #include "dbus-snapshot.h" -static const char introspection[] = - DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE - "" - BUS_UNIT_INTERFACE - BUS_PROPERTIES_INTERFACE - " " - " " - " " - " " - BUS_INTROSPECTABLE_INTERFACE - ""; +#define BUS_SNAPSHOT_INTERFACE \ + " \n" \ + " \n" \ + " \n" \ + " \n" + +#define INTROSPECTION \ + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ + "\n" \ + BUS_UNIT_INTERFACE \ + BUS_SNAPSHOT_INTERFACE \ + BUS_PROPERTIES_INTERFACE \ + BUS_INTROSPECTABLE_INTERFACE \ + "\n" + +const char bus_snapshot_interface[] = BUS_SNAPSHOT_INTERFACE; DBusHandlerResult bus_snapshot_message_handler(Unit *u, DBusMessage *message) { const BusProperty properties[] = { @@ -54,7 +59,7 @@ DBusHandlerResult bus_snapshot_message_handler(Unit *u, DBusMessage *message) { goto oom; } else - return bus_default_message_handler(u->meta.manager, message, introspection, properties); + return bus_default_message_handler(u->meta.manager, message, INTROSPECTION, properties); if (reply) { if (!dbus_connection_send(u->meta.manager->api_bus, reply, NULL)) diff --git a/src/dbus-snapshot.h b/src/dbus-snapshot.h index 5f28550c95..bf5a4d4bc5 100644 --- a/src/dbus-snapshot.h +++ b/src/dbus-snapshot.h @@ -28,4 +28,6 @@ DBusHandlerResult bus_snapshot_message_handler(Unit *u, DBusMessage *message); +extern const char bus_snapshot_interface[]; + #endif diff --git a/src/dbus-socket.c b/src/dbus-socket.c index cb244a9003..426af2b4cf 100644 --- a/src/dbus-socket.c +++ b/src/dbus-socket.c @@ -25,25 +25,30 @@ #include "dbus-socket.h" #include "dbus-execute.h" -static const char introspection[] = - DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE - "" - BUS_UNIT_INTERFACE - BUS_PROPERTIES_INTERFACE - " " - " " - " " - " " - BUS_EXEC_CONTEXT_INTERFACE - " " - " " - " " - " " - " " - " " - " " - BUS_INTROSPECTABLE_INTERFACE - ""; +#define BUS_SOCKET_INTERFACE \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + BUS_EXEC_CONTEXT_INTERFACE \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + +#define INTROSPECTION \ + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ + "\n" \ + BUS_UNIT_INTERFACE \ + BUS_SOCKET_INTERFACE \ + BUS_PROPERTIES_INTERFACE \ + BUS_INTROSPECTABLE_INTERFACE \ + "\n" + +const char bus_socket_interface[] = BUS_SOCKET_INTERFACE; static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_socket_append_bind_ipv6_only, socket_address_bind_ipv6_only, SocketAddressBindIPv6Only); @@ -64,5 +69,5 @@ DBusHandlerResult bus_socket_message_handler(Unit *u, DBusMessage *message) { { NULL, NULL, NULL, NULL, NULL } }; - return bus_default_message_handler(u->meta.manager, message, introspection, properties); + return bus_default_message_handler(u->meta.manager, message, INTROSPECTION, properties); } diff --git a/src/dbus-socket.h b/src/dbus-socket.h index 6a8f534bf5..ab06322042 100644 --- a/src/dbus-socket.h +++ b/src/dbus-socket.h @@ -28,4 +28,6 @@ DBusHandlerResult bus_socket_message_handler(Unit *u, DBusMessage *message); +extern const char bus_socket_interface[]; + #endif diff --git a/src/dbus-swap.c b/src/dbus-swap.c index e935e09bf2..f6f8685daf 100644 --- a/src/dbus-swap.c +++ b/src/dbus-swap.c @@ -25,17 +25,22 @@ #include "dbus-unit.h" #include "dbus-swap.h" -static const char introspection[] = - DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE - "" - BUS_UNIT_INTERFACE - BUS_PROPERTIES_INTERFACE - " " - " " - " " - " " - BUS_INTROSPECTABLE_INTERFACE - ""; +#define BUS_SWAP_INTERFACE \ + " \n" \ + " \n" \ + " \n" \ + " \n" + +#define INTROSPECTION \ + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ + "\n" \ + BUS_UNIT_INTERFACE \ + BUS_SWAP_INTERFACE \ + BUS_PROPERTIES_INTERFACE \ + BUS_INTROSPECTABLE_INTERFACE \ + "\n" + +const char bus_swap_interface[] = BUS_SWAP_INTERFACE; static int bus_swap_append_priority(Manager *m, DBusMessageIter *i, const char *property, void *data) { Swap *s = data; @@ -69,5 +74,5 @@ DBusHandlerResult bus_swap_message_handler(Unit *u, DBusMessage *message) { { NULL, NULL, NULL, NULL, NULL } }; - return bus_default_message_handler(u->meta.manager, message, introspection, properties); + return bus_default_message_handler(u->meta.manager, message, INTROSPECTION, properties); } diff --git a/src/dbus-swap.h b/src/dbus-swap.h index 3bef6ad621..cbd15919db 100644 --- a/src/dbus-swap.h +++ b/src/dbus-swap.h @@ -29,4 +29,6 @@ DBusHandlerResult bus_swap_message_handler(Unit *u, DBusMessage *message); +extern const char bus_swap_interface[]; + #endif diff --git a/src/dbus-target.c b/src/dbus-target.c index be984b9bb4..45f0d31f05 100644 --- a/src/dbus-target.c +++ b/src/dbus-target.c @@ -22,15 +22,20 @@ #include "dbus-unit.h" #include "dbus-target.h" -static const char introspection[] = - DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE - "" - BUS_UNIT_INTERFACE - BUS_PROPERTIES_INTERFACE - " " - " " - BUS_INTROSPECTABLE_INTERFACE - ""; +#define BUS_TARGET_INTERFACE \ + " \n" \ + " \n" + +#define INTROSPECTION \ + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ + "\n" \ + BUS_UNIT_INTERFACE \ + BUS_TARGET_INTERFACE \ + BUS_PROPERTIES_INTERFACE \ + BUS_INTROSPECTABLE_INTERFACE \ + "\n" + +const char bus_target_interface[] = BUS_TARGET_INTERFACE; DBusHandlerResult bus_target_message_handler(Unit *u, DBusMessage *message) { const BusProperty properties[] = { @@ -38,5 +43,5 @@ DBusHandlerResult bus_target_message_handler(Unit *u, DBusMessage *message) { { NULL, NULL, NULL, NULL, NULL } }; - return bus_default_message_handler(u->meta.manager, message, introspection, properties); + return bus_default_message_handler(u->meta.manager, message, INTROSPECTION, properties); } diff --git a/src/dbus-target.h b/src/dbus-target.h index f6a1ac5e65..d118441a67 100644 --- a/src/dbus-target.h +++ b/src/dbus-target.h @@ -28,4 +28,6 @@ DBusHandlerResult bus_target_message_handler(Unit *u, DBusMessage *message); +extern const char bus_target_interface[]; + #endif diff --git a/src/dbus-unit.c b/src/dbus-unit.c index ba428a8d5c..87218cd2ee 100644 --- a/src/dbus-unit.c +++ b/src/dbus-unit.c @@ -25,6 +25,8 @@ #include "log.h" #include "dbus-unit.h" +const char bus_unit_interface[] = BUS_UNIT_INTERFACE; + int bus_unit_append_names(Manager *m, DBusMessageIter *i, const char *property, void *data) { char *t; Iterator j; diff --git a/src/dbus-unit.h b/src/dbus-unit.h index c5840d5632..07641ee951 100644 --- a/src/dbus-unit.h +++ b/src/dbus-unit.h @@ -27,54 +27,54 @@ #include "manager.h" #define BUS_UNIT_INTERFACE \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" #define BUS_UNIT_PROPERTIES \ { "org.freedesktop.systemd1.Unit", "Id", bus_property_append_string, "s", u->meta.id }, \ @@ -125,4 +125,6 @@ void bus_unit_send_removed_signal(Unit *u); extern const DBusObjectPathVTable bus_unit_vtable; +extern const char bus_unit_interface[]; + #endif diff --git a/src/dbus.c b/src/dbus.c index 6ed659a239..5caf1eb191 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -32,6 +32,34 @@ #include "dbus-unit.h" #include "dbus-job.h" #include "dbus-manager.h" +#include "dbus-service.h" +#include "dbus-socket.h" +#include "dbus-target.h" +#include "dbus-device.h" +#include "dbus-mount.h" +#include "dbus-automount.h" +#include "dbus-snapshot.h" +#include "dbus-swap.h" + +static const char bus_properties_interface[] = BUS_PROPERTIES_INTERFACE; +static const char bus_introspectable_interface[] = BUS_INTROSPECTABLE_INTERFACE; + +const char *const bus_interface_table[] = { + "org.freedesktop.DBus.Properties", bus_properties_interface, + "org.freedesktop.DBus.Introspectable", bus_introspectable_interface, + "org.freedesktop.systemd1.Manager", bus_manager_interface, + "org.freedesktop.systemd1.Job", bus_job_interface, + "org.freedesktop.systemd1.Unit", bus_unit_interface, + "org.freedesktop.systemd1.Service", bus_service_interface, + "org.freedesktop.systemd1.Socket", bus_socket_interface, + "org.freedesktop.systemd1.Target", bus_target_interface, + "org.freedesktop.systemd1.Device", bus_device_interface, + "org.freedesktop.systemd1.Mount", bus_mount_interface, + "org.freedesktop.systemd1.Automount", bus_automount_interface, + "org.freedesktop.systemd1.Snapshot", bus_snapshot_interface, + "org.freedesktop.systemd1.Swap", bus_swap_interface, + NULL +}; static void api_bus_dispatch_status(DBusConnection *bus, DBusDispatchStatus status, void *data) { Manager *m = data; diff --git a/src/dbus.h b/src/dbus.h index 51b71eac61..3ad299ed38 100644 --- a/src/dbus.h +++ b/src/dbus.h @@ -37,24 +37,24 @@ typedef struct BusProperty { } BusProperty; #define BUS_PROPERTIES_INTERFACE \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " \ - " " + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" #define BUS_INTROSPECTABLE_INTERFACE \ - " " \ - " " \ - " " \ - " " \ - " " + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" int bus_init_system(Manager *m); int bus_init_api(Manager *m); @@ -104,4 +104,6 @@ int bus_property_append_uint64(Manager *m, DBusMessageIter *i, const char *prope int bus_parse_strv(DBusMessage *m, char ***_l); +extern const char * const bus_interface_table[]; + #endif diff --git a/src/main.c b/src/main.c index 3b9719fb56..d7d3995e1b 100644 --- a/src/main.c +++ b/src/main.c @@ -45,7 +45,8 @@ static enum { ACTION_RUN, ACTION_HELP, ACTION_TEST, - ACTION_DUMP_CONFIGURATION_ITEMS + ACTION_DUMP_CONFIGURATION_ITEMS, + ACTION_DONE } action = ACTION_RUN; static char *default_unit = NULL; @@ -347,7 +348,8 @@ static int parse_argv(int argc, char *argv[]) { ARG_TEST, ARG_DUMP_CONFIGURATION_ITEMS, ARG_CONFIRM_SPAWN, - ARG_DESERIALIZE + ARG_DESERIALIZE, + ARG_INTROSPECT }; static const struct option options[] = { @@ -360,6 +362,7 @@ static int parse_argv(int argc, char *argv[]) { { "dump-configuration-items", no_argument, NULL, ARG_DUMP_CONFIGURATION_ITEMS }, { "confirm-spawn", no_argument, NULL, ARG_CONFIRM_SPAWN }, { "deserialize", required_argument, NULL, ARG_DESERIALIZE }, + { "introspect", optional_argument, NULL, ARG_INTROSPECT }, { NULL, 0, NULL, 0 } }; @@ -444,6 +447,27 @@ static int parse_argv(int argc, char *argv[]) { break; } + case ARG_INTROSPECT: { + const char * const * i = NULL; + + for (i = bus_interface_table; *i; i += 2) + if (!optarg || streq(i[0], optarg)) { + fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE + "\n", stdout); + fputs(i[1], stdout); + fputs("\n", stdout); + + if (optarg) + break; + } + + if (!i[0] && optarg) + log_error("Unknown interface %s.", optarg); + + action = ACTION_DONE; + break; + } + case 'h': action = ACTION_HELP; break; @@ -478,7 +502,8 @@ static int help(void) { " --running-as=AS Set running as (init, system, session)\n" " --test Determine startup sequence, dump it and exit\n" " --dump-configuration-items Dump understood unit configuration items\n" - " --confirm-spawn Ask for confirmation when spawning processes\n", + " --confirm-spawn Ask for confirmation when spawning processes\n" + " --introspect[=INTERFACE] Extract D-Bus interface data\n", __progname); return 0; @@ -582,6 +607,9 @@ int main(int argc, char *argv[]) { unit_dump_config_items(stdout); retval = 0; goto finish; + } else if (action == ACTION_DONE) { + retval = 0; + goto finish; } assert_se(action == ACTION_RUN || action == ACTION_TEST); -- cgit v1.2.3-54-g00ecf From 10e87ee7f66b59a504c0ed2025463ba5faa69923 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 16 Jun 2010 01:58:50 +0200 Subject: install: add systemd-install tool for managing alias/wants symlinks --- .gitignore | 1 + Makefile.am | 62 +++--- fixme | 5 - src/conf-parser.c | 26 ++- src/conf-parser.h | 3 +- src/install.c | 593 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/load-fragment.c | 12 +- src/path-lookup.c | 34 ++- src/path-lookup.h | 2 + src/systemctl.c | 1 + 10 files changed, 684 insertions(+), 55 deletions(-) create mode 100644 src/install.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index de52b6fbd6..ecc9f7074f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-install org.freedesktop.systemd1.*.xml test-ns test-loopback diff --git a/Makefile.am b/Makefile.am index 20fd06f826..d8757868e8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -50,8 +50,11 @@ rootbin_PROGRAMS = \ systemd \ systemctl -if HAVE_GTK bin_PROGRAMS = \ + systemd-install + +if HAVE_GTK +bin_PROGRAMS += \ systemadm endif @@ -275,8 +278,8 @@ systemd_SOURCES = \ $(COMMON_SOURCES) \ src/main.c -systemd_CPPFLAGS = \ - $(AM_CPPFLAGS) \ +systemd_CFLAGS = \ + $(AM_CFLAGS) \ $(DBUS_CFLAGS) \ $(UDEV_CFLAGS) \ $(CGROUP_CFLAGS) @@ -290,14 +293,14 @@ test_engine_SOURCES = \ $(COMMON_SOURCES) \ src/test-engine.c -test_engine_CPPFLAGS = $(systemd_CPPFLAGS) +test_engine_CFLAGS = $(systemd_CFLAGS) test_engine_LDADD = $(systemd_LDADD) test_job_type_SOURCES = \ $(COMMON_SOURCES) \ src/test-job-type.c -test_job_type_CPPFLAGS = $(systemd_CPPFLAGS) +test_job_type_CFLAGS = $(systemd_CFLAGS) test_job_type_LDADD = $(systemd_LDADD) test_ns_SOURCES = \ @@ -305,7 +308,7 @@ test_ns_SOURCES = \ src/test-ns.c \ src/namespace.c -test_ns_CPPFLAGS = $(systemd_CPPFLAGS) +test_ns_CFLAGS = $(systemd_CFLAGS) test_ns_LDADD = $(systemd_LDADD) test_loopback_SOURCES = \ @@ -313,7 +316,7 @@ test_loopback_SOURCES = \ src/test-loopback.c \ src/loopback-setup.c -test_loopback_CPPFLAGS = $(systemd_CPPFLAGS) +test_loopback_CFLAGS = $(systemd_CFLAGS) test_loopback_LDADD = $(systemd_LDADD) systemd_logger_SOURCES = \ @@ -326,8 +329,8 @@ systemd_initctl_SOURCES = \ src/initctl.c \ src/sd-daemon.c -systemd_initctl_CPPFLAGS = \ - $(AM_CPPFLAGS) \ +systemd_initctl_CFLAGS = \ + $(AM_CFLAGS) \ $(DBUS_CFLAGS) systemd_initctl_LDADD = \ @@ -337,8 +340,8 @@ systemd_cgroups_agent_SOURCES = \ $(BASIC_SOURCES) \ src/cgroups-agent.c -systemd_cgroups_agent_CPPFLAGS = \ - $(AM_CPPFLAGS) \ +systemd_cgroups_agent_CFLAGS = \ + $(AM_CFLAGS) \ $(DBUS_CFLAGS) systemd_cgroups_agent_LDADD = \ @@ -348,29 +351,36 @@ systemctl_SOURCES = \ src/systemctl.c \ $(BASIC_SOURCES) -systemctl_CPPFLAGS = $(AM_CPPFLAGS) $(DBUS_CFLAGS) +systemctl_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) systemctl_LDADD = $(DBUS_LIBS) -VALAFLAGS = \ - -g \ - --save-temps \ - --pkg=dbus-glib-1 \ - --pkg=posix - -if HAVE_GTK -VALAFLAGS += \ - --pkg=gtk+-2.0 -endif +systemd_install_SOURCES = \ + src/install.c \ + src/path-lookup.c \ + $(BASIC_SOURCES) -VALA_CFLAGS = \ - -Wno-unused-variable \ - -Wno-unused-function +# We don't really link here against D-Bus, however we indirectly include D-Bus header files +systemd_install_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) systemadm_SOURCES = \ src/systemadm.vala \ src/systemd-interfaces.vala -systemadm_CPPFLAGS = $(AM_CPPFLAGS) $(DBUSGLIB_CFLAGS) $(GTK_CFLAGS) $(VALA_CFLAGS) +systemadm_CFLAGS = \ + $(AM_CFLAGS) \ + $(DBUSGLIB_CFLAGS) \ + $(GTK_CFLAGS) \ + -Wno-unused-variable \ + -Wno-unused-function \ + -Wno-shadow \ + -Wno-format-nonliteral + +systemadm_VALAFLAGS = \ + --pkg=dbus-glib-1 \ + --pkg=posix \ + --pkg=gtk+-2.0 \ + -g + systemadm_LDADD = $(DBUSGLIB_LIBS) $(GTK_LIBS) SED_PROCESS = \ diff --git a/fixme b/fixme index 57682f0ea8..d1718cea5b 100644 --- a/fixme +++ b/fixme @@ -8,11 +8,6 @@ * implicitly import "defaults" settings file into all types -* write .service file install tool - [Install] - WantedBy=graphical.target - Names=prefdm.service - * service startup should be delayed if the matching socket is being started * add #ifdefs for non-redhat builds in sysv parser diff --git a/src/conf-parser.c b/src/conf-parser.c index 20f7641b13..a6d4d92aa2 100644 --- a/src/conf-parser.c +++ b/src/conf-parser.c @@ -40,6 +40,7 @@ static int next_assignment( unsigned line, const char *section, const ConfigItem *t, + bool relaxed, const char *lvalue, const char *rvalue, void *userdata) { @@ -60,18 +61,21 @@ static int next_assignment( if (t->section && !streq(section, t->section)) continue; + if (!t->parse) + return 0; + return t->parse(filename, line, section, lvalue, rvalue, t->data, userdata); } /* Warn about unknown non-extension fields. */ - if (!startswith(lvalue, "X-")) + if (!relaxed && !startswith(lvalue, "X-")) log_info("[%s:%u] Unknown lvalue '%s' in section '%s'. Ignoring.", filename, line, lvalue, strna(section)); return 0; } /* Parse a variable assignment line */ -static int parse_line(const char *filename, unsigned line, char **section, const char* const * sections, const ConfigItem *t, char *l, void *userdata) { +static int parse_line(const char *filename, unsigned line, char **section, const char* const * sections, const ConfigItem *t, bool relaxed, char *l, void *userdata) { char *e; l = strstrip(l); @@ -89,7 +93,7 @@ static int parse_line(const char *filename, unsigned line, char **section, const if (!(fn = file_in_same_dir(filename, strstrip(l+9)))) return -ENOMEM; - r = config_parse(fn, NULL, sections, t, userdata); + r = config_parse(fn, NULL, sections, t, relaxed, userdata); free(fn); return r; @@ -110,11 +114,8 @@ static int parse_line(const char *filename, unsigned line, char **section, const if (!(n = strndup(l+1, k-2))) return -ENOMEM; - if (sections && !strv_contains((char**) sections, n)) { - log_error("[%s:%u] Unknown section '%s'.", filename, line, n); - free(n); - return -EBADMSG; - } + if (!relaxed && sections && !strv_contains((char**) sections, n)) + log_info("[%s:%u] Unknown section '%s'. Ignoring.", filename, line, n); free(*section); *section = n; @@ -122,6 +123,9 @@ static int parse_line(const char *filename, unsigned line, char **section, const return 0; } + if (sections && !strv_contains((char**) sections, *section)) + return 0; + if (!(e = strchr(l, '='))) { log_error("[%s:%u] Missing '='.", filename, line); return -EBADMSG; @@ -130,11 +134,11 @@ static int parse_line(const char *filename, unsigned line, char **section, const *e = 0; e++; - return next_assignment(filename, line, *section, t, strstrip(l), strstrip(e), userdata); + return next_assignment(filename, line, *section, t, relaxed, strstrip(l), strstrip(e), userdata); } /* Go through the file and parse each line */ -int config_parse(const char *filename, FILE *f, const char* const * sections, const ConfigItem *t, void *userdata) { +int config_parse(const char *filename, FILE *f, const char* const * sections, const ConfigItem *t, bool relaxed, void *userdata) { unsigned line = 0; char *section = NULL; int r; @@ -165,7 +169,7 @@ int config_parse(const char *filename, FILE *f, const char* const * sections, co goto finish; } - if ((r = parse_line(filename, ++line, §ion, sections, t, l, userdata)) < 0) + if ((r = parse_line(filename, ++line, §ion, sections, t, relaxed, l, userdata)) < 0) goto finish; } diff --git a/src/conf-parser.h b/src/conf-parser.h index bea2a8e9be..b3b2915d41 100644 --- a/src/conf-parser.h +++ b/src/conf-parser.h @@ -23,6 +23,7 @@ ***/ #include +#include /* An abstract parser for simple, line based, shallow configuration * files consisting of variable assignments only. */ @@ -40,7 +41,7 @@ typedef struct ConfigItem { /* The configuration file parsing routine. Expects a table of * config_items in *t that is terminated by an item where lvalue is * NULL */ -int config_parse(const char *filename, FILE *f, const char* const * sections, const ConfigItem *t, void *userdata); +int config_parse(const char *filename, FILE *f, const char* const *sections, const ConfigItem *t, bool relaxed, void *userdata); /* Generic parsers */ int config_parse_int(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata); diff --git a/src/install.c b/src/install.c new file mode 100644 index 0000000000..e3db63769f --- /dev/null +++ b/src/install.c @@ -0,0 +1,593 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "log.h" +#include "path-lookup.h" +#include "util.h" +#include "macro.h" +#include "strv.h" +#include "conf-parser.h" + +static bool arg_force = false; + +static enum { + WHERE_SYSTEM, + WHERE_SESSION, + WHERE_GLOBAL, +} arg_where = WHERE_SYSTEM; + +static enum { + ACTION_INVALID, + ACTION_ENABLE, + ACTION_DISABLE, + ACTION_TEST +} arg_action = ACTION_INVALID; + +typedef struct { + char *name; + char *path; + + char **aliases; + char **wanted_by; +} InstallInfo; + +Hashmap *will_install = NULL, *have_installed = NULL; + +static int help(void) { + + printf("%s [options]\n\n" + " -h --help Show this help\n" + " --force Override existing links\n" + " --system Install into system\n" + " --session Install into session\n" + " --global Install into all sessions\n" + "Commands:\n" + " enable [NAME...] Enable one or more units\n" + " disable [NAME...] Disable one or more units\n" + " test [NAME...] Test whether any of the specified units are enabled\n", + __progname); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_SESSION = 0x100, + ARG_SYSTEM, + ARG_GLOBAL, + ARG_FORCE + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "session", no_argument, NULL, ARG_SESSION }, + { "system", no_argument, NULL, ARG_SYSTEM }, + { "global", no_argument, NULL, ARG_GLOBAL }, + { "force", no_argument, NULL, ARG_FORCE }, + { NULL, 0, NULL, 0 } + }; + + int c; + + assert(argc >= 1); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_SESSION: + arg_where = WHERE_SESSION; + break; + + case ARG_SYSTEM: + arg_where = WHERE_SYSTEM; + break; + + case ARG_GLOBAL: + arg_where = WHERE_GLOBAL; + break; + + case ARG_FORCE: + arg_force = true; + break; + + case '?': + return -EINVAL; + + default: + log_error("Unknown option code %c", c); + return -EINVAL; + } + } + + if (optind >= argc) { + log_error("Missing verb."); + return -EINVAL; + } + + if (streq(argv[optind], "enable")) + arg_action = ACTION_ENABLE; + else if (streq(argv[optind], "disable")) + arg_action = ACTION_DISABLE; + else if (streq(argv[optind], "test")) + arg_action = ACTION_TEST; + else { + log_error("Unknown verb %s", argv[optind]); + return -EINVAL; + } + + optind++; + + if (optind >= argc) { + log_error("Missing unit name."); + return -EINVAL; + } + + return 1; +} + +static void install_info_free(InstallInfo *i) { + assert(i); + + free(i->name); + free(i->path); + strv_free(i->aliases); + strv_free(i->wanted_by); + free(i); +} + +static void install_info_hashmap_free(Hashmap *m) { + InstallInfo *i; + + while ((i = hashmap_steal_first(m))) + install_info_free(i); + + hashmap_free(m); +} + +static bool unit_name_valid(const char *name) { + + /* This is a minimal version of unit_name_valid() from + * unit-name.c */ + + if (strchr(name, '/')) + return false; + + if (!*name) + return false; + + if (ignore_file(name)) + return false; + + return true; +} + +static int install_info_add(const char *name) { + InstallInfo *i; + int r; + + if (!unit_name_valid(name)) + return -EINVAL; + + if (hashmap_get(have_installed, name) || + hashmap_get(will_install, name)) + return 0; + + if (!(i = new0(InstallInfo, 1))) { + r = -ENOMEM; + goto fail; + } + + if (!(i->name = strdup(name))) { + r = -ENOMEM; + goto fail; + } + + if ((r = hashmap_put(will_install, i->name, i)) < 0) + goto fail; + + return 0; + +fail: + if (i) + install_info_free(i); + + return r; +} + +static int config_parse_also( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + char *w; + size_t l; + char *state; + + assert(filename); + assert(lvalue); + assert(rvalue); + + FOREACH_WORD_QUOTED(w, l, rvalue, state) { + char *n; + int r; + + if (!(n = strndup(w, l))) + return -ENOMEM; + + r = install_info_add(n); + free(n); + + if (r < 0) + return r; + } + + return 0; +} + +static int create_symlink(const char *old_path, const char *new_path) { + int r; + + assert(old_path); + assert(new_path); + + if (arg_action == ACTION_ENABLE) { + char *dest; + + mkdir_parents(new_path, 0755); + + if (symlink(old_path, new_path) >= 0) + return 0; + + if (errno != EEXIST) { + log_error("Cannot link %s to %s: %m", old_path, new_path); + return -errno; + } + + if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) { + + if (errno == EINVAL) { + log_error("Cannot link %s to %s, file exists already and is not a symlink.", old_path, new_path); + return -EEXIST; + } + + log_error("readlink() failed: %s", strerror(-r)); + return r; + } + + if (streq(dest, old_path)) { + free(dest); + return 0; + } + + if (!arg_force) { + log_error("Cannot link %s to %s, symlink exists already and points to %s.", old_path, new_path, dest); + free(dest); + return -EEXIST; + } + + free(dest); + unlink(new_path); + + if (symlink(old_path, new_path) >= 0) + return 0; + + log_error("Cannot link %s to %s: %m", old_path, new_path); + return -errno; + + } else if (arg_action == ACTION_DISABLE) { + char *dest; + + if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) { + if (errno == ENOENT) + return 0; + + if (errno == EINVAL) { + log_warning("File %s not a symlink, ignoring.", old_path); + return 0; + } + + log_error("readlink() failed: %s", strerror(-r)); + return r; + } + + if (!streq(dest, old_path)) { + log_warning("File %s not a symlink to %s but points to %s, ignoring.", new_path, old_path, dest); + free(dest); + return 0; + } + + free(dest); + if (unlink(new_path) >= 0) + return 0; + + log_error("Cannot unlink %s: %m", new_path); + return -errno; + + } else if (arg_action == ACTION_TEST) { + char *dest; + + if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) { + + if (errno == ENOENT || errno == EINVAL) + return 0; + + log_error("readlink() failed: %s", strerror(-r)); + return r; + } + + if (streq(dest, old_path)) { + free(dest); + return 1; + } + + return 0; + } + + assert_not_reached("Unknown action."); +} + +static int install_info_symlink_alias(InstallInfo *i, const char *config_path) { + char **s; + char *alias_path = NULL; + int r; + + assert(i); + + STRV_FOREACH(s, i->aliases) { + + if (!unit_name_valid(*s)) { + log_error("Invalid name %s.", *s); + r = -EINVAL; + goto finish; + } + + free(alias_path); + if (!(alias_path = path_make_absolute(*s, config_path))) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + if ((r = create_symlink(i->path, alias_path)) != 0) + goto finish; + } + + r = 0; + +finish: + free(alias_path); + + return r; +} + +static int install_info_symlink_wants(InstallInfo *i, const char *config_path) { + char **s; + char *alias_path = NULL; + int r; + + assert(i); + + STRV_FOREACH(s, i->wanted_by) { + if (!unit_name_valid(*s)) { + log_error("Invalid name %s.", *s); + r = -EINVAL; + goto finish; + } + + free(alias_path); + alias_path = NULL; + + if (asprintf(&alias_path, "%s/%s.wants/%s", config_path, *s, i->name) < 0) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + if ((r = create_symlink(i->path, alias_path)) != 0) + goto finish; + + if (arg_action == ACTION_DISABLE) { + char *t; + + /* Try to remove .wants dir if we don't need it anymore */ + if (asprintf(&t, "%s/%s.wants", config_path, *s) >= 0) { + rmdir(t); + free(t); + } + } + } + + r = 0; + +finish: + free(alias_path); + + return r; +} + +static int install_info_apply(LookupPaths *paths, InstallInfo *i, const char *config_path) { + + const ConfigItem items[] = { + { "Alias", config_parse_strv, &i->aliases, "Install" }, + { "WantedBy", config_parse_strv, &i->wanted_by, "Install" }, + { "Also", config_parse_also, NULL, "Install" }, + + { NULL, NULL, NULL, NULL } + }; + + char **p; + char *filename = NULL; + FILE *f = NULL; + int r; + + assert(paths); + assert(i); + + STRV_FOREACH(p, paths->unit_path) { + + if (!(filename = path_make_absolute(i->name, *p))) { + log_error("Out of memory"); + return -ENOMEM; + } + + if ((f = fopen(filename, "re"))) + break; + + free(filename); + filename = NULL; + + if (errno != ENOENT) { + log_error("Failed to open %s: %m", filename); + return -errno; + } + } + + if (!f) { + log_error("Couldn't find %s.", i->name); + return -ENOENT; + } + + i->path = filename; + + if ((r = config_parse(filename, f, NULL, items, true, i)) < 0) { + fclose(f); + return r; + } + + fclose(f); + + if ((r = install_info_symlink_alias(i, config_path)) != 0) + return r; + + if ((r = install_info_symlink_wants(i, config_path)) != 0) + return r; + + return 0; +} + +static char *get_config_path(void) { + + switch (arg_where) { + + case WHERE_SYSTEM: + return strdup(SYSTEM_CONFIG_UNIT_PATH); + + case WHERE_GLOBAL: + return strdup(SESSION_CONFIG_UNIT_PATH); + + case WHERE_SESSION: { + char *p; + + if (session_config_home(&p) < 0) + return NULL; + + return p; + } + + default: + assert_not_reached("Unknown config path."); + } +} + +int main(int argc, char *argv[]) { + int r, retval = 1, j; + LookupPaths paths; + InstallInfo *i; + char *config_path = NULL; + + zero(paths); + + log_set_target(LOG_TARGET_CONSOLE); + log_set_max_level(LOG_INFO); + log_parse_environment(); + + if ((r = parse_argv(argc, argv)) < 0) + goto finish; + else if (r == 0) { + retval = 0; + goto finish; + } + + if ((r = lookup_paths_init(&paths, arg_where == WHERE_SYSTEM ? MANAGER_INIT : MANAGER_SESSION)) < 0) { + log_error("Failed to determine lookup paths: %s", strerror(-r)); + goto finish; + } + + if (!(config_path = get_config_path())) { + log_error("Failed to determine config path"); + goto finish; + } + + will_install = hashmap_new(string_hash_func, string_compare_func); + have_installed = hashmap_new(string_hash_func, string_compare_func); + + if (!will_install || !have_installed) { + log_error("Failed to allocate unit sets."); + goto finish; + } + + for (j = optind; j < argc; j++) + if ((r = install_info_add(argv[j])) < 0) + goto finish; + + while ((i = hashmap_first(will_install))) { + assert_se(hashmap_move_one(have_installed, will_install, i->name) == 0); + + if ((r = install_info_apply(&paths, i, config_path)) != 0) { + + if (r < 0) + goto finish; + + /* In test mode and found something */ + retval = 0; + goto finish; + } + } + + retval = arg_action == ACTION_TEST ? 1 : 0; + +finish: + install_info_hashmap_free(will_install); + install_info_hashmap_free(have_installed); + + lookup_paths_free(&paths); + + free(config_path); + + return retval; +} diff --git a/src/load-fragment.c b/src/load-fragment.c index 793c952594..cf1434eb1e 100644 --- a/src/load-fragment.c +++ b/src/load-fragment.c @@ -1471,12 +1471,17 @@ static int load_from_path(Unit *u, const char *path) { { "DirectoryNotEmpty", config_parse_path_spec, &u->path, "Path" }, { "Unit", config_parse_path_unit, &u->path, "Path" }, + /* The [Install] section is ignored here. */ + { "Alias", NULL, NULL, "Install" }, + { "WantedBy", NULL, NULL, "Install" }, + { "Also", NULL, NULL, "Install" }, + { NULL, NULL, NULL, NULL } }; #undef EXEC_CONTEXT_CONFIG_ITEMS - const char *sections[3]; + const char *sections[4]; int r; Set *symlink_names; FILE *f = NULL; @@ -1494,7 +1499,8 @@ static int load_from_path(Unit *u, const char *path) { sections[0] = "Unit"; sections[1] = section_table[u->meta.type]; - sections[2] = NULL; + sections[2] = "Install"; + sections[3] = NULL; if (!(symlink_names = set_new(string_hash_func, string_compare_func))) return -ENOMEM; @@ -1563,7 +1569,7 @@ static int load_from_path(Unit *u, const char *path) { } /* Now, parse the file contents */ - if ((r = config_parse(filename, f, sections, items, u)) < 0) + if ((r = config_parse(filename, f, sections, items, false, u)) < 0) goto finish; free(u->meta.fragment_path); diff --git a/src/path-lookup.c b/src/path-lookup.c index 1c4b5dc4e1..7a5b9b813e 100644 --- a/src/path-lookup.c +++ b/src/path-lookup.c @@ -30,6 +30,28 @@ #include "path-lookup.h" +int session_config_home(char **config_home) { + const char *e; + + if ((e = getenv("XDG_CONFIG_HOME"))) { + if (asprintf(config_home, "%s/systemd/session", e) < 0) + return -ENOMEM; + + return 1; + } else { + const char *home; + + if ((home = getenv("HOME"))) { + if (asprintf(config_home, "%s/.config/systemd/session", home) < 0) + return -ENOMEM; + + return 1; + } + } + + return 0; +} + static char** session_dirs(void) { const char *home, *e; char *config_home = NULL, *data_home = NULL; @@ -45,16 +67,10 @@ static char** session_dirs(void) { * as data, and allow overriding as configuration. */ - home = getenv("HOME"); - - if ((e = getenv("XDG_CONFIG_HOME"))) { - if (asprintf(&config_home, "%s/systemd/session", e) < 0) - goto fail; + if (session_config_home(&config_home) < 0) + goto fail; - } else if (home) { - if (asprintf(&config_home, "%s/.config/systemd/session", home) < 0) - goto fail; - } + home = getenv("HOME"); if ((e = getenv("XDG_CONFIG_DIRS"))) if (!(config_dirs = strv_split(e, ":"))) diff --git a/src/path-lookup.h b/src/path-lookup.h index a04d5a0fab..e5a10925fd 100644 --- a/src/path-lookup.h +++ b/src/path-lookup.h @@ -30,6 +30,8 @@ typedef struct LookupPaths { #include "manager.h" +int session_config_home(char **config_home); + int lookup_paths_init(LookupPaths *p, ManagerRunningAs running_as); void lookup_paths_free(LookupPaths *p); diff --git a/src/systemctl.c b/src/systemctl.c index 4afe53e18d..663cb833bc 100644 --- a/src/systemctl.c +++ b/src/systemctl.c @@ -1437,6 +1437,7 @@ int main(int argc, char*argv[]) { dbus_error_init(&error); log_set_target(LOG_TARGET_CONSOLE); + log_set_max_level(LOG_INFO); log_parse_environment(); if ((r = parse_argv(argc, argv)) < 0) -- cgit v1.2.3-54-g00ecf From 8c47c7325fa1ab72febf807f8831ff24c75fbf45 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 16 Jun 2010 05:10:31 +0200 Subject: notify: add minimal readiness/status protocol for spawned daemons --- .gitignore | 1 + Makefile.am | 9 ++- fixme | 4 +- src/cgroup.c | 31 +++++++++++ src/cgroup.h | 2 + src/dbus-service.c | 4 +- src/initctl.c | 7 +++ src/logger.c | 7 +++ src/manager.c | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++++- src/manager.h | 3 +- src/mount.c | 8 ++- src/sd-daemon.c | 113 ++++++++++++++++++++++++++++++++++++- src/sd-daemon.h | 59 ++++++++++++++++++++ src/service.c | 81 ++++++++++++++++++++++++--- src/service.h | 3 + src/socket.c | 8 ++- src/test-daemon.c | 37 ++++++++++++ src/unit.h | 3 + 18 files changed, 516 insertions(+), 25 deletions(-) create mode 100644 src/test-daemon.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index ecc9f7074f..0ad1446777 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +test-daemon systemd-install org.freedesktop.systemd1.*.xml test-ns diff --git a/Makefile.am b/Makefile.am index d8757868e8..e50ae82040 100644 --- a/Makefile.am +++ b/Makefile.am @@ -67,7 +67,8 @@ noinst_PROGRAMS = \ test-engine \ test-job-type \ test-ns \ - test-loopback + test-loopback \ + test-daemon dist_dbuspolicy_DATA = \ src/org.freedesktop.systemd1.conf @@ -316,8 +317,10 @@ test_loopback_SOURCES = \ src/test-loopback.c \ src/loopback-setup.c -test_loopback_CFLAGS = $(systemd_CFLAGS) -test_loopback_LDADD = $(systemd_LDADD) +test_daemon_SOURCES = \ + $(BASIC_SOURCES) \ + src/test-daemon.c \ + src/sd-daemon.c systemd_logger_SOURCES = \ $(BASIC_SOURCES) \ diff --git a/fixme b/fixme index d1718cea5b..e39e7251dc 100644 --- a/fixme +++ b/fixme @@ -1,4 +1,4 @@ -* timer +* calendar time support in timer * enforce max number of concurrent connection limit in sockets. @@ -49,8 +49,6 @@ - bluetoothd (/var/run/sdp! @/org/bluez/audio!) - distccd -* regnerate unit/sysv search paths on daemon reload - * write utmp record a la upstart for processes * run PAM session stuff diff --git a/src/cgroup.c b/src/cgroup.c index 330014dc9f..108c4fcf5e 100644 --- a/src/cgroup.c +++ b/src/cgroup.c @@ -535,6 +535,37 @@ int cgroup_notify_empty(Manager *m, const char *group) { return 0; } +Unit* cgroup_unit_by_pid(Manager *m, pid_t pid) { + CGroupBonding *l, *b; + char *group = NULL; + int r; + + assert(m); + + if (pid <= 1) + return NULL; + + if ((r = cgroup_get_current_controller_path(pid, m->cgroup_controller, &group))) + return NULL; + + l = hashmap_get(m->cgroup_bondings, group); + free(group); + + if (!l) + return NULL; + + LIST_FOREACH(by_path, b, l) { + + if (!b->unit) + continue; + + if (b->only_us) + return b->unit; + } + + return NULL; +} + CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller) { CGroupBonding *b; diff --git a/src/cgroup.h b/src/cgroup.h index 26fac0a660..67c7cc3501 100644 --- a/src/cgroup.h +++ b/src/cgroup.h @@ -76,4 +76,6 @@ int manager_shutdown_cgroup(Manager *m, bool delete); int cgroup_notify_empty(Manager *m, const char *group); +Unit* cgroup_unit_by_pid(Manager *m, pid_t pid); + #endif diff --git a/src/dbus-service.c b/src/dbus-service.c index 6286172a1b..f70a77212c 100644 --- a/src/dbus-service.c +++ b/src/dbus-service.c @@ -41,7 +41,8 @@ " \n" \ " \n" \ " \n" \ - " \n" + " \n" \ + " \n" #define INTROSPECTION \ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ @@ -76,6 +77,7 @@ DBusHandlerResult bus_service_message_handler(Unit *u, DBusMessage *message) { { "org.freedesktop.systemd1.Service", "ControlPID", bus_property_append_pid, "u", &u->service.control_pid }, { "org.freedesktop.systemd1.Service", "SysVPath", bus_property_append_string, "s", u->service.sysv_path }, { "org.freedesktop.systemd1.Service", "BusName", bus_property_append_string, "s", u->service.bus_name }, + { "org.freedesktop.systemd1.Service", "StatusText", bus_property_append_string, "s", u->service.status_text }, { NULL, NULL, NULL, NULL, NULL } }; diff --git a/src/initctl.c b/src/initctl.c index 34c38839df..56ed5cdf30 100644 --- a/src/initctl.c +++ b/src/initctl.c @@ -354,6 +354,10 @@ int main(int argc, char *argv[]) { if (server_init(&server, (unsigned) n) < 0) return 2; + sd_notify(false, + "READY=1\n" + "STATUS=Processing requests..."); + for (;;) { struct epoll_event event; int k; @@ -378,6 +382,9 @@ int main(int argc, char *argv[]) { r = 0; fail: + sd_notify(false, + "STATUS=Shutting down..."); + server_done(&server); log_info("systemd-initctl stopped as pid %llu", (unsigned long long) getpid()); diff --git a/src/logger.c b/src/logger.c index 5c7e4ee42b..48eee6cd12 100644 --- a/src/logger.c +++ b/src/logger.c @@ -547,6 +547,10 @@ int main(int argc, char *argv[]) { if (server_init(&server, (unsigned) n) < 0) return 3; + sd_notify(false, + "READY=1\n" + "STATUS=Processing requests..."); + for (;;) { struct epoll_event event; int k; @@ -571,6 +575,9 @@ int main(int argc, char *argv[]) { r = 0; fail: + sd_notify(false, + "STATUS=Shutting down..."); + server_done(&server); log_info("systemd-logger stopped as pid %llu", (unsigned long long) getpid()); diff --git a/src/manager.c b/src/manager.c index 4dcdf2e507..97d05b52c3 100644 --- a/src/manager.c +++ b/src/manager.c @@ -60,6 +60,67 @@ /* As soon as 5s passed since a unit was added to our GC queue, make sure to run a gc sweep */ #define GC_QUEUE_USEC_MAX (10*USEC_PER_SEC) +/* Where clients shall send notification messages to */ +#define NOTIFY_SOCKET "/org/freedesktop/systemd1/notify" + +static int manager_setup_notify(Manager *m) { + union { + struct sockaddr sa; + struct sockaddr_un un; + } sa; + struct epoll_event ev; + char *ne[2], **t; + int one = 1; + + assert(m); + + m->notify_watch.type = WATCH_NOTIFY; + if ((m->notify_watch.fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) { + log_error("Failed to allocate notification socket: %m"); + return -errno; + } + + zero(sa); + sa.sa.sa_family = AF_UNIX; + + if (m->running_as == MANAGER_SESSION) + snprintf(sa.un.sun_path+1, sizeof(sa.un.sun_path)-1, NOTIFY_SOCKET "/%llu", random_ull()); + else + strncpy(sa.un.sun_path+1, NOTIFY_SOCKET, sizeof(sa.un.sun_path)-1); + + if (bind(m->notify_watch.fd, &sa.sa, sizeof(sa)) < 0) { + log_error("bind() failed: %m"); + return -errno; + } + + if (setsockopt(m->notify_watch.fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) { + log_error("SO_PASSCRED failed: %m"); + return -errno; + } + + zero(ev); + ev.events = EPOLLIN; + ev.data.ptr = &m->notify_watch; + + if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->notify_watch.fd, &ev) < 0) + return -errno; + + if (asprintf(&ne[0], "NOTIFY_SOCKET=@%s", sa.un.sun_path+1) < 0) + return -ENOMEM; + + ne[1] = NULL; + t = strv_env_merge(m->environment, ne, NULL); + free(ne[0]); + + if (!t) + return -ENOMEM; + + strv_free(m->environment); + m->environment = t; + + return 0; +} + static int enable_special_signals(Manager *m) { char fd; @@ -177,6 +238,9 @@ int manager_new(ManagerRunningAs running_as, bool confirm_spawn, Manager **_m) { if ((r = manager_setup_cgroup(m)) < 0) goto fail; + if ((r = manager_setup_notify(m)) < 0) + goto fail; + /* Try to connect to the busses, if possible. */ if ((r = bus_init_system(m)) < 0 || (r = bus_init_api(m)) < 0) @@ -364,6 +428,8 @@ void manager_free(Manager *m) { close_nointr_nofail(m->epoll_fd); if (m->signal_watch.fd >= 0) close_nointr_nofail(m->signal_watch.fd); + if (m->notify_watch.fd >= 0) + close_nointr_nofail(m->notify_watch.fd); lookup_paths_free(&m->lookup_paths); strv_free(m->environment); @@ -1521,12 +1587,82 @@ unsigned manager_dispatch_dbus_queue(Manager *m) { return n; } +static int manager_process_notify_fd(Manager *m) { + ssize_t n; + + assert(m); + + for (;;) { + char buf[4096]; + struct msghdr msghdr; + struct iovec iovec; + struct ucred *ucred; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; + } control; + Unit *u; + char **tags; + + zero(iovec); + iovec.iov_base = buf; + iovec.iov_len = sizeof(buf)-1; + + zero(control); + zero(msghdr); + msghdr.msg_iov = &iovec; + msghdr.msg_iovlen = 1; + msghdr.msg_control = &control; + msghdr.msg_controllen = sizeof(control); + + if ((n = recvmsg(m->notify_watch.fd, &msghdr, MSG_DONTWAIT)) <= 0) { + if (n >= 0) + return -EIO; + + if (errno == EAGAIN) + break; + + return -errno; + } + + if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) || + control.cmsghdr.cmsg_level != SOL_SOCKET || + control.cmsghdr.cmsg_type != SCM_CREDENTIALS || + control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) { + log_warning("Received notify message without credentials. Ignoring."); + continue; + } + + ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr); + + if (!(u = hashmap_get(m->watch_pids, UINT32_TO_PTR(ucred->pid)))) + if (!(u = cgroup_unit_by_pid(m, ucred->pid))) { + log_warning("Cannot find unit for notify message of PID %lu.", (unsigned long) ucred->pid); + continue; + } + + char_array_0(buf); + if (!(tags = strv_split(buf, "\n\r"))) + return -ENOMEM; + + log_debug("Got notification message for unit %s", u->meta.id); + + if (UNIT_VTABLE(u)->notify_message) + UNIT_VTABLE(u)->notify_message(u, tags); + + strv_free(tags); + } + + return 0; +} + static int manager_dispatch_sigchld(Manager *m) { assert(m); for (;;) { siginfo_t si; Unit *u; + int r; zero(si); @@ -1555,6 +1691,17 @@ static int manager_dispatch_sigchld(Manager *m) { free(name); } + /* Let's flush any message the dying child might still + * have queued for us. This ensures that the process + * still exists in /proc so that we can figure out + * which cgroup and hence unit it belongs to. */ + if ((r = manager_process_notify_fd(m)) < 0) + return r; + + /* And now figure out the unit this belongs to */ + if (!(u = hashmap_get(m->watch_pids, UINT32_TO_PTR(si.si_pid)))) + u = cgroup_unit_by_pid(m, si.si_pid); + /* And now, we actually reap the zombie. */ if (waitid(P_PID, si.si_pid, &si, WEXITED) < 0) { if (errno == EINTR) @@ -1572,11 +1719,12 @@ static int manager_dispatch_sigchld(Manager *m) { si.si_status, strna(si.si_code == CLD_EXITED ? exit_status_to_string(si.si_status) : strsignal(si.si_status))); - if (!(u = hashmap_remove(m->watch_pids, UINT32_TO_PTR(si.si_pid)))) + if (!u) continue; log_debug("Child %llu belongs to %s", (long long unsigned) si.si_pid, u->meta.id); + hashmap_remove(m->watch_pids, UINT32_TO_PTR(si.si_pid)); UNIT_VTABLE(u)->sigchld_event(u, si.si_pid, si.si_code, si.si_status); } @@ -1738,6 +1886,17 @@ static int process_event(Manager *m, struct epoll_event *ev) { break; + case WATCH_NOTIFY: + + /* An incoming daemon notification event? */ + if (ev->events != EPOLLIN) + return -EINVAL; + + if ((r = manager_process_notify_fd(m)) < 0) + return r; + + break; + case WATCH_FD: /* Some fd event, to be dispatched to the units */ diff --git a/src/manager.h b/src/manager.h index d78bcf5d76..070a27f70b 100644 --- a/src/manager.h +++ b/src/manager.h @@ -56,6 +56,7 @@ typedef enum ManagerRunningAs { enum WatchType { WATCH_INVALID, WATCH_SIGNAL, + WATCH_NOTIFY, WATCH_FD, WATCH_TIMER, WATCH_MOUNT, @@ -171,6 +172,7 @@ struct Manager { Hashmap *watch_pids; /* pid => Unit object n:1 */ + Watch notify_watch; Watch signal_watch; int epoll_fd; @@ -215,7 +217,6 @@ struct Manager { char *cgroup_hierarchy; usec_t gc_queue_timestamp; - int gc_marker; unsigned n_in_gc_queue; diff --git a/src/mount.c b/src/mount.c index 5577f16dfd..a8f3d7b9aa 100644 --- a/src/mount.c +++ b/src/mount.c @@ -921,12 +921,14 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) { assert(m); assert(pid >= 0); - success = is_clean_exit(code, status); - m->failure = m->failure || !success; + if (pid != m->control_pid) + return; - assert(m->control_pid == pid); m->control_pid = 0; + success = is_clean_exit(code, status); + m->failure = m->failure || !success; + if (m->control_command) { exec_status_fill(&m->control_command->exec_status, pid, code, status); m->control_command = NULL; diff --git a/src/sd-daemon.c b/src/sd-daemon.c index 29bd204680..0dad73f94d 100644 --- a/src/sd-daemon.c +++ b/src/sd-daemon.c @@ -24,6 +24,10 @@ SOFTWARE. ***/ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include #include #include @@ -34,12 +38,14 @@ #include #include #include +#include +#include #include "sd-daemon.h" int sd_listen_fds(int unset_environment) { -#ifdef DISABLE_SYSTEMD +#if defined(DISABLE_SYSTEMD) || !defined(__linux__) return 0; #else int r, fd; @@ -317,3 +323,108 @@ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t return 1; } + +int sd_notify(int unset_environment, const char *state) { +#if defined(DISABLE_SYSTEMD) || !defined(__linux__) + return 0; +#else + int fd = -1, r; + struct msghdr msghdr; + struct iovec iovec; + union sockaddr_union sockaddr; + struct ucred *ucred; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; + } control; + const char *e; + + if (!state) { + r = -EINVAL; + goto finish; + } + + if (!(e = getenv("NOTIFY_SOCKET"))) { + r = 0; + goto finish; + } + + /* Must be an abstract socket, or an absolute path */ + if ((e[0] != '@' && e[0] != '/') || e[1] == 0) { + r = -EINVAL; + goto finish; + } + + if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) { + r = -errno; + goto finish; + } + + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sa.sa_family = AF_UNIX; + strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path)); + + if (sockaddr.un.sun_path[0] == '@') + sockaddr.un.sun_path[0] = 0; + + memset(&iovec, 0, sizeof(iovec)); + iovec.iov_base = (char*) state; + iovec.iov_len = strlen(state); + + memset(&control, 0, sizeof(control)); + control.cmsghdr.cmsg_level = SOL_SOCKET; + control.cmsghdr.cmsg_type = SCM_CREDENTIALS; + control.cmsghdr.cmsg_len = CMSG_LEN(sizeof(struct ucred)); + + ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr); + ucred->pid = getpid(); + ucred->uid = getuid(); + ucred->gid = getgid(); + + memset(&msghdr, 0, sizeof(msghdr)); + msghdr.msg_name = &sockaddr; + msghdr.msg_namelen = sizeof(struct sockaddr_un); + msghdr.msg_iov = &iovec; + msghdr.msg_iovlen = 1; + msghdr.msg_control = &control; + msghdr.msg_controllen = control.cmsghdr.cmsg_len; + + if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) { + r = -errno; + goto finish; + } + + r = 0; + +finish: + if (unset_environment) + unsetenv("NOTIFY_SOCKET"); + + if (fd >= 0) + close(fd); + + return r; +#endif +} + +int sd_notifyf(int unset_environment, const char *format, ...) { +#if defined(DISABLE_SYSTEMD) || !defined(__linux__) + return 0; +#else + va_list ap; + char *p = NULL; + int r; + + va_start(ap, format); + r = vasprintf(&p, format, ap); + va_end(ap); + + if (r < 0 || !p) + return -ENOMEM; + + r = sd_notify(unset_environment, p); + free(p); + + return r; +#endif +} diff --git a/src/sd-daemon.h b/src/sd-daemon.h index 0d8de45b72..0277b0fb5c 100644 --- a/src/sd-daemon.h +++ b/src/sd-daemon.h @@ -27,8 +27,13 @@ SOFTWARE. ***/ +#include #include +#ifdef __cplusplus +extern "C" { +#endif + /* Reference implementation of a few systemd related interfaces for * writing daemons. These interfaces are trivial to implement. To * simplify porting we provide this reference @@ -111,4 +116,58 @@ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port * errno style error code on failure. */ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length); +/* Informs systemd about changed daemon state. This takes a numeber of + * newline seperated environment-style variable assignments in a + * string. The following strings are known: + * + * READY=1 Tells systemd that daemon startup is finished (only + * relevant for services of Type=notify). The passed + * argument is a boolean "1" or "0". Since there is + * little value in signalling non-readiness the only + * value daemons should send is "READY=1". + * + * STATUS=... Passes a status string back to systemd that + * describes the daemon state. This is free-from and + * can be used for various purposes: general state + * feedback, fsck-like programs could pass completion + * percentages and failing programs could pass a human + * readable error message. Example: "STATUS=Completed + * 66% of file system check..." + * + * ERRNO=... If a daemon fails, the errno-style error code, + * formatted as string. Example: "ERRNO=2" for ENOENT. + * + * BUSERROR=... If a daemon fails, the D-Bus error-style error + * code. Example: "BUSERROR=org.freedesktop.DBus.Error.TimedOut" + * + * MAINPID=... The main pid of a daemon, in case systemd did not + * fork off the process itself. Example: "MAINPID=4711" + * + * See sd_notifyf() for more complete examples. + */ +int sd_notify(int unset_environment, const char *state); + +/* Similar to sd_send_state() but takes a format string. + * + * Example 1: A daemon could send the following after initialization: + * + * sd_notifyf(0, "READY=1\n" + * "STATUS=Processing requests...\n" + * "MAINPID=%lu", + * (unsigned long) getpid()); + * + * Example 2: A daemon could send the following shortly before + * exiting, on failure: + * + * sd_notifyf(0, "STATUS=Failed to start up: %s\n" + * "ERRNO=%i", + * strerror(errno), + * errno); + */ +int sd_notifyf(int unset_environment, const char *format, ...); + +#ifdef __cplusplus +} +#endif + #endif diff --git a/src/service.c b/src/service.c index 547a55567a..6e35a4da58 100644 --- a/src/service.c +++ b/src/service.c @@ -149,6 +149,9 @@ static void service_done(Unit *u) { free(s->sysv_runlevels); s->sysv_runlevels = NULL; + free(s->status_text); + s->status_text = NULL; + exec_context_done(&s->exec_context); exec_command_free_array(s->exec_command, _SERVICE_EXEC_COMMAND_MAX); s->control_command = NULL; @@ -907,6 +910,10 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { fprintf(f, "%sSysVRunLevels: %s\n", prefix, s->sysv_runlevels); + if (s->status_text) + fprintf(f, "%sStatus Text: %s\n", + prefix, s->status_text); + free(p2); } @@ -1120,7 +1127,9 @@ static int service_coldplug(Unit *u) { if ((s->deserialized_state == SERVICE_START && (s->type == SERVICE_FORKING || - s->type == SERVICE_DBUS)) || + s->type == SERVICE_DBUS || + s->type == SERVICE_FINISH || + s->type == SERVICE_NOTIFY)) || s->deserialized_state == SERVICE_START_POST || s->deserialized_state == SERVICE_RUNNING || s->deserialized_state == SERVICE_RELOAD || @@ -1541,7 +1550,7 @@ static void service_enter_start(Service *s) { if ((r = service_spawn(s, s->exec_command[SERVICE_EXEC_START], - s->type == SERVICE_FORKING || s->type == SERVICE_DBUS, + s->type == SERVICE_FORKING || s->type == SERVICE_DBUS || s->type == SERVICE_NOTIFY, true, true, true, @@ -1569,13 +1578,15 @@ static void service_enter_start(Service *s) { service_set_state(s, SERVICE_START); } else if (s->type == SERVICE_FINISH || - s->type == SERVICE_DBUS) { + s->type == SERVICE_DBUS || + s->type == SERVICE_NOTIFY) { /* For finishing services we wait until the start * process exited, too, but it is our main process. */ /* For D-Bus services we know the main pid right away, - * but wait for the bus name to appear on the bus. */ + * but wait for the bus name to appear on the + * bus. Notify services are similar. */ s->main_pid = pid; s->main_pid_known = true; @@ -1946,7 +1957,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { exec_status_fill(&s->main_exec_status, pid, code, status); s->main_pid = 0; - if (s->type == SERVICE_SIMPLE || s->type == SERVICE_FINISH) { + if (s->type != SERVICE_FORKING) { assert(s->exec_command[SERVICE_EXEC_START]); s->exec_command[SERVICE_EXEC_START]->exec_status = s->main_exec_status; } @@ -1974,7 +1985,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { service_enter_signal(s, SERVICE_FINAL_SIGTERM, false); break; } else { - assert(s->type == SERVICE_DBUS); + assert(s->type == SERVICE_DBUS || s->type == SERVICE_NOTIFY); /* Fall through */ } @@ -2101,8 +2112,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { assert_not_reached("Uh, control process died at wrong time."); } } - } else - assert_not_reached("Got SIGCHLD for unkown PID"); + } } static void service_timer_event(Unit *u, uint64_t elapsed, Watch* w) { @@ -2195,6 +2205,57 @@ static void service_cgroup_notify_event(Unit *u) { } } +static void service_notify_message(Unit *u, char **tags) { + Service *s = SERVICE(u); + const char *e; + + assert(u); + + log_debug("%s: Got message", u->meta.id); + + /* Interpret MAINPID= */ + if ((e = strv_find_prefix(tags, "MAINPID=")) && + (s->state == SERVICE_START || + s->state == SERVICE_START_POST || + s->state == SERVICE_RUNNING || + s->state == SERVICE_RELOAD)) { + unsigned long pid; + + if (safe_atolu(e + 8, &pid) < 0 || + (unsigned long) (pid_t) pid != pid || + pid <= 1) + log_warning("Failed to parse %s", e); + else { + log_debug("%s: got %s", u->meta.id, e); + s->main_pid = (pid_t) pid; + } + } + + /* Interpret READY= */ + if (s->type == SERVICE_NOTIFY && + s->state == SERVICE_START && + strv_find(tags, "READY=1")) { + log_debug("%s: got READY=1", u->meta.id); + + service_enter_start_post(s); + } + + /* Interpret STATUS= */ + if ((e = strv_find_prefix(tags, "STATUS="))) { + char *t; + + if (!(t = strdup(e+7))) { + log_error("Failed to allocate string."); + return; + } + + log_debug("%s: got %s", u->meta.id, e); + + free(s->status_text); + s->status_text = t; + } +} + static int service_enumerate(Manager *m) { char **p; unsigned i; @@ -2456,7 +2517,8 @@ static const char* const service_type_table[_SERVICE_TYPE_MAX] = { [SERVICE_FORKING] = "forking", [SERVICE_SIMPLE] = "simple", [SERVICE_FINISH] = "finish", - [SERVICE_DBUS] = "dbus" + [SERVICE_DBUS] = "dbus", + [SERVICE_NOTIFY] = "notify" }; DEFINE_STRING_TABLE_LOOKUP(service_type, ServiceType); @@ -2502,6 +2564,7 @@ const UnitVTable service_vtable = { .timer_event = service_timer_event, .cgroup_notify_empty = service_cgroup_notify_event, + .notify_message = service_notify_message, .bus_name_owner_change = service_bus_name_owner_change, .bus_query_pid_done = service_bus_query_pid_done, diff --git a/src/service.h b/src/service.h index 5242de58fa..d644e7207a 100644 --- a/src/service.h +++ b/src/service.h @@ -60,6 +60,7 @@ typedef enum ServiceType { SERVICE_FORKING, /* forks by itself (i.e. traditional daemons) */ SERVICE_FINISH, /* we fork and wait until the program finishes (i.e. programs like fsck which run and need to finish before we continue) */ SERVICE_DBUS, /* we fork and wait until a specific D-Bus name appears on the bus */ + SERVICE_NOTIFY, /* we fork and wait until a daemon sends us a ready message with sd_notify() */ _SERVICE_TYPE_MAX, _SERVICE_TYPE_INVALID = -1 } ServiceType; @@ -121,6 +122,8 @@ struct Service { char *bus_name; + char *status_text; + RateLimit ratelimit; int socket_fd; diff --git a/src/socket.c b/src/socket.c index 19f1d22097..66131f867d 100644 --- a/src/socket.c +++ b/src/socket.c @@ -1228,12 +1228,14 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { assert(s); assert(pid >= 0); - success = is_clean_exit(code, status); - s->failure = s->failure || !success; + if (pid != s->control_pid) + return; - assert(s->control_pid == pid); s->control_pid = 0; + success = is_clean_exit(code, status); + s->failure = s->failure || !success; + if (s->control_command) exec_status_fill(&s->control_command->exec_status, pid, code, status); diff --git a/src/test-daemon.c b/src/test-daemon.c new file mode 100644 index 0000000000..8911b68620 --- /dev/null +++ b/src/test-daemon.c @@ -0,0 +1,37 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include + +#include "sd-daemon.h" + +int main(int argc, char*argv[]) { + + sd_notify(0, "STATUS=Starting up"); + sleep(5); + sd_notify(0, + "STATUS=Running\n" + "READY=1"); + sleep(10); + sd_notify(0, "STATUS=Quitting"); + + return 0; +} diff --git a/src/unit.h b/src/unit.h index 5e61f7c027..1f8874f4cc 100644 --- a/src/unit.h +++ b/src/unit.h @@ -285,6 +285,9 @@ struct UnitVTable { * ran empty */ void (*cgroup_notify_empty)(Unit *u); + /* Called whenever a process of this unit sends us a message */ + void (*notify_message)(Unit *u, char **tags); + /* Called whenever a name thus Unit registered for comes or * goes away. */ void (*bus_name_owner_change)(Unit *u, const char *name, const char *old_owner, const char *new_owner); -- cgit v1.2.3-54-g00ecf From 4a2a8b5a82325494f5daf4c66c23fdb4f906c9e6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 17 Jun 2010 22:50:06 +0200 Subject: notify: add systemd-notify command line tool --- .gitignore | 1 + Makefile.am | 8 ++- src/notify.c | 179 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 src/notify.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 0ad1446777..a0a284f2df 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-notify test-daemon systemd-install org.freedesktop.systemd1.*.xml diff --git a/Makefile.am b/Makefile.am index 3e77a4737b..197407cdb0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -48,7 +48,8 @@ AM_CPPFLAGS = \ rootbin_PROGRAMS = \ systemd \ - systemctl + systemctl \ + systemd-notify bin_PROGRAMS = \ systemd-install @@ -373,6 +374,11 @@ systemctl_SOURCES = \ systemctl_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) systemctl_LDADD = $(DBUS_LIBS) +systemd_notify_SOURCES = \ + src/notify.c \ + src/sd-daemon.c \ + $(BASIC_SOURCES) + systemd_install_SOURCES = \ src/install.c \ src/path-lookup.c \ diff --git a/src/notify.c b/src/notify.c new file mode 100644 index 0000000000..864b7c278d --- /dev/null +++ b/src/notify.c @@ -0,0 +1,179 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "strv.h" +#include "util.h" +#include "log.h" +#include "sd-daemon.h" + +static bool arg_ready = false; +static pid_t arg_pid = 0; +static const char *arg_status = NULL; + +static int help(void) { + + printf("%s [options] [VARIABLE=VALUE...]\n\n" + "Notify the init system about service status updates.\n\n" + " -h --help Show this help\n" + " --ready Inform the init system about service start-up completion\n" + " --pid[=PID] Set main pid of daemon\n" + " --status=TEXT Set status text\n", + program_invocation_short_name); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_READY = 0x100, + ARG_PID, + ARG_STATUS + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "ready", no_argument, NULL, ARG_READY }, + { "pid", optional_argument, NULL, ARG_PID }, + { "status", required_argument, NULL, ARG_STATUS }, + { NULL, 0, NULL, 0 } + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_READY: + arg_ready = true; + break; + + case ARG_PID: + + if (optarg) { + if (parse_pid(optarg, &arg_pid) < 0) { + log_error("Failed to parse PID %s.", optarg); + return -EINVAL; + } + } else + arg_pid = getppid(); + + break; + + case ARG_STATUS: + arg_status = optarg; + break; + + case '?': + return -EINVAL; + + default: + log_error("Unknown option code %c", c); + return -EINVAL; + } + } + + return 1; +} + +int main(int argc, char* argv[]) { + char* our_env[4], **final_env = NULL; + unsigned i = 0; + char *status = NULL, *cpid = NULL, *n = NULL; + int r, retval = 1; + + log_parse_environment(); + + if ((r = parse_argv(argc, argv)) <= 0) { + retval = r < 0; + goto finish; + } + + if (arg_ready) + our_env[i++] = (char*) "READY=1"; + + if (arg_status) { + if (!(status = strappend("STATUS=", arg_status))) { + log_error("Failed to allocate STATUS string."); + goto finish; + } + + our_env[i++] = status; + } + + if (arg_pid > 0) { + if (asprintf(&cpid, "MAINPID=%lu", (unsigned long) arg_pid) < 0) { + log_error("Failed to allocate MAINPID string."); + goto finish; + } + + our_env[i++] = cpid; + } + + our_env[i++] = NULL; + + if (!(final_env = strv_env_merge(2, our_env, argv + optind))) { + log_error("Failed to merge string sets."); + goto finish; + } + + if (strv_length(final_env) <= 0) { + retval = 0; + goto finish; + } + + if (!(n = strv_join(final_env, "\n"))) { + log_error("Failed to concatenate strings."); + goto finish; + } + + if ((r = sd_notify(false, n)) < 0) { + log_error("Failed to notify init system: %s", strerror(-r)); + goto finish; + } + + retval = r <= 0; + +finish: + free(status); + free(cpid); + free(n); + + strv_free(final_env); + + return retval; +} -- cgit v1.2.3-54-g00ecf From 514f4ef52f91edb3741cad88d34572d162459346 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 18 Jun 2010 04:22:59 +0200 Subject: systemctl: add verbs for special units --- .gitignore | 6 ++ Makefile.am | 3 +- man/systemd.special.xml.in | 42 --------- src/initctl.c | 12 +-- src/main.c | 11 ++- src/manager.c | 1 + src/manager.h | 51 ----------- src/mount.c | 1 + src/service.c | 7 +- src/special.h | 73 +++++++++++++++ src/swap.c | 1 + src/systemctl.c | 221 ++++++++++++++++++++++++++++++++------------- src/target.c | 7 +- src/unit.c | 1 + 14 files changed, 265 insertions(+), 172 deletions(-) create mode 100644 src/special.h (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index a0a284f2df..2316dd5093 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,9 @@ systemadm *~ *.tar.gz *.o +*.lo +*.a +*.la .deps/ Makefile.in aclocal.m4 @@ -37,3 +40,6 @@ missing stamp-* *.stamp Makefile +ltmain.sh +*.tar.bz2 +libtool diff --git a/Makefile.am b/Makefile.am index 3dc4b0f28e..0e7c489ca0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -287,7 +287,8 @@ EXTRA_DIST += \ src/securebits.h \ src/linux/auto_dev-ioctl.h \ src/initreq.h \ - src/sd-daemon.h + src/sd-daemon.h \ + src/special.h dist_man_MANS = \ man/systemd.unit.5 \ diff --git a/man/systemd.special.xml.in b/man/systemd.special.xml.in index a2e1bb2cbd..09707aa081 100644 --- a/man/systemd.special.xml.in +++ b/man/systemd.special.xml.in @@ -69,13 +69,10 @@ rescue.target, rpcbind.target, rtc-set.target, - runlevel0.target, - runlevel1.target, runlevel2.target, runlevel3.target, runlevel4.target, runlevel5.target, - runlevel6.target shutdown.target, sigpwr.target, sockets.target, @@ -414,32 +411,6 @@ facility. - - runlevel0.target - - This is a target that is - called whever the SysV - compatibility code asks for - runlevel 0. This is an alias - for - poweroff.target, - for compatibility with - SysV. - - - - runlevel1.target - - This is a target that is - called whever the SysV - compatibility code asks for - runlevel 1. This is an alias - for - rescue.target, - for compatibility with - SysV. - - runlevel2.target @@ -494,19 +465,6 @@ graphical.target. - - runlevel6.target - - This is a target that is - called whever the SysV - compatibility code asks for - runlevel 6. This is an alias - for - reboot.target, - for compatibility with - SysV. - - shutdown.target diff --git a/src/initctl.c b/src/initctl.c index 56ed5cdf30..fef45007ba 100644 --- a/src/initctl.c +++ b/src/initctl.c @@ -39,7 +39,7 @@ #include "log.h" #include "list.h" #include "initreq.h" -#include "manager.h" +#include "special.h" #include "sd-daemon.h" #define SERVER_FD_MAX 16 @@ -72,15 +72,15 @@ static const char *translate_runlevel(int runlevel) { const int runlevel; const char *special; } table[] = { - { '0', SPECIAL_RUNLEVEL0_TARGET }, - { '1', SPECIAL_RUNLEVEL1_TARGET }, - { 's', SPECIAL_RUNLEVEL1_TARGET }, - { 'S', SPECIAL_RUNLEVEL1_TARGET }, + { '0', SPECIAL_POWEROFF_TARGET }, + { '1', SPECIAL_RESCUE_TARGET }, + { 's', SPECIAL_RESCUE_TARGET }, + { 'S', SPECIAL_RESCUE_TARGET }, { '2', SPECIAL_RUNLEVEL2_TARGET }, { '3', SPECIAL_RUNLEVEL3_TARGET }, { '4', SPECIAL_RUNLEVEL4_TARGET }, { '5', SPECIAL_RUNLEVEL5_TARGET }, - { '6', SPECIAL_RUNLEVEL6_TARGET }, + { '6', SPECIAL_REBOOT_TARGET }, }; unsigned i; diff --git a/src/main.c b/src/main.c index ec2733e185..99f6bdd712 100644 --- a/src/main.c +++ b/src/main.c @@ -40,6 +40,7 @@ #include "kmod-setup.h" #include "load-fragment.h" #include "fdset.h" +#include "special.h" static enum { ACTION_RUN, @@ -222,11 +223,11 @@ static int set_default_unit(const char *u) { static int parse_proc_cmdline_word(const char *word) { static const char * const rlmap[] = { - "single", SPECIAL_RUNLEVEL1_TARGET, - "-s", SPECIAL_RUNLEVEL1_TARGET, - "s", SPECIAL_RUNLEVEL1_TARGET, - "S", SPECIAL_RUNLEVEL1_TARGET, - "1", SPECIAL_RUNLEVEL1_TARGET, + "single", SPECIAL_RESCUE_TARGET, + "-s", SPECIAL_RESCUE_TARGET, + "s", SPECIAL_RESCUE_TARGET, + "S", SPECIAL_RESCUE_TARGET, + "1", SPECIAL_RESCUE_TARGET, "2", SPECIAL_RUNLEVEL2_TARGET, "3", SPECIAL_RUNLEVEL3_TARGET, "4", SPECIAL_RUNLEVEL4_TARGET, diff --git a/src/manager.c b/src/manager.c index 2ad60721aa..c93b7912eb 100644 --- a/src/manager.c +++ b/src/manager.c @@ -53,6 +53,7 @@ #include "dbus-job.h" #include "missing.h" #include "path-lookup.h" +#include "special.h" /* As soon as 16 units are in our GC queue, make sure to run a gc sweep */ #define GC_QUEUE_ENTRIES_MAX 16 diff --git a/src/manager.h b/src/manager.h index 5ee75d5248..405f143566 100644 --- a/src/manager.h +++ b/src/manager.h @@ -85,57 +85,6 @@ struct Watch { #include "dbus.h" #include "path-lookup.h" -#define SPECIAL_DEFAULT_TARGET "default.target" - -/* This is not really intended to be started by directly. This is - * mostly so that other targets (reboot/halt/poweroff) can depend on - * it to bring all services down that want to be brought down on - * system shutdown. */ -#define SPECIAL_SHUTDOWN_TARGET "shutdown.target" - -#define SPECIAL_LOGGER_SOCKET "systemd-logger.socket" - -#define SPECIAL_KBREQUEST_TARGET "kbrequest.target" -#define SPECIAL_SIGPWR_TARGET "sigpwr.target" -#define SPECIAL_CTRL_ALT_DEL_TARGET "ctrl-alt-del.target" - -#define SPECIAL_LOCAL_FS_TARGET "local-fs.target" /* LSB's $local_fs */ -#define SPECIAL_REMOTE_FS_TARGET "remote-fs.target" /* LSB's $remote_fs */ -#define SPECIAL_SWAP_TARGET "swap.target" -#define SPECIAL_NETWORK_TARGET "network.target" /* LSB's $network */ -#define SPECIAL_NSS_LOOKUP_TARGET "nss-lookup.target" /* LSB's $named */ -#define SPECIAL_RPCBIND_TARGET "rpcbind.target" /* LSB's $portmap */ -#define SPECIAL_SYSLOG_TARGET "syslog.target" /* LSB's $syslog; Should pull in syslog.socket or syslog.service */ -#define SPECIAL_RTC_SET_TARGET "rtc-set.target" /* LSB's $time */ -#define SPECIAL_DISPLAY_MANAGER_SERVICE "display-manager.service" /* Debian's $x-display-manager */ -#define SPECIAL_MAIL_TRANSFER_AGENT_TARGET "mail-transfer-agent.target" /* Debian's $mail-{transport|transfer-agent */ -#define SPECIAL_BASIC_TARGET "basic.target" -#define SPECIAL_SYSINIT_TARGET "sysinit.target" -#define SPECIAL_RESCUE_TARGET "rescue.target" -#define SPECIAL_EXIT_SERVICE "exit.service" -#define SPECIAL_EMERGENCY_SERVICE "emergency.service" -#define SPECIAL_HALT_TARGET "halt.target" -#define SPECIAL_POWEROFF_TARGET "poweroff.target" -#define SPECIAL_REBOOT_TARGET "reboot.target" - -#ifndef SPECIAL_DBUS_SERVICE -#define SPECIAL_DBUS_SERVICE "dbus.service" -#endif - -#ifndef SPECIAL_SYSLOG_SERVICE -#define SPECIAL_SYSLOG_SERVICE "syslog.service" -#endif - -/* For SysV compatibility. Usually an alias for a saner target. On - * SysV-free systems this doesn't exist. */ -#define SPECIAL_RUNLEVEL0_TARGET "runlevel0.target" -#define SPECIAL_RUNLEVEL1_TARGET "runlevel1.target" -#define SPECIAL_RUNLEVEL2_TARGET "runlevel2.target" -#define SPECIAL_RUNLEVEL3_TARGET "runlevel3.target" -#define SPECIAL_RUNLEVEL4_TARGET "runlevel4.target" -#define SPECIAL_RUNLEVEL5_TARGET "runlevel5.target" -#define SPECIAL_RUNLEVEL6_TARGET "runlevel6.target" - struct Manager { uint32_t current_job_id; diff --git a/src/mount.c b/src/mount.c index ea81f5fee6..94f19294fd 100644 --- a/src/mount.c +++ b/src/mount.c @@ -35,6 +35,7 @@ #include "unit-name.h" #include "mount.h" #include "dbus-mount.h" +#include "special.h" static const UnitActiveState state_translation_table[_MOUNT_STATE_MAX] = { [MOUNT_DEAD] = UNIT_INACTIVE, diff --git a/src/service.c b/src/service.c index fe91b9241e..a767c34f58 100644 --- a/src/service.c +++ b/src/service.c @@ -32,6 +32,7 @@ #include "strv.h" #include "unit-name.h" #include "dbus-service.h" +#include "special.h" #define COMMENTS "#;\n" #define NEWLINES "\n\r" @@ -49,13 +50,13 @@ static const struct { const RunlevelType type; } rcnd_table[] = { /* Standard SysV runlevels */ - { "rc0.d", SPECIAL_RUNLEVEL0_TARGET, RUNLEVEL_DOWN }, - { "rc1.d", SPECIAL_RUNLEVEL1_TARGET, RUNLEVEL_UP }, + { "rc0.d", SPECIAL_POWEROFF_TARGET, RUNLEVEL_DOWN }, + { "rc1.d", SPECIAL_RESCUE_TARGET, RUNLEVEL_UP }, { "rc2.d", SPECIAL_RUNLEVEL2_TARGET, RUNLEVEL_UP }, { "rc3.d", SPECIAL_RUNLEVEL3_TARGET, RUNLEVEL_UP }, { "rc4.d", SPECIAL_RUNLEVEL4_TARGET, RUNLEVEL_UP }, { "rc5.d", SPECIAL_RUNLEVEL5_TARGET, RUNLEVEL_UP }, - { "rc6.d", SPECIAL_RUNLEVEL6_TARGET, RUNLEVEL_DOWN }, + { "rc6.d", SPECIAL_REBOOT_TARGET, RUNLEVEL_DOWN }, /* SUSE style boot.d */ { "boot.d", SPECIAL_SYSINIT_TARGET, RUNLEVEL_SYSINIT }, diff --git a/src/special.h b/src/special.h new file mode 100644 index 0000000000..2d5bc7b89d --- /dev/null +++ b/src/special.h @@ -0,0 +1,73 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#ifndef foospecialhfoo +#define foospecialhfoo + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#define SPECIAL_DEFAULT_TARGET "default.target" + +/* This is not really intended to be started by directly. This is + * mostly so that other targets (reboot/halt/poweroff) can depend on + * it to bring all services down that want to be brought down on + * system shutdown. */ +#define SPECIAL_SHUTDOWN_TARGET "shutdown.target" + +#define SPECIAL_LOGGER_SOCKET "systemd-logger.socket" + +#define SPECIAL_KBREQUEST_TARGET "kbrequest.target" +#define SPECIAL_SIGPWR_TARGET "sigpwr.target" +#define SPECIAL_CTRL_ALT_DEL_TARGET "ctrl-alt-del.target" + +#define SPECIAL_LOCAL_FS_TARGET "local-fs.target" /* LSB's $local_fs */ +#define SPECIAL_REMOTE_FS_TARGET "remote-fs.target" /* LSB's $remote_fs */ +#define SPECIAL_SWAP_TARGET "swap.target" +#define SPECIAL_NETWORK_TARGET "network.target" /* LSB's $network */ +#define SPECIAL_NSS_LOOKUP_TARGET "nss-lookup.target" /* LSB's $named */ +#define SPECIAL_RPCBIND_TARGET "rpcbind.target" /* LSB's $portmap */ +#define SPECIAL_SYSLOG_TARGET "syslog.target" /* LSB's $syslog; Should pull in syslog.socket or syslog.service */ +#define SPECIAL_RTC_SET_TARGET "rtc-set.target" /* LSB's $time */ +#define SPECIAL_DISPLAY_MANAGER_SERVICE "display-manager.service" /* Debian's $x-display-manager */ +#define SPECIAL_MAIL_TRANSFER_AGENT_TARGET "mail-transfer-agent.target" /* Debian's $mail-{transport|transfer-agent */ +#define SPECIAL_BASIC_TARGET "basic.target" +#define SPECIAL_SYSINIT_TARGET "sysinit.target" +#define SPECIAL_RESCUE_TARGET "rescue.target" +#define SPECIAL_EXIT_SERVICE "exit.service" +#define SPECIAL_EMERGENCY_SERVICE "emergency.service" +#define SPECIAL_HALT_TARGET "halt.target" +#define SPECIAL_POWEROFF_TARGET "poweroff.target" +#define SPECIAL_REBOOT_TARGET "reboot.target" + +#ifndef SPECIAL_DBUS_SERVICE +#define SPECIAL_DBUS_SERVICE "dbus.service" +#endif + +#ifndef SPECIAL_SYSLOG_SERVICE +#define SPECIAL_SYSLOG_SERVICE "syslog.service" +#endif + +/* For SysV compatibility. Usually an alias for a saner target. On + * SysV-free systems this doesn't exist. */ +#define SPECIAL_RUNLEVEL2_TARGET "runlevel2.target" +#define SPECIAL_RUNLEVEL3_TARGET "runlevel3.target" +#define SPECIAL_RUNLEVEL4_TARGET "runlevel4.target" +#define SPECIAL_RUNLEVEL5_TARGET "runlevel5.target" + +#endif diff --git a/src/swap.c b/src/swap.c index 6c0322f3e5..5b3073bd4a 100644 --- a/src/swap.c +++ b/src/swap.c @@ -33,6 +33,7 @@ #include "load-dropin.h" #include "unit-name.h" #include "dbus-swap.h" +#include "special.h" static const UnitActiveState state_translation_table[_SWAP_STATE_MAX] = { [SWAP_DEAD] = UNIT_INACTIVE, diff --git a/src/systemctl.c b/src/systemctl.c index aad9ba039b..a41d7ea8bd 100644 --- a/src/systemctl.c +++ b/src/systemctl.c @@ -36,6 +36,7 @@ #include "macro.h" #include "set.h" #include "utmp-wtmp.h" +#include "special.h" static const char *arg_type = NULL; static bool arg_all = false; @@ -45,6 +46,7 @@ static bool arg_block = false; static bool arg_immediate = false; static bool arg_no_wtmp = false; static bool arg_no_sync = false; +static bool arg_no_wall = false; static bool arg_dry = false; static char **arg_wall = NULL; enum action { @@ -58,6 +60,8 @@ enum action { ACTION_RUNLEVEL4, ACTION_RUNLEVEL5, ACTION_RESCUE, + ACTION_EMERGENCY, + ACTION_DEFAULT, ACTION_RELOAD, ACTION_REEXEC, ACTION_RUNLEVEL, @@ -66,6 +70,8 @@ enum action { static bool error_is_no_service(DBusError *error) { + assert(error); + if (!dbus_error_is_set(error)) return false; @@ -80,6 +86,9 @@ static bool error_is_no_service(DBusError *error) { static int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *data, bool next) { + assert(iter); + assert(data); + if (dbus_message_iter_get_arg_type(iter) != type) return -EIO; @@ -116,18 +125,22 @@ static int columns(void) { } -static void warn_wall(void) { +static void warn_wall(enum action action) { static const char *table[_ACTION_MAX] = { - [ACTION_HALT] = "The system is going down for system halt NOW!", - [ACTION_REBOOT] = "The system is going down for reboot NOW!", - [ACTION_POWEROFF] = "The system is going down for power-off NOW!", - [ACTION_RESCUE] = "The system is going down to rescue mode NOW!" + [ACTION_HALT] = "The system is going down for system halt NOW!", + [ACTION_REBOOT] = "The system is going down for reboot NOW!", + [ACTION_POWEROFF] = "The system is going down for power-off NOW!", + [ACTION_RESCUE] = "The system is going down to rescue mode NOW!", + [ACTION_EMERGENCY] = "The system is going down to emergency mode NOW!" }; - if (!table[arg_action]) + if (arg_no_wall) + return; + + if (!table[action]) return; - utmp_wall(table[arg_action]); + utmp_wall(table[action]); } static int list_units(DBusConnection *bus, char **args, unsigned n) { @@ -139,6 +152,8 @@ static int list_units(DBusConnection *bus, char **args, unsigned n) { dbus_error_init(&error); + assert(bus); + if (!(m = dbus_message_new_method_call( "org.freedesktop.systemd1", "/org/freedesktop/systemd1", @@ -247,6 +262,8 @@ static int list_jobs(DBusConnection *bus, char **args, unsigned n) { dbus_error_init(&error); + assert(bus); + if (!(m = dbus_message_new_method_call( "org.freedesktop.systemd1", "/org/freedesktop/systemd1", @@ -326,6 +343,9 @@ static int load_unit(DBusConnection *bus, char **args, unsigned n) { dbus_error_init(&error); + assert(bus); + assert(args); + for (i = 1; i < n; i++) { if (!(m = dbus_message_new_method_call( @@ -380,6 +400,9 @@ static int cancel_job(DBusConnection *bus, char **args, unsigned n) { dbus_error_init(&error); + assert(bus); + assert(args); + for (i = 1; i < n; i++) { unsigned id; const char *path; @@ -669,72 +692,100 @@ finish: return r; } +static enum action verb_to_action(const char *verb) { + if (streq(verb, "halt")) + return ACTION_HALT; + else if (streq(verb, "poweroff")) + return ACTION_POWEROFF; + else if (streq(verb, "reboot")) + return ACTION_REBOOT; + else if (streq(verb, "rescue")) + return ACTION_RESCUE; + else if (streq(verb, "emergency")) + return ACTION_EMERGENCY; + else if (streq(verb, "default")) + return ACTION_DEFAULT; + else + return ACTION_INVALID; +} + static int start_unit(DBusConnection *bus, char **args, unsigned n) { static const char * const table[_ACTION_MAX] = { - [ACTION_HALT] = "halt.target", - [ACTION_POWEROFF] = "poweroff.target", - [ACTION_REBOOT] = "reboot.target", - [ACTION_RUNLEVEL2] = "runlevel2.target", - [ACTION_RUNLEVEL3] = "runlevel3.target", - [ACTION_RUNLEVEL4] = "runlevel4.target", - [ACTION_RUNLEVEL5] = "runlevel5.target", - [ACTION_RESCUE] = "rescue.target" + [ACTION_HALT] = SPECIAL_HALT_TARGET, + [ACTION_POWEROFF] = SPECIAL_POWEROFF_TARGET, + [ACTION_REBOOT] = SPECIAL_REBOOT_TARGET, + [ACTION_RUNLEVEL2] = SPECIAL_RUNLEVEL2_TARGET, + [ACTION_RUNLEVEL3] = SPECIAL_RUNLEVEL3_TARGET, + [ACTION_RUNLEVEL4] = SPECIAL_RUNLEVEL4_TARGET, + [ACTION_RUNLEVEL5] = SPECIAL_RUNLEVEL5_TARGET, + [ACTION_RESCUE] = SPECIAL_RESCUE_TARGET, + [ACTION_EMERGENCY] = SPECIAL_EMERGENCY_SERVICE, + [ACTION_DEFAULT] = SPECIAL_DEFAULT_TARGET }; int r; unsigned i; - const char *method, *mode; + const char *method, *mode, *one_name; Set *s = NULL; + assert(bus); + if (arg_action == ACTION_SYSTEMCTL) { method = - streq(args[0], "start") ? "StartUnit" : streq(args[0], "stop") ? "StopUnit" : streq(args[0], "reload") ? "ReloadUnit" : streq(args[0], "restart") ? "RestartUnit" : - /* isolate */ "StartUnit"; + "StartUnit"; mode = - streq(args[0], "isolate") ? "isolate" : - arg_replace ? "replace" : - "fail"; + (streq(args[0], "isolate") || + streq(args[0], "rescue") || + streq(args[0], "emergency")) ? "isolate" : + arg_replace ? "replace" : + "fail"; - if (arg_block) { - if ((r = enable_wait_for_jobs(bus)) < 0) { - log_error("Could not watch jobs: %s", strerror(-r)); - goto finish; - } + one_name = table[verb_to_action(args[0])]; - if (!(s = set_new(string_hash_func, string_compare_func))) { - log_error("Failed to allocate set."); - r = -ENOMEM; - goto finish; - } - } } else { assert(arg_action < ELEMENTSOF(table)); assert(table[arg_action]); method = "StartUnit"; - mode = arg_action == ACTION_RESCUE ? "isolate" : "replace"; + + mode = (arg_action == ACTION_EMERGENCY || + arg_action == ACTION_RESCUE) ? "isolate" : "replace"; + + one_name = table[arg_action]; + } + + if (arg_block) { + if ((r = enable_wait_for_jobs(bus)) < 0) { + log_error("Could not watch jobs: %s", strerror(-r)); + goto finish; + } + + if (!(s = set_new(string_hash_func, string_compare_func))) { + log_error("Failed to allocate set."); + r = -ENOMEM; + goto finish; + } } r = 0; - if (arg_action == ACTION_SYSTEMCTL) { + if (one_name) { + if ((r = start_unit_one(bus, method, one_name, mode, s)) <= 0) + goto finish; + } else { for (i = 1; i < n; i++) if ((r = start_unit_one(bus, method, args[i], mode, s)) < 0) goto finish; - - if (arg_block) - r = wait_for_jobs(bus, s); - - } else { - if ((r = start_unit_one(bus, method, table[arg_action], mode, s)) <= 0) - goto finish; } + if (arg_block) + r = wait_for_jobs(bus, s); + finish: if (s) set_free_free(s); @@ -742,6 +793,15 @@ finish: return r; } +static int start_special(DBusConnection *bus, char **args, unsigned n) { + assert(bus); + assert(args); + + warn_wall(verb_to_action(args[0])); + + return start_unit(bus, args, n); +} + static DBusHandlerResult monitor_filter(DBusConnection *connection, DBusMessage *message, void *data) { DBusError error; DBusMessage *m = NULL, *reply = NULL; @@ -1322,14 +1382,15 @@ finish: static int systemctl_help(void) { printf("%s [options]\n\n" - "Send control commands to the init system.\n\n" + "Send control commands to the init daemon.\n\n" " -h --help Show this help\n" " -t --type=TYPE List only units of a particular type\n" " -a --all Show all units, including dead ones\n" " --replace When installing a new job, replace existing conflicting ones\n" " --system Connect to system bus\n" " --session Connect to session bus\n" - " --block Wait until operation finished\n\n" + " --block Wait until operation finished\n" + " --no-wall Don't send wall message before reboot/halt/power-off\n\n" "Commands:\n" " list-units List units\n" " list-jobs List jobs\n" @@ -1344,12 +1405,18 @@ static int systemctl_help(void) { " monitor Monitor unit/job changes\n" " dump Dump server status\n" " snapshot [NAME] Create a snapshot\n" - " daemon-reload Reload daemon configuration\n" - " daemon-reexecute Reexecute daemon\n" - " daemon-exit Ask the daemon to quit\n" + " daemon-reload Reload init daemon configuration\n" + " daemon-reexecute Reexecute init daemon\n" + " daemon-exit Ask the init daemon to quit\n" " show-environment Dump environment\n" " set-environment [NAME=VALUE...] Set one or more environment variables\n" - " unset-environment [NAME...] Unset one or more environment variables\n", + " unset-environment [NAME...] Unset one or more environment variables\n" + " halt Shut down and halt the system\n" + " reboot Shut down and reboot the system\n" + " poweroff Shut down and power off the system\n" + " default Enter default mode\n" + " rescue Enter rescue mode\n" + " emergency Enter emergency mode\n", program_invocation_short_name); return 0; @@ -1366,7 +1433,8 @@ static int halt_help(void) { " -f --force Force immediate reboot/halt/power-off\n" " -w --wtmp-only Don't reboot/halt/power-off, just write wtmp record\n" " -d --no-wtmp Don't write wtmp record\n" - " -n --no-sync Don't sync before reboot/halt/power-off\n", + " -n --no-sync Don't sync before reboot/halt/power-off\n" + " --no-wall Don't send wall message before reboot/halt/power-off\n", program_invocation_short_name, arg_action == ACTION_REBOOT ? "Reboot" : arg_action == ACTION_POWEROFF ? "Power off" : @@ -1377,14 +1445,15 @@ static int halt_help(void) { static int shutdown_help(void) { - printf("%s [options] [TIME] [WALL...]\n\n" + printf("%s [options] [IGNORED] [WALL...]\n\n" "Shut down the system.\n\n" " --help Show this help\n" " -H --halt Halt the machine\n" " -P --poweroff Power-off the machine\n" " -r --reboot Reboot the machine\n" " -h Equivalent to --poweroff, overriden by --halt\n" - " -k Don't reboot/halt/power-off, just send warnings\n", + " -k Don't reboot/halt/power-off, just send warnings\n" + " --no-wall Don't send wall message before reboot/halt/power-off\n", program_invocation_short_name); return 0; @@ -1393,15 +1462,16 @@ static int shutdown_help(void) { static int telinit_help(void) { printf("%s [options]\n\n" - "Send control commands to the init system.\n\n" - " --help Show this help\n\n" + "Send control commands to the init daemon.\n\n" + " --help Show this help\n" + " --no-wall Don't send wall message before reboot/halt/power-off\n\n" "Commands:\n" " 0 Power-off the machine\n" " 6 Reboot the machine\n" - " 1, 2, 3, 4, 5 Start runlevelX.target unit\n" - " s, S Start the rescue.target unit\n" - " q, Q Ask systemd to reload its configuration\n" - " u, U Ask systemd to reexecute itself\n", + " 2, 3, 4, 5 Start runlevelX.target unit\n" + " 1, s, S Enter rescue mode\n" + " q, Q Reload init daemon configuration\n" + " u, U Reexecute init daemon\n", program_invocation_short_name); return 0; @@ -1423,7 +1493,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) { ARG_REPLACE = 0x100, ARG_SESSION, ARG_SYSTEM, - ARG_BLOCK + ARG_BLOCK, + ARG_NO_WALL }; static const struct option options[] = { @@ -1434,6 +1505,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "session", no_argument, NULL, ARG_SESSION }, { "system", no_argument, NULL, ARG_SYSTEM }, { "block", no_argument, NULL, ARG_BLOCK }, + { "no-wall", no_argument, NULL, ARG_NO_WALL }, { NULL, 0, NULL, 0 } }; @@ -1474,6 +1546,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_block = true; break; + case ARG_NO_WALL: + arg_no_wall = true; + break; + case '?': return -EINVAL; @@ -1491,7 +1567,8 @@ static int halt_parse_argv(int argc, char *argv[]) { enum { ARG_HELP = 0x100, ARG_HALT, - ARG_REBOOT + ARG_REBOOT, + ARG_NO_WALL }; static const struct option options[] = { @@ -1503,6 +1580,7 @@ static int halt_parse_argv(int argc, char *argv[]) { { "wtmp-only", no_argument, NULL, 'w' }, { "no-wtmp", no_argument, NULL, 'd' }, { "no-sync", no_argument, NULL, 'n' }, + { "no-wall", no_argument, NULL, ARG_NO_WALL }, { NULL, 0, NULL, 0 } }; @@ -1550,6 +1628,10 @@ static int halt_parse_argv(int argc, char *argv[]) { arg_no_sync = true; break; + case ARG_NO_WALL: + arg_no_wall = true; + break; + case 'i': case 'h': /* Compatibility nops */ @@ -1576,6 +1658,7 @@ static int shutdown_parse_argv(int argc, char *argv[]) { enum { ARG_HELP = 0x100, + ARG_NO_WALL }; static const struct option options[] = { @@ -1583,6 +1666,7 @@ static int shutdown_parse_argv(int argc, char *argv[]) { { "halt", no_argument, NULL, 'H' }, { "poweroff", no_argument, NULL, 'P' }, { "reboot", no_argument, NULL, 'r' }, + { "no-wall", no_argument, NULL, ARG_NO_WALL }, { NULL, 0, NULL, 0 } }; @@ -1619,6 +1703,10 @@ static int shutdown_parse_argv(int argc, char *argv[]) { arg_dry = true; break; + case ARG_NO_WALL: + arg_no_wall = true; + break; + case 't': case 'a': /* Compatibility nops */ @@ -1647,10 +1735,12 @@ static int telinit_parse_argv(int argc, char *argv[]) { enum { ARG_HELP = 0x100, + ARG_NO_WALL }; static const struct option options[] = { { "help", no_argument, NULL, ARG_HELP }, + { "no-wall", no_argument, NULL, ARG_NO_WALL }, { NULL, 0, NULL, 0 } }; @@ -1686,6 +1776,10 @@ static int telinit_parse_argv(int argc, char *argv[]) { telinit_help(); return 0; + case ARG_NO_WALL: + arg_no_wall = true; + break; + case '?': return -EINVAL; @@ -1838,6 +1932,12 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[]) { { "show-environment", EQUAL, 1, show_enviroment }, { "set-environment", MORE, 2, set_environment }, { "unset-environment", MORE, 2, set_environment }, + { "halt", EQUAL, 1, start_special }, + { "poweroff", EQUAL, 1, start_special }, + { "reboot", EQUAL, 1, start_special }, + { "default", EQUAL, 1, start_special }, + { "rescue", EQUAL, 1, start_special }, + { "emergency", EQUAL, 1, start_special }, }; int left; @@ -1919,7 +2019,7 @@ static int reload_with_fallback(DBusConnection *bus) { static int start_with_fallback(DBusConnection *bus) { int r; - warn_wall(); + warn_wall(arg_action); if (bus) { /* First, try systemd via D-Bus. */ @@ -1943,8 +2043,6 @@ static int halt_main(DBusConnection *bus) { if (!arg_immediate) return start_with_fallback(bus); - warn_wall(); - if (!arg_no_wtmp) if ((r = utmp_put_shutdown(0)) < 0) log_warning("Failed to write utmp record: %s", strerror(-r)); @@ -2049,6 +2147,7 @@ int main(int argc, char*argv[]) { case ACTION_RUNLEVEL4: case ACTION_RUNLEVEL5: case ACTION_RESCUE: + case ACTION_EMERGENCY: retval = start_with_fallback(bus) < 0; break; diff --git a/src/target.c b/src/target.c index 75f8ef8948..d3f3748cd5 100644 --- a/src/target.c +++ b/src/target.c @@ -27,6 +27,7 @@ #include "load-fragment.h" #include "log.h" #include "dbus-target.h" +#include "special.h" static const UnitActiveState state_translation_table[_TARGET_STATE_MAX] = { [TARGET_DEAD] = UNIT_INACTIVE, @@ -147,9 +148,9 @@ int target_get_runlevel(Target *t) { { SPECIAL_RUNLEVEL4_TARGET, '4' }, { SPECIAL_RUNLEVEL3_TARGET, '3' }, { SPECIAL_RUNLEVEL2_TARGET, '2' }, - { SPECIAL_RUNLEVEL1_TARGET, '1' }, - { SPECIAL_RUNLEVEL0_TARGET, '0' }, - { SPECIAL_RUNLEVEL6_TARGET, '6' }, + { SPECIAL_RESCUE_TARGET, '1' }, + { SPECIAL_POWEROFF_TARGET, '0' }, + { SPECIAL_REBOOT_TARGET, '6' }, }; unsigned i; diff --git a/src/unit.c b/src/unit.c index 229f44c1a9..327444bba4 100644 --- a/src/unit.c +++ b/src/unit.c @@ -38,6 +38,7 @@ #include "unit-name.h" #include "specifier.h" #include "dbus-unit.h" +#include "special.h" const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = { [UNIT_SERVICE] = &service_vtable, -- cgit v1.2.3-54-g00ecf From 8c6db8336536916d0476ff8233e0abf40a2f6aab Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 21 Jun 2010 23:27:18 +0200 Subject: pam: implement systemd PAM module and generelize cgroup API for that a bit --- .gitignore | 3 + Makefile.am | 65 ++++- configure.ac | 9 +- fixme | 20 +- src/cgroup-util.c | 694 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/cgroup-util.h | 54 +++++ src/cgroup.c | 318 ++++--------------------- src/cgroup.h | 9 +- src/manager.c | 4 +- src/pam-module.c | 466 ++++++++++++++++++++++++++++++++++++ src/test-cgroup.c | 84 +++++++ src/util.c | 169 ++++++++++++- src/util.h | 8 +- src/utmp-wtmp.c | 6 +- systemd.pc.in | 11 + 15 files changed, 1609 insertions(+), 311 deletions(-) create mode 100644 src/cgroup-util.c create mode 100644 src/cgroup-util.h create mode 100644 src/pam-module.c create mode 100644 src/test-cgroup.c create mode 100644 systemd.pc.in (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 2316dd5093..ed881d7856 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +systemd.pc +test-cgroup +.libs/ systemd-notify test-daemon systemd-install diff --git a/Makefile.am b/Makefile.am index 76662b1826..ae83c4a44b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,6 +23,8 @@ dbussessionservicedir=@dbussessionservicedir@ dbussystemservicedir=@dbussystemservicedir@ dbusinterfacedir=@dbusinterfacedir@ udevrulesdir=@udevrulesdir@ +pamlibdir=@pamlibdir@ +pkgconfigdatadir=$(datadir)/pkgconfig # Our own, non-special dirs pkgsysconfdir=$(sysconfdir)/systemd @@ -45,6 +47,7 @@ AM_CPPFLAGS = \ -DCGROUP_AGENT_PATH=\"$(rootlibexecdir)/systemd-cgroups-agent\" \ -DSYSTEMD_BINARY_PATH=\"$(rootbindir)/systemd\" \ -DSYSTEMCTL_BINARY_PATH=\"$(rootbindir)/systemctl\" \ + -DRUNTIME_DIR=\"$(localstatedir)/run\" \ -I $(top_srcdir)/src rootbin_PROGRAMS = \ @@ -70,7 +73,13 @@ noinst_PROGRAMS = \ test-job-type \ test-ns \ test-loopback \ - test-daemon + test-daemon \ + test-cgroup + +if HAVE_PAM +pamlib_LTLIBRARIES = \ + pam_systemd.la +endif dist_dbuspolicy_DATA = \ src/org.freedesktop.systemd1.conf @@ -158,7 +167,8 @@ EXTRA_DIST = \ units/session/exit.service.in \ LICENSE \ README \ - DISTRO_PORTING + DISTRO_PORTING \ + src/systemd.pc.in if TARGET_FEDORA dist_systemunit_DATA += \ @@ -201,6 +211,9 @@ dist_doc_DATA = \ src/sd-daemon.h \ src/sd-daemon.c +pkgconfigdata_DATA = \ + systemd.pc + noinst_LTLIBRARIES = \ libsystemd-basic.la \ libsystemd-core.la @@ -262,7 +275,8 @@ libsystemd_core_la_SOURCES = \ src/unit-name.c \ src/fdset.c \ src/namespace.c \ - src/tcpwrap.c + src/tcpwrap.c \ + src/cgroup-util.c libsystemd_core_la_CFLAGS = \ $(AM_CFLAGS) \ @@ -356,6 +370,18 @@ test_daemon_SOURCES = \ test_daemon_LDADD = \ libsystemd-basic.la +test_cgroup_SOURCES = \ + src/test-cgroup.c \ + src/cgroup-util.c + +test_cgroup_CFLAGS = \ + $(AM_CFLAGS) \ + $(CGROUP_CFLAGS) + +test_cgroup_LDADD = \ + libsystemd-basic.la \ + $(CGROUP_LIBS) + systemd_logger_SOURCES = \ src/logger.c \ src/sd-daemon.c \ @@ -442,12 +468,41 @@ systemadm_LDADD = \ $(DBUSGLIB_LIBS) \ $(GTK_LIBS) +pam_systemd_la_SOURCES = \ + src/pam-module.c \ + src/cgroup-util.c \ + src/sd-daemon.c + +pam_systemd_la_CFLAGS = \ + $(AM_CFLAGS) \ + $(CGROUP_CFLAGS) \ + -fvisibility=hidden + +pam_systemd_la_LDFLAGS = \ + -module \ + -export-dynamic \ + -avoid-version \ + -shared \ + -export-symbols-regex '^pam_sm_.*' + +pam_systemd_la_LIBADD = \ + libsystemd-basic.la \ + $(PAM_LIBS) \ + $(CGROUP_LIBS) + SED_PROCESS = \ $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \ $(SED) -e 's,@rootlibexecdir\@,$(rootlibexecdir),g' \ -e 's,@SPECIAL_SYSLOG_SERVICE\@,$(SPECIAL_SYSLOG_SERVICE),g' \ -e 's,@SPECIAL_DBUS_SERVICE\@,$(SPECIAL_DBUS_SERVICE),g' \ -e 's,@SYSTEMCTL\@,$(rootbindir)/systemctl,g' \ + -e 's,@pkgsysconfdir\@,$(pkgsysconfdir),g' \ + -e 's,@pkgdatadir\@,$(pkgdatadir),g' \ + -e 's,@systemunitdir\@,$(systemunitdir),g' \ + -e 's,@PACKAGE_VERSION\@,$(PACKAGE_VERSION),g' \ + -e 's,@PACKAGE_NAME\@,$(PACKAGE_NAME),g' \ + -e 's,@PACKAGE_URL\@,$(PACKAGE_URL),g' \ + -e 's,@prefix\@,$(prefix),g' \ < $< > $@ units/%: units/%.in Makefile @@ -456,6 +511,9 @@ units/%: units/%.in Makefile man/%: man/%.in Makefile $(SED_PROCESS) +%.pc: %.pc.in Makefile + $(SED_PROCESS) + M4_PROCESS_SYSTEM = \ $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \ $(M4) -P $(M4_DISTRO_FLAG) -DFOR_SYSTEM=1 < $< > $@ @@ -639,4 +697,5 @@ DISTCHECK_CONFIGURE_FLAGS = \ --with-dbussystemservicedir=$$dc_install_base/$(dbussystemservicedir) \ --with-dbusinterfacedir=$$dc_install_base/$(dbusinterfacedir) \ --with-udevrulesdir=$$dc_install_base/$(udevrulesdir) \ + --with-pamlibdir=$$dc_install_base/$(pamlibdir) \ --with-rootdir=$$dc_install_base/$(rootdir) diff --git a/configure.ac b/configure.ac index cdcb71cec1..cb419169ed 100644 --- a/configure.ac +++ b/configure.ac @@ -352,7 +352,12 @@ AC_ARG_WITH([dbusinterfacedir], AC_ARG_WITH([udevrulesdir], AS_HELP_STRING([--with-udevrulesdir=DIR], [Diectory for udev rules]), [], - [with_udevrulesdir=/lib/udev/rules.d]) + [with_udevrulesdir=`pkg-config --variable=udevdir udev`/rules.d]) + +AC_ARG_WITH([pamlibdir], + AS_HELP_STRING([--with-pamlibdir=DIR], [Diectory for PAM modules]), + [], + [with_pamlibdir=/lib/`$CC -print-multi-os-directory`/security]) AC_ARG_WITH([rootdir], AS_HELP_STRING([--with-rootdir=DIR], [Root directory for files necessary for boot]), @@ -364,6 +369,7 @@ AC_SUBST([dbussessionservicedir], [$with_dbussessionservicedir]) AC_SUBST([dbussystemservicedir], [$with_dbussystemservicedir]) AC_SUBST([dbusinterfacedir], [$with_dbusinterfacedir]) AC_SUBST([udevrulesdir], [$with_udevrulesdir]) +AC_SUBST([pamlibdir], [$with_pamlibdir]) AC_SUBST([rootdir], [$with_rootdir]) AC_CONFIG_FILES([Makefile]) @@ -383,6 +389,7 @@ echo " prefix: ${prefix} root dir: ${with_rootdir} udev rules dir: ${with_udevrulesdir} + pam modules dir: ${with_pamlibdir} dbus policy dir: ${with_dbuspolicydir} dbus session dir: ${with_dbussessionservicedir} dbus system dir: ${with_dbussystemservicedir} diff --git a/fixme b/fixme index 9152a2521d..3ca6a02e00 100644 --- a/fixme +++ b/fixme @@ -1,29 +1,17 @@ -* calendar time support in timer +* calendar time support in timer, iCalendar semantics for the timer stuff (RFC2445) * complete dbus exposure -* make conf parser work more like .desktop parsers - * implicitly import "defaults" settings file into all types -* service startup should be delayed if the matching socket is being started - -* add #ifdefs for non-redhat builds in sysv parser - * add #ifdefs for non-sysv builds -* bootchart hookup - * reinvestigate random seed, hwclock * "disabled" load state? -* %m in printf() instead of strerror(); - * gc: don't reap broken services -* iCalendar semantics for the timer stuff (RFC2445) - * ability to kill services? i.e. in contrast to stopping them, go directly into killing mode? @@ -47,10 +35,6 @@ * follow property change dbus spec -* make systemd bus activatable (?) - -* pam module - * selinux External: @@ -68,3 +52,5 @@ Regularly: * check for strerror(r) instead of strerror(-r) * Use PR_SET_PROCTITLE_AREA if it becomes available in the kernel + +* %m in printf() instead of strerror(); diff --git a/src/cgroup-util.c b/src/cgroup-util.c new file mode 100644 index 0000000000..9337817b6b --- /dev/null +++ b/src/cgroup-util.c @@ -0,0 +1,694 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include + +#include "cgroup-util.h" +#include "log.h" +#include "set.h" +#include "macro.h" +#include "util.h" + +int cg_translate_error(int error, int _errno) { + + switch (error) { + + case ECGROUPNOTCOMPILED: + case ECGROUPNOTMOUNTED: + case ECGROUPNOTEXIST: + case ECGROUPNOTCREATED: + return -ENOENT; + + case ECGINVAL: + return -EINVAL; + + case ECGROUPNOTALLOWED: + return -EPERM; + + case ECGOTHER: + return -_errno; + } + + return -EIO; +} + +static struct cgroup* cg_new(const char *controller, const char *path) { + struct cgroup *cgroup; + + assert(path); + assert(controller); + + if (!(cgroup = cgroup_new_cgroup(path))) + return NULL; + + if (!cgroup_add_controller(cgroup, controller)) { + cgroup_free(&cgroup); + return NULL; + } + + return cgroup; +} + +int cg_kill(const char *controller, const char *path, int sig, bool ignore_self) { + bool killed = false, done = false; + Set *s; + pid_t my_pid; + int r, ret = 0; + + assert(controller); + assert(path); + assert(sig >= 0); + + /* This goes through the tasks list and kills them all. This + * is repeated until no further processes are added to the + * tasks list, to properly handle forking processes */ + + if (!(s = set_new(trivial_hash_func, trivial_compare_func))) + return -ENOMEM; + + my_pid = getpid(); + + do { + void *iterator = NULL; + pid_t pid = 0; + + done = true; + + r = cgroup_get_task_begin(path, controller, &iterator, &pid); + while (r == 0) { + + if (pid == my_pid && ignore_self) + goto next; + + if (set_get(s, INT_TO_PTR(pid)) == INT_TO_PTR(pid)) + goto next; + + /* If we haven't killed this process yet, kill + * it */ + + if (kill(pid, sig) < 0 && errno != ESRCH) { + if (ret == 0) + ret = -errno; + } + + killed = true; + done = false; + + if ((r = set_put(s, INT_TO_PTR(pid))) < 0) + goto loop_exit; + + next: + r = cgroup_get_task_next(&iterator, &pid); + } + + if (r == 0 || r == ECGEOF) + r = 0; + else if (r == ECGOTHER && errno == ENOENT) + r = -ESRCH; + else + r = cg_translate_error(r, errno); + + loop_exit: + assert_se(cgroup_get_task_end(&iterator) == 0); + + /* To avoid racing against processes which fork + * quicker than we can kill them we repeat this until + * no new pids need to be killed. */ + + } while (!done && r >= 0); + + set_free(s); + + if (ret < 0) + return ret; + + if (r < 0) + return r; + + return !!killed; +} + +int cg_kill_recursive(const char *controller, const char *path, int sig, bool ignore_self) { + struct cgroup_file_info info; + int level = 0, r, ret = 0; + void *iterator = NULL; + bool killed = false; + + assert(path); + assert(controller); + assert(sig >= 0); + + zero(info); + + r = cgroup_walk_tree_begin(controller, path, 0, &iterator, &info, &level); + while (r == 0) { + int k; + char *p; + + if (info.type != CGROUP_FILE_TYPE_DIR) + goto next; + + if (asprintf(&p, "%s/%s", path, info.path) < 0) { + ret = -ENOMEM; + break; + } + + k = cg_kill(controller, p, sig, ignore_self); + free(p); + + if (k < 0) { + if (ret == 0) + ret = k; + } else if (k > 0) + killed = true; + + next: + + r = cgroup_walk_tree_next(0, &iterator, &info, level); + } + + if (ret == 0) { + if (r == 0 || r == ECGEOF) + ret = !!killed; + else if (r == ECGOTHER && errno == ENOENT) + ret = -ESRCH; + else + ret = cg_translate_error(r, errno); + } + + assert_se(cgroup_walk_tree_end(&iterator) == 0); + + return ret; +} + +int cg_kill_recursive_and_wait(const char *controller, const char *path) { + unsigned i; + + assert(path); + assert(controller); + + /* This safely kills all processes; first it sends a SIGTERM, + * then checks 8 times after 50ms whether the group is + * now empty, and finally kills everything that is left with + * SIGKILL */ + + for (i = 0; i < 10; i++) { +int sig, r; + + if (i <= 0) + sig = SIGTERM; + else if (i >= 9) + sig = SIGKILL; + else + sig = 0; + + if ((r = cg_kill_recursive(controller, path, sig, true)) <= 0) + return r; + + usleep(50 * USEC_PER_MSEC); + } + + return 0; +} + +int cg_migrate(const char *controller, const char *from, const char *to, bool ignore_self) { + bool migrated = false, done = false; + struct cgroup *dest; + int r, ret = 0; + pid_t my_pid; + + assert(controller); + assert(from); + assert(to); + + if (!(dest = cg_new(controller, to))) + return -ENOMEM; + + my_pid = getpid(); + + do { + void *iterator = NULL; + pid_t pid = 0; + + done = true; + + r = cgroup_get_task_begin(from, controller, &iterator, &pid); + while (r == 0) { + + if (pid == my_pid && ignore_self) + goto next; + + if ((r = cgroup_attach_task_pid(dest, pid)) != 0) { + if (ret == 0) + r = cg_translate_error(r, errno); + } + + migrated = true; + done = false; + + next: + + r = cgroup_get_task_next(&iterator, &pid); + } + + if (r == 0 || r == ECGEOF) + r = 0; + else if (r == ECGOTHER && errno == ENOENT) + r = -ESRCH; + else + r = cg_translate_error(r, errno); + + assert_se(cgroup_get_task_end(&iterator) == 0); + + } while (!done && r >= 0); + + cgroup_free(&dest); + + if (ret < 0) + return ret; + + if (r < 0) + return r; + + return !!migrated; +} + +int cg_migrate_recursive(const char *controller, const char *from, const char *to, bool ignore_self) { + struct cgroup_file_info info; + int level = 0, r, ret = 0; + void *iterator = NULL; + bool migrated = false; + + assert(controller); + assert(from); + assert(to); + + zero(info); + + r = cgroup_walk_tree_begin(controller, from, 0, &iterator, &info, &level); + while (r == 0) { + int k; + char *p; + + if (info.type != CGROUP_FILE_TYPE_DIR) + goto next; + + if (asprintf(&p, "%s/%s", from, info.path) < 0) { + ret = -ENOMEM; + break; + } + + k = cg_migrate(controller, p, to, ignore_self); + free(p); + + if (k < 0) { + if (ret == 0) + ret = k; + } else if (k > 0) + migrated = true; + + next: + r = cgroup_walk_tree_next(0, &iterator, &info, level); + } + + if (ret == 0) { + if (r == 0 || r == ECGEOF) + r = !!migrated; + else if (r == ECGOTHER && errno == ENOENT) + r = -ESRCH; + else + r = cg_translate_error(r, errno); + } + + assert_se(cgroup_walk_tree_end(&iterator) == 0); + + return ret; +} + +int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) { + char *mp; + int r; + + assert(controller); + assert(path); + + if ((r = cgroup_get_subsys_mount_point(controller, &mp)) != 0) + return cg_translate_error(r, errno); + + if (suffix) + r = asprintf(fs, "%s/%s/%s", mp, path, suffix); + else + r = asprintf(fs, "%s/%s", mp, path); + + free(mp); + + return r < 0 ? -ENOMEM : 0; +} + +int cg_trim(const char *controller, const char *path, bool delete_root) { + char *fs; + int r; + + assert(controller); + assert(path); + + if ((r = cg_get_path(controller, path, NULL, &fs)) < 0) + return r; + + r = rm_rf(fs, true, delete_root); + free(fs); + + return r; +} + +int cg_delete(const char *controller, const char *path) { + struct cgroup *cg; + int r; + + assert(controller); + assert(path); + + if (!(cg = cg_new(controller, path))) + return -ENOMEM; + + if ((r = cgroup_delete_cgroup_ext(cg, CGFLAG_DELETE_RECURSIVE|CGFLAG_DELETE_IGNORE_MIGRATION)) != 0) { + r = cg_translate_error(r, errno); + goto finish; + } + + r = 0; + +finish: + cgroup_free(&cg); + + return r; +} + +int cg_create(const char *controller, const char *path) { + struct cgroup *cg; + int r; + + assert(controller); + assert(path); + + if (!(cg = cg_new(controller, path))) + return -ENOMEM; + + if ((r = cgroup_create_cgroup(cg, 1)) != 0) { + r = cg_translate_error(r, errno); + goto finish; + } + + r = 0; + +finish: + cgroup_free(&cg); + + return r; +} + +int cg_attach(const char *controller, const char *path, pid_t pid) { + struct cgroup *cg; + int r; + + assert(controller); + assert(path); + assert(pid >= 0); + + if (!(cg = cg_new(controller, path))) + return -ENOMEM; + + if (pid == 0) + pid = getpid(); + + if ((r = cgroup_attach_task_pid(cg, pid))) { + r = cg_translate_error(r, errno); + goto finish; + } + + r = 0; + +finish: + cgroup_free(&cg); + + return r; +} + +int cg_create_and_attach(const char *controller, const char *path, pid_t pid) { + struct cgroup *cg; + int r; + + assert(controller); + assert(path); + assert(pid >= 0); + + if (!(cg = cg_new(controller, path))) + return -ENOMEM; + + if ((r = cgroup_create_cgroup(cg, 1)) != 0) { + r = cg_translate_error(r, errno); + goto finish; + } + + if (pid == 0) + pid = getpid(); + + if ((r = cgroup_attach_task_pid(cg, pid))) { + r = cg_translate_error(r, errno); + goto finish; + } + + r = 0; + +finish: + cgroup_free(&cg); + + return r; +} + +int cg_set_group_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid) { + char *fs; + int r; + + assert(controller); + assert(path); + + if ((r = cg_get_path(controller, path, NULL, &fs)) < 0) + return r; + + r = chmod_and_chown(fs, mode, uid, gid); + free(fs); + + return r; +} + +int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid) { + char *fs; + int r; + + assert(controller); + assert(path); + + if ((r = cg_get_path(controller, path, "tasks", &fs)) < 0) + return r; + + r = chmod_and_chown(fs, mode, uid, gid); + free(fs); + + return r; +} + +int cg_get_by_pid(const char *controller, pid_t pid, char **path) { + int r; + char *p = NULL; + + assert(controller); + assert(pid > 0); + assert(path); + + if ((r = cgroup_get_current_controller_path(pid, controller, &p)) != 0) + return cg_translate_error(r, errno); + + assert(p); + + *path = p; + return 0; +} + +int cg_install_release_agent(const char *controller, const char *agent) { + char *mp = NULL, *path = NULL, *contents = NULL, *line = NULL, *sc; + int r; + + assert(controller); + assert(agent); + + if ((r = cgroup_get_subsys_mount_point(controller, &mp)) != 0) + return cg_translate_error(r, errno); + + if (asprintf(&path, "%s/release_agent", mp) < 0) { + r = -ENOMEM; + goto finish; + } + + if ((r = read_one_line_file(path, &contents)) < 0) + goto finish; + + sc = strstrip(contents); + + if (sc[0] == 0) { + + if (asprintf(&line, "%s\n", agent) < 0) { + r = -ENOMEM; + goto finish; + } + + if ((r = write_one_line_file(path, line)) < 0) + goto finish; + + } else if (!streq(sc, agent)) { + r = -EEXIST; + goto finish; + } + + free(path); + path = NULL; + if (asprintf(&path, "%s/notify_on_release", mp) < 0) { + r = -ENOMEM; + goto finish; + } + + free(contents); + contents = NULL; + if ((r = read_one_line_file(path, &contents)) < 0) + goto finish; + + sc = strstrip(contents); + + if (streq(sc, "0")) { + if ((r = write_one_line_file(path, "1\n")) < 0) + goto finish; + } else if (!streq(sc, "1")) { + r = -EIO; + goto finish; + } + + r = 0; + +finish: + free(mp); + free(path); + free(contents); + free(line); + + return r; +} + +int cg_is_empty(const char *controller, const char *path, bool ignore_self) { + void *iterator = NULL; + pid_t pid = 0; + int r; + + assert(controller); + assert(path); + + r = cgroup_get_task_begin(path, controller, &iterator, &pid); + while (r == 0) { + + if (ignore_self&& pid == getpid()) + goto next; + + break; + + next: + r = cgroup_get_task_next(&iterator, &pid); + } + + + if (r == ECGEOF) + r = 1; + else if (r != 0) + r = cg_translate_error(r, errno); + else + r = 0; + + assert_se(cgroup_get_task_end(&iterator) == 0); + + return r; +} + +int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self) { + struct cgroup_file_info info; + int level = 0, r, ret = 0; + void *iterator = NULL; + bool empty = true; + + assert(controller); + assert(path); + + zero(info); + + r = cgroup_walk_tree_begin(controller, path, 0, &iterator, &info, &level); + while (r == 0) { + int k; + char *p; + + if (info.type != CGROUP_FILE_TYPE_DIR) + goto next; + + if (asprintf(&p, "%s/%s", path, info.path) < 0) { + ret = -ENOMEM; + break; + } + + k = cg_is_empty(controller, p, ignore_self); + free(p); + + if (k < 0) { + ret = k; + break; + } else if (k == 0) { + empty = false; + break; + } + + next: + r = cgroup_walk_tree_next(0, &iterator, &info, level); + } + + if (ret == 0) { + if (r == 0 || r == ECGEOF) + ret = !!empty; + else if (r == ECGOTHER && errno == ENOENT) + ret = -ESRCH; + else + ret = cg_translate_error(r, errno); + } + + assert_se(cgroup_walk_tree_end(&iterator) == 0); + + return ret; +} diff --git a/src/cgroup-util.h b/src/cgroup-util.h new file mode 100644 index 0000000000..4ada71bc17 --- /dev/null +++ b/src/cgroup-util.h @@ -0,0 +1,54 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +#ifndef foocgrouputilhfoo +#define foocgrouputilhfoo + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include + +int cg_translate_error(int error, int _errno); + +int cg_kill(const char *controller, const char *path, int sig, bool ignore_self); +int cg_kill_recursive(const char *controller, const char *path, int sig, bool ignore_self); +int cg_kill_recursive_and_wait(const char *controller, const char *path); + +int cg_migrate(const char *controller, const char *from, const char *to, bool ignore_self); +int cg_migrate_recursive(const char *controller, const char *from, const char *to, bool ignore_self); + +int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs); +int cg_get_by_pid(const char *controller, pid_t pid, char **path); + +int cg_trim(const char *controller, const char *path, bool delete_root); +int cg_delete(const char *controller, const char *path); + +int cg_create(const char *controller, const char *path); +int cg_attach(const char *controller, const char *path, pid_t pid); +int cg_create_and_attach(const char *controller, const char *path, pid_t pid); + +int cg_set_group_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid); +int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid); + +int cg_install_release_agent(const char *controller, const char *agent); + +int cg_is_empty(const char *controller, const char *path, bool ignore_self); +int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self); + +#endif diff --git a/src/cgroup.c b/src/cgroup.c index 291db4e6c9..27130a9c39 100644 --- a/src/cgroup.c +++ b/src/cgroup.c @@ -26,32 +26,12 @@ #include #include +#include + #include "cgroup.h" +#include "cgroup-util.h" #include "log.h" -static int translate_error(int error, int _errno) { - - switch (error) { - - case ECGROUPNOTCOMPILED: - case ECGROUPNOTMOUNTED: - case ECGROUPNOTEXIST: - case ECGROUPNOTCREATED: - return -ENOENT; - - case ECGINVAL: - return -EINVAL; - - case ECGROUPNOTALLOWED: - return -EPERM; - - case ECGOTHER: - return -_errno; - } - - return -EIO; -} - int cgroup_bonding_realize(CGroupBonding *b) { int r; @@ -59,39 +39,27 @@ int cgroup_bonding_realize(CGroupBonding *b) { assert(b->path); assert(b->controller); - if (b->cgroup) + if (b->realized) return 0; - if (!(b->cgroup = cgroup_new_cgroup(b->path))) - return -ENOMEM; + if ((r = cg_create(b->controller, b->path)) < 0) + return r; - if (!cgroup_add_controller(b->cgroup, b->controller)) { - r = -ENOMEM; - goto fail; - } + b->realized = true; - if ((r = cgroup_create_cgroup(b->cgroup, true)) != 0) { - r = translate_error(r, errno); - goto fail; - } + if (b->only_us && b->clean_up) + cg_trim(b->controller, b->path, false); return 0; - -fail: - cgroup_free(&b->cgroup); - b->cgroup = NULL; - return r; } int cgroup_bonding_realize_list(CGroupBonding *first) { CGroupBonding *b; + int r; - LIST_FOREACH(by_unit, b, first) { - int r; - + LIST_FOREACH(by_unit, b, first) if ((r = cgroup_bonding_realize(b)) < 0) return r; - } return 0; } @@ -113,11 +81,12 @@ void cgroup_bonding_free(CGroupBonding *b) { hashmap_remove(b->unit->meta.manager->cgroup_bondings, b->path); } - if (b->cgroup) { - if (b->only_us && b->clean_up && cgroup_bonding_is_empty(b) > 0) - cgroup_delete_cgroup_ext(b->cgroup, true); + if (b->realized && b->only_us && b->clean_up) { - cgroup_free(&b->cgroup); + if (cgroup_bonding_is_empty(b) > 0) + cg_delete(b->controller, b->path); + else + cg_trim(b->controller, b->path, false); } free(b->controller); @@ -138,109 +107,36 @@ int cgroup_bonding_install(CGroupBonding *b, pid_t pid) { assert(b); assert(pid >= 0); - if (pid == 0) - pid = getpid(); - - if (!b->cgroup) - return -ENOENT; - - if ((r = cgroup_attach_task_pid(b->cgroup, pid))) - return translate_error(r, errno); + if ((r = cg_create_and_attach(b->controller, b->path, pid)) < 0) + return r; + b->realized = true; return 0; } int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid) { CGroupBonding *b; + int r; - LIST_FOREACH(by_unit, b, first) { - int r; - + LIST_FOREACH(by_unit, b, first) if ((r = cgroup_bonding_install(b, pid)) < 0) return r; - } return 0; } int cgroup_bonding_kill(CGroupBonding *b, int sig) { int r; - Set *s; - bool done; - bool killed = false; assert(b); - assert(sig > 0); - - if (!b->only_us) - return -EAGAIN; - - if (!(s = set_new(trivial_hash_func, trivial_compare_func))) - return -ENOMEM; - - do { - void *iterator; - pid_t pid; - - done = true; - - if ((r = cgroup_get_task_begin(b->path, b->controller, &iterator, &pid)) != 0) { - if (r == ECGEOF) { - r = 0; - goto kill_done; - } else { - if (r == ECGOTHER && errno == ENOENT) - r = ESRCH; - else - r = translate_error(r, errno); - break; - } - } - - for (;;) { - if (set_get(s, INT_TO_PTR(pid)) != INT_TO_PTR(pid)) { - - /* If we haven't killed this process - * yet, kill it */ - - if (kill(pid, sig) < 0 && errno != ESRCH) { - r = -errno; - break; - } - - killed = true; - done = false; - - if ((r = set_put(s, INT_TO_PTR(pid))) < 0) - break; - } - - if ((r = cgroup_get_task_next(&iterator, &pid)) != 0) { + assert(sig >= 0); - if (r == ECGEOF) - r = 0; - else - r = translate_error(r, errno); - - break; - } - } - - kill_done: - assert_se(cgroup_get_task_end(&iterator) == 0); - - /* To avoid racing against processes which fork - * quicker than we can kill them we repeat this until - * no new pids need to be killed. */ - - } while (!done && r >= 0); - - set_free(s); - - if (r < 0) + if ((r = cgroup_bonding_realize(b)) < 0) return r; - return killed ? 0 : -ESRCH; + assert(b->realized); + + return cg_kill_recursive(b->controller, b->path, sig, true); } int cgroup_bonding_kill_list(CGroupBonding *first, int sig) { @@ -249,7 +145,7 @@ int cgroup_bonding_kill_list(CGroupBonding *first, int sig) { LIST_FOREACH(by_unit, b, first) { if ((r = cgroup_bonding_kill(b, sig)) < 0) { - if (r == -EAGAIN || -ESRCH) + if (r == -EAGAIN || r == -ESRCH) continue; return r; @@ -264,33 +160,19 @@ int cgroup_bonding_kill_list(CGroupBonding *first, int sig) { /* Returns 1 if the group is empty, 0 if it is not, -EAGAIN if we * cannot know */ int cgroup_bonding_is_empty(CGroupBonding *b) { - void *iterator; - pid_t pid; int r; assert(b); - r = cgroup_get_task_begin(b->path, b->controller, &iterator, &pid); - - if (r == 0 || r == ECGEOF) - cgroup_get_task_end(&iterator); + if ((r = cg_is_empty_recursive(b->controller, b->path, true)) < 0) + return r; - /* Hmm, no PID in this group? Then it is definitely empty */ - if (r == ECGEOF) + /* If it is empty it is empty */ + if (r > 0) return 1; - /* Some error? Let's return it */ - if (r != 0) - return translate_error(r, errno); - - /* It's not empty, and we are the only user, then it is - * definitely not empty */ - if (b->only_us) - return 0; - - /* There are PIDs in the group but we aren't the only users, - * hence we cannot say */ - return -EAGAIN; + /* It's not only us using this cgroup, so we just don't know */ + return b->only_us ? 0 : -EAGAIN; } int cgroup_bonding_is_empty_list(CGroupBonding *first) { @@ -313,99 +195,6 @@ int cgroup_bonding_is_empty_list(CGroupBonding *first) { return -EAGAIN; } -static int install_release_agent(Manager *m, const char *mount_point) { - char *p, *c, *sc; - int r; - - assert(m); - assert(mount_point); - - if (asprintf(&p, "%s/release_agent", mount_point) < 0) - return -ENOMEM; - - if ((r = read_one_line_file(p, &c)) < 0) { - free(p); - return r; - } - - sc = strstrip(c); - - if (sc[0] == 0) { - if ((r = write_one_line_file(p, CGROUP_AGENT_PATH "\n" )) < 0) { - free(p); - free(c); - return r; - } - } else if (!streq(sc, CGROUP_AGENT_PATH)) { - free(p); - free(c); - return -EEXIST; - } - - free(c); - free(p); - - if (asprintf(&p, "%s/notify_on_release", mount_point) < 0) - return -ENOMEM; - - if ((r = read_one_line_file(p, &c)) < 0) { - free(p); - return r; - } - - sc = strstrip(c); - - if (streq(sc, "0")) { - if ((r = write_one_line_file(p, "1\n")) < 0) { - free(p); - free(c); - return r; - } - } else if (!streq(sc, "1")) { - free(p); - free(c); - return -EIO; - } - - free(p); - free(c); - - return 0; -} - -static int create_hierarchy_cgroup(Manager *m) { - struct cgroup *cg; - int r; - - assert(m); - - if (!(cg = cgroup_new_cgroup(m->cgroup_hierarchy))) - return -ENOMEM; - - if (!(cgroup_add_controller(cg, m->cgroup_controller))) { - r = -ENOMEM; - goto finish; - } - - if ((r = cgroup_create_cgroup(cg, true)) != 0) { - log_error("Failed to create cgroup hierarchy group: %s", cgroup_strerror(r)); - r = translate_error(r, errno); - goto finish; - } - - if ((r = cgroup_attach_task(cg)) != 0) { - log_error("Failed to add ourselves to hierarchy group: %s", cgroup_strerror(r)); - r = translate_error(r, errno); - goto finish; - } - - r = 0; - -finish: - cgroup_free(&cg); - return r; -} - int manager_setup_cgroup(Manager *m) { char *cp; int r; @@ -416,7 +205,7 @@ int manager_setup_cgroup(Manager *m) { if ((r = cgroup_init()) != 0) { log_error("Failed to initialize libcg: %s", cgroup_strerror(r)); - return translate_error(r, errno); + return cg_translate_error(r, errno); } free(m->cgroup_controller); @@ -426,12 +215,12 @@ int manager_setup_cgroup(Manager *m) { free(m->cgroup_mount_point); m->cgroup_mount_point = NULL; if ((r = cgroup_get_subsys_mount_point(m->cgroup_controller, &m->cgroup_mount_point))) - return translate_error(r, errno); + return cg_translate_error(r, errno); pid = getpid(); if ((r = cgroup_get_current_controller_path(pid, m->cgroup_controller, &cp))) - return translate_error(r, errno); + return cg_translate_error(r, errno); snprintf(suffix, sizeof(suffix), "/systemd-%u", (unsigned) pid); char_array_0(suffix); @@ -457,12 +246,12 @@ int manager_setup_cgroup(Manager *m) { m->cgroup_mount_point, m->cgroup_hierarchy); - if ((r = install_release_agent(m, m->cgroup_mount_point)) < 0) + if ((r = cg_install_release_agent(m->cgroup_controller, CGROUP_AGENT_PATH)) < 0) log_warning("Failed to install release agent, ignoring: %s", strerror(-r)); else log_debug("Installed release agent, or already installed."); - if ((r = create_hierarchy_cgroup(m)) < 0) + if ((r = cg_create_and_attach(m->cgroup_controller, m->cgroup_hierarchy, 0)) < 0) log_error("Failed to create root cgroup hierarchy: %s", strerror(-r)); else log_debug("Created root group."); @@ -470,33 +259,13 @@ int manager_setup_cgroup(Manager *m) { return r; } -int manager_shutdown_cgroup(Manager *m, bool delete) { - struct cgroup *cg; - int r; - +int manager_shutdown_cgroup(Manager *m) { assert(m); - if (!m->cgroup_hierarchy) + if (!m->cgroup_controller || !m->cgroup_hierarchy) return 0; - if (!(cg = cgroup_new_cgroup(m->cgroup_hierarchy))) - return -ENOMEM; - - if (!(cgroup_add_controller(cg, m->cgroup_controller))) { - r = -ENOMEM; - goto finish; - } - - /* Often enough we won't be able to delete the cgroup we - * ourselves are in, hence ignore all errors here */ - if (delete) - cgroup_delete_cgroup_ext(cg, CGFLAG_DELETE_IGNORE_MIGRATION|CGFLAG_DELETE_RECURSIVE); - r = 0; - -finish: - cgroup_free(&cg); - return r; - + return cg_delete(m->cgroup_controller, m->cgroup_hierarchy); } int cgroup_notify_empty(Manager *m, const char *group) { @@ -541,15 +310,12 @@ Unit* cgroup_unit_by_pid(Manager *m, pid_t pid) { if (pid <= 1) return NULL; - if ((r = cgroup_get_current_controller_path(pid, m->cgroup_controller, &group))) + if ((r = cg_get_by_pid(m->cgroup_controller, pid, &group))) return NULL; l = hashmap_get(m->cgroup_bondings, group); free(group); - if (!l) - return NULL; - LIST_FOREACH(by_path, b, l) { if (!b->unit) diff --git a/src/cgroup.h b/src/cgroup.h index 67c7cc3501..11d2aba8cf 100644 --- a/src/cgroup.h +++ b/src/cgroup.h @@ -22,8 +22,6 @@ along with systemd; If not, see . ***/ -#include - typedef struct CGroupBonding CGroupBonding; #include "unit.h" @@ -35,8 +33,6 @@ struct CGroupBonding { Unit *unit; - struct cgroup *cgroup; - /* For the Unit::cgroup_bondings list */ LIST_FIELDS(CGroupBonding, by_unit); @@ -48,6 +44,9 @@ struct CGroupBonding { /* When our tasks are the only ones in this group */ bool only_us:1; + + /* This cgroup is realized */ + bool realized:1; }; int cgroup_bonding_realize(CGroupBonding *b); @@ -72,7 +71,7 @@ char *cgroup_bonding_to_string(CGroupBonding *b); #include "manager.h" int manager_setup_cgroup(Manager *m); -int manager_shutdown_cgroup(Manager *m, bool delete); +int manager_shutdown_cgroup(Manager *m); int cgroup_notify_empty(Manager *m, const char *group); diff --git a/src/manager.c b/src/manager.c index 554dd8e866..f1a79b5e89 100644 --- a/src/manager.c +++ b/src/manager.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -421,7 +420,8 @@ void manager_free(Manager *m) { /* If we reexecute ourselves, we keep the root cgroup * around */ - manager_shutdown_cgroup(m, m->exit_code != MANAGER_REEXECUTE); + if (m->exit_code != MANAGER_REEXECUTE) + manager_shutdown_cgroup(m); bus_done(m); diff --git a/src/pam-module.c b/src/pam-module.c new file mode 100644 index 0000000000..5157897a71 --- /dev/null +++ b/src/pam-module.c @@ -0,0 +1,466 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "util.h" +#include "cgroup-util.h" +#include "macro.h" +#include "sd-daemon.h" + +static int parse_argv(pam_handle_t *handle, + int argc, const char **argv, + bool *create_session, + bool *kill_session, + bool *kill_user) { + + unsigned i; + + assert(argc >= 0); + assert(argc == 0 || argv); + + for (i = 0; i < (unsigned) argc; i++) { + int k; + + if (startswith(argv[i], "create-session=")) { + if ((k = parse_boolean(argv[i] + 15)) < 0) { + pam_syslog(handle, LOG_ERR, "Failed to parse create-session= argument."); + return k; + } + + if (create_session) + *create_session = k; + } else if (startswith(argv[i], "kill-session=")) { + if ((k = parse_boolean(argv[i] + 13)) < 0) { + pam_syslog(handle, LOG_ERR, "Failed to parse kill-session= argument."); + return k; + } + + if (kill_session) + *kill_session = k; + + } else if (startswith(argv[i], "kill-user=")) { + if ((k = parse_boolean(argv[i] + 10)) < 0) { + pam_syslog(handle, LOG_ERR, "Failed to parse kill-user= argument."); + return k; + } + + if (kill_user) + *kill_user = k; + } else { + pam_syslog(handle, LOG_ERR, "Unknown parameter '%s'.", argv[i]); + return -EINVAL; + } + } + + if (kill_session && *kill_session && kill_user) + *kill_user = true; + + return 0; +} + +static int open_file_and_lock(const char *fn) { + int fd; + + assert(fn); + + if ((fd = open(fn, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_CREAT, 0600)) < 0) + return -errno; + + if (flock(fd, LOCK_EX) < 0) + return -errno; + + return fd; +} + +static uint32_t combine32(unsigned long long u) { + uint32_t r = 0; + unsigned i; + + for (i = 0; i < sizeof(u)/4; i ++) + r ^= (uint32_t) ((u >> (i*32)) & 0xFFFFFFFFULL); + + return r; +} + +static char *generate_session_cookie(void) { + char *machine; + char *cookie; + unsigned long long r; + usec_t u; + int k; + + if (getmachineid_malloc(&machine) < 0) + if (!(machine = gethostname_malloc())) + return NULL; + + r = random_ull(); + u = now(CLOCK_REALTIME); + + k = asprintf(&cookie, "%s-%lu-%lu", + machine, + (unsigned long) combine32(r), + (unsigned long) combine32((unsigned long long) u)); + free(machine); + + return k < 0 ? NULL : cookie; +} + +static int get_user_data( + pam_handle_t *handle, + const char **ret_username, + struct passwd **ret_pw) { + + const char *username; + struct passwd *pw; + int r; + + assert(handle); + assert(ret_username); + assert(ret_pw); + + if ((r = pam_get_user(handle, &username, NULL)) != PAM_SUCCESS) { + pam_syslog(handle, LOG_ERR, "Failed to get user name."); + return r; + } + + if (!username || !*username) { + pam_syslog(handle, LOG_ERR, "User name not valid."); + return PAM_AUTH_ERR; + } + + if (!(pw = pam_modutil_getpwnam(handle, username))) { + pam_syslog(handle, LOG_ERR, "Failed to get user data."); + return PAM_USER_UNKNOWN; + } + + *ret_pw = pw; + *ret_username = username; + + return PAM_SUCCESS; +} + +static int create_user_group(pam_handle_t *handle, const char *group, struct passwd *pw, bool attach) { + int r; + + assert(handle); + assert(group); + + if (attach) + r = cg_create_and_attach("name=systemd", group, 0); + else + r = cg_create("name=systemd", group); + + if (r < 0) { + pam_syslog(handle, LOG_ERR, "Failed to create cgroup: %s", strerror(-r)); + return PAM_SESSION_ERR; + } + + if ((r = cg_set_task_access("name=systemd", group, 0755, pw->pw_uid, pw->pw_gid)) < 0 || + (r = cg_set_group_access("name=systemd", group, 0755, pw->pw_uid, pw->pw_gid)) < 0) { + pam_syslog(handle, LOG_ERR, "Failed to change access modes: %s", strerror(-r)); + return PAM_SESSION_ERR; + } + + return PAM_SUCCESS; +} + +_public_ PAM_EXTERN int pam_sm_open_session( + pam_handle_t *handle, + int flags, + int argc, const char **argv) { + + const char *username = NULL; + struct passwd *pw; + int r; + char *buf = NULL; + int lock_fd = -1; + bool create_session = true; + + assert(handle); + + pam_syslog(handle, LOG_INFO, "pam-systemd initializing"); + + if (parse_argv(handle, argc, argv, &create_session, NULL, NULL) < 0) + return PAM_SESSION_ERR; + + /* Make this a NOP on non-systemd systems */ + if (sd_booted() <= 0) + return PAM_SUCCESS; + + if ((r = cgroup_init()) != 0) { + pam_syslog(handle, LOG_ERR, "libcgroup initialization failed: %s", cgroup_strerror(r)); + r = PAM_SESSION_ERR; + goto finish; + } + + if ((r = get_user_data(handle, &username, &pw)) != PAM_SUCCESS) + goto finish; + + if (safe_mkdir(RUNTIME_DIR "/user", 0755, 0, 0) < 0) { + pam_syslog(handle, LOG_ERR, "Failed to create runtime directory: %m"); + r = PAM_SYSTEM_ERR; + goto finish; + } + + if ((lock_fd = open_file_and_lock(RUNTIME_DIR "/user/.pam-systemd-lock")) < 0) { + pam_syslog(handle, LOG_ERR, "Failed to lock runtime directory: %m"); + r = PAM_SYSTEM_ERR; + goto finish; + } + + /* Create /var/run/$USER */ + free(buf); + if (asprintf(&buf, RUNTIME_DIR "/user/%s", username) < 0) { + r = PAM_BUF_ERR; + goto finish; + } + + if (safe_mkdir(buf, 0700, pw->pw_uid, pw->pw_gid) < 0) { + pam_syslog(handle, LOG_WARNING, "Failed to create runtime directory: %m"); + r = PAM_SYSTEM_ERR; + goto finish; + } else if ((r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", buf, 0)) != PAM_SUCCESS) { + pam_syslog(handle, LOG_ERR, "Failed to set runtime dir."); + goto finish; + } + + free(buf); + buf = NULL; + + if (create_session) { + const char *cookie; + + /* Reuse or create XDG session ID */ + if (!(cookie = pam_getenv(handle, "XDG_SESSION_COOKIE"))) { + if (!(buf = generate_session_cookie())) { + r = PAM_BUF_ERR; + goto finish; + } + + if ((r = pam_misc_setenv(handle, "XDG_SESSION_COOKIE", buf, 0)) != PAM_SUCCESS) { + pam_syslog(handle, LOG_ERR, "Failed to set cookie."); + goto finish; + } + + if (!(cookie = pam_getenv(handle, "XDG_SESSION_COOKIE"))) { + pam_syslog(handle, LOG_ERR, "Failed to get cookie."); + r = PAM_SESSION_ERR; + goto finish; + } + } + + r = asprintf(&buf, "/user/%s/%s", username, cookie); + } else + r = asprintf(&buf, "/user/%s/no-session", username); + + if (r < 0) { + r = PAM_BUF_ERR; + goto finish; + } + + if ((r = create_user_group(handle, buf, pw, true)) != PAM_SUCCESS) + goto finish; + + r = PAM_SUCCESS; + +finish: + free(buf); + + if (lock_fd >= 0) + close_nointr_nofail(lock_fd); + + return r; +} + +static int session_remains(pam_handle_t *handle, const char *user_path) { + struct cgroup_file_info info; + int level = 0, r; + void *iterator = NULL; + bool remains = false; + + zero(info); + + r = cgroup_walk_tree_begin("name=systemd", user_path, 0, &iterator, &info, &level); + while (r == 0) { + + if (info.type != CGROUP_FILE_TYPE_DIR) + goto next; + + if (streq(info.path, "")) + goto next; + + if (streq(info.path, "no-session")) + goto next; + + remains = true; + break; + + next: + + r = cgroup_walk_tree_next(0, &iterator, &info, level); + } + + + if (remains) + r = 1; + else if (r == 0 || r == ECGEOF) + r = 0; + else + r = cg_translate_error(r, errno); + + assert_se(cgroup_walk_tree_end(&iterator) == 0); + + return r; +} + +_public_ PAM_EXTERN int pam_sm_close_session( + pam_handle_t *handle, + int flags, + int argc, const char **argv) { + + const char *username = NULL; + bool kill_session = false; + bool kill_user = false; + int lock_fd = -1, r; + char *session_path = NULL, *nosession_path = NULL, *user_path = NULL; + const char *cookie; + struct passwd *pw; + + assert(handle); + + if (parse_argv(handle, argc, argv, NULL, &kill_session, &kill_user) < 0) + return PAM_SESSION_ERR; + + /* Make this a NOP on non-systemd systems */ + if (sd_booted() <= 0) + return PAM_SUCCESS; + + if ((r = get_user_data(handle, &username, &pw)) != PAM_SUCCESS) + goto finish; + + if ((lock_fd = open_file_and_lock(RUNTIME_DIR "/user/.pam-systemd-lock")) < 0) { + pam_syslog(handle, LOG_ERR, "Failed to lock runtime directory: %m"); + r = PAM_SYSTEM_ERR; + goto finish; + } + + if (asprintf(&user_path, "/user/%s", username) < 0) { + r = PAM_BUF_ERR; + goto finish; + } + + if ((cookie = pam_getenv(handle, "XDG_SESSION_COOKIE"))) { + + if (asprintf(&session_path, "/user/%s/%s", username, cookie) < 0 || + asprintf(&nosession_path, "/user/%s/no-session", username) < 0) { + r = PAM_BUF_ERR; + goto finish; + } + + if (kill_session) { + pam_syslog(handle, LOG_INFO, "KILLING ENTER"); + + /* Kill processes in session cgroup */ + if ((r = cg_kill_recursive_and_wait("name=systemd", session_path)) < 0) + pam_syslog(handle, LOG_ERR, "Failed to kill session cgroup: %s", strerror(-r)); + + pam_syslog(handle, LOG_INFO, "KILLING EXIT"); + + } else { + /* Migrate processes from session to + * no-session cgroup. First, try to create the + * no-session group in case it doesn't exist + * yet. */ + create_user_group(handle, nosession_path, pw, 0); + + if ((r = cg_migrate_recursive("name=systemd", session_path, nosession_path, false)) < 0) + pam_syslog(handle, LOG_ERR, "Failed to migrate session cgroup: %s", strerror(-r)); + } + + /* Delete session cgroup */ + if (r < 0) + pam_syslog(handle, LOG_INFO, "Couldn't empty session cgroup, not deleting."); + else { + if ((r = cg_delete("name=systemd", session_path)) < 0) + pam_syslog(handle, LOG_ERR, "Failed to delete session cgroup: %s", strerror(-r)); + } + } + + /* GC user tree */ + cg_trim("name=systemd", user_path, false); + + if ((r = session_remains(handle, user_path)) < 0) + pam_syslog(handle, LOG_ERR, "Failed to determine whether a session remains: %s", strerror(-r)); + + /* Kill user processes not attached to any session */ + if (kill_user && r == 0) { + + /* Kill no-session cgroup */ + if ((r = cg_kill_recursive_and_wait("name=systemd", user_path)) < 0) + pam_syslog(handle, LOG_ERR, "Failed to kill user cgroup: %s", strerror(-r)); + } else { + + if ((r = cg_is_empty_recursive("name=systemd", user_path, true)) < 0) + pam_syslog(handle, LOG_ERR, "Failed to check user cgroup: %s", strerror(-r)); + + /* If we managed to kill somebody, don't cleanup the cgroup. */ + if (r == 0) + r = -EBUSY; + } + + if (r >= 0) { + const char *runtime_dir; + + /* Remove user cgroup */ + if ((r = cg_delete("name=systemd", user_path)) < 0) + pam_syslog(handle, LOG_ERR, "Failed to delete user cgroup: %s", strerror(-r)); + + /* This will migrate us to the /user cgroup. */ + + if ((runtime_dir = pam_getenv(handle, "XDG_RUNTIME_DIR"))) + if ((r = rm_rf(runtime_dir, false, true)) < 0) + pam_syslog(handle, LOG_ERR, "Failed to remove runtime directory: %s", strerror(-r)); + } + + r = PAM_SUCCESS; + +finish: + if (lock_fd >= 0) + close_nointr_nofail(lock_fd); + + free(session_path); + free(nosession_path); + free(user_path); + + return r; +} diff --git a/src/test-cgroup.c b/src/test-cgroup.c new file mode 100644 index 0000000000..389df6d398 --- /dev/null +++ b/src/test-cgroup.c @@ -0,0 +1,84 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include + +#include "cgroup-util.h" +#include "util.h" +#include "log.h" + +int main(int argc, char*argv[]) { + char *path; + + assert_se(cgroup_init() == 0); + + assert_se(cg_create("name=systemd", "/test-a") == 0); + assert_se(cg_create("name=systemd", "/test-a") == 0); + assert_se(cg_create("name=systemd", "/test-b") == 0); + assert_se(cg_create("name=systemd", "/test-b/test-c") == 0); + assert_se(cg_create_and_attach("name=systemd", "/test-b", 0) == 0); + + assert_se(cg_get_by_pid("name=systemd", getpid(), &path) == 0); + assert_se(streq(path, "/test-b")); + free(path); + + assert_se(cg_attach("name=systemd", "/test-a", 0) == 0); + + assert_se(cg_get_by_pid("name=systemd", getpid(), &path) == 0); + assert_se(path_equal(path, "/test-a")); + free(path); + + assert_se(cg_create_and_attach("name=systemd", "/test-b/test-d", 0) == 0); + + assert_se(cg_get_by_pid("name=systemd", getpid(), &path) == 0); + assert_se(path_equal(path, "/test-b/test-d")); + free(path); + + assert_se(cg_get_path("name=systemd", "/test-b/test-d", NULL, &path) == 0); + assert_se(path_equal(path, "/cgroup/systemd/test-b/test-d")); + free(path); + + assert_se(cg_is_empty("name=systemd", "/test-a", false) > 0); + assert_se(cg_is_empty("name=systemd", "/test-b", false) > 0); + assert_se(cg_is_empty_recursive("name=systemd", "/test-a", false) > 0); + assert_se(cg_is_empty_recursive("name=systemd", "/test-b", false) == 0); + + assert_se(cg_kill_recursive("name=systemd", "/test-a", 0, false) == 0); + assert_se(cg_kill_recursive("name=systemd", "/test-b", 0, false) > 0); + + assert_se(cg_migrate_recursive("name=systemd", "/test-b", "/test-a", false) == 0); + + assert_se(cg_is_empty_recursive("name=systemd", "/test-a", false) == 0); + assert_se(cg_is_empty_recursive("name=systemd", "/test-b", false) > 0); + + assert_se(cg_kill_recursive("name=systemd", "/test-a", 0, false) > 0); + assert_se(cg_kill_recursive("name=systemd", "/test-b", 0, false) == 0); + + cg_trim("name=systemd", "/", false); + + assert_se(cg_delete("name=systemd", "/test-b") < 0); + assert_se(cg_delete("name=systemd", "/test-a") == 0); + + return 0; +} diff --git a/src/util.c b/src/util.c index 6fa9dec3a6..11ab074569 100644 --- a/src/util.c +++ b/src/util.c @@ -846,6 +846,28 @@ char *file_in_same_dir(const char *path, const char *filename) { return r; } +int safe_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid) { + struct stat st; + + if (mkdir(path, mode) >= 0) + if (chmod_and_chown(path, mode, uid, gid) < 0) + return -errno; + + if (lstat(path, &st) < 0) + return -errno; + + if ((st.st_mode & 0777) != mode || + st.st_uid != uid || + st.st_gid != gid || + !S_ISDIR(st.st_mode)) { + errno = EEXIST; + return -errno; + } + + return 0; +} + + int mkdir_parents(const char *path, mode_t mode) { const char *p, *e; @@ -2325,6 +2347,18 @@ char* gethostname_malloc(void) { return strdup(u.sysname); } +int getmachineid_malloc(char **b) { + int r; + + assert(b); + + if ((r = read_one_line_file("/var/lib/dbus/machine-id", b)) < 0) + return r; + + strstrip(*b); + return 0; +} + char* getlogname_malloc(void) { uid_t uid; long bufsize; @@ -2361,11 +2395,13 @@ char* getlogname_malloc(void) { return name; } -char *getttyname_malloc(void) { - char path[PATH_MAX], *p; +int getttyname_malloc(char **r) { + char path[PATH_MAX], *p, *c; + + assert(r); if (ttyname_r(STDIN_FILENO, path, sizeof(path)) < 0) - return strdup("unknown"); + return -errno; char_array_0(path); @@ -2373,7 +2409,132 @@ char *getttyname_malloc(void) { if (startswith(path, "/dev/")) p += 5; - return strdup(p); + if (!(c = strdup(p))) + return -ENOMEM; + + *r = c; + return 0; +} + +static int rm_rf_children(int fd, bool only_dirs) { + DIR *d; + int ret = 0; + + assert(fd >= 0); + + /* This returns the first error we run into, but nevertheless + * tries to go on */ + + if (!(d = fdopendir(fd))) { + close_nointr_nofail(fd); + return -errno; + } + + for (;;) { + struct dirent buf, *de; + bool is_dir; + int r; + + if ((r = readdir_r(d, &buf, &de)) != 0) { + if (ret == 0) + ret = -r; + break; + } + + if (!de) + break; + + if (streq(de->d_name, ".") || streq(de->d_name, "..")) + continue; + + if (de->d_type == DT_UNKNOWN) { + struct stat st; + + if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { + if (ret == 0) + ret = -errno; + continue; + } + + is_dir = S_ISDIR(st.st_mode); + } else + is_dir = de->d_type == DT_DIR; + + if (is_dir) { + int subdir_fd; + + if ((subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)) < 0) { + if (ret == 0) + ret = -errno; + continue; + } + + if ((r = rm_rf_children(subdir_fd, only_dirs)) < 0) { + if (ret == 0) + ret = r; + } + + if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) { + if (ret == 0) + ret = -errno; + } + } else if (!only_dirs) { + + if (unlinkat(fd, de->d_name, 0) < 0) { + if (ret == 0) + ret = -errno; + } + } + } + + closedir(d); + + return ret; +} + +int rm_rf(const char *path, bool only_dirs, bool delete_root) { + int fd; + int r; + + assert(path); + + if ((fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)) < 0) { + + if (errno != ENOTDIR) + return -errno; + + if (delete_root && !only_dirs) + if (unlink(path) < 0) + return -errno; + + return 0; + } + + r = rm_rf_children(fd, only_dirs); + + if (delete_root) + if (rmdir(path) < 0) { + if (r == 0) + r = -errno; + } + + return r; +} + +int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { + assert(path); + + /* Under the assumption that we are running privileged we + * first change the access mode and only then hand out + * ownership to avoid a window where access is too open. */ + + if (chmod(path, mode) < 0) + return -errno; + + if (chown(path, uid, gid) < 0) + return -errno; + + return 0; } static const char *const ioprio_class_table[] = { diff --git a/src/util.h b/src/util.h index 9af2ca8ae6..864d98fa63 100644 --- a/src/util.h +++ b/src/util.h @@ -160,6 +160,7 @@ char *delete_chars(char *s, const char *bad); char *truncate_nl(char *s); char *file_in_same_dir(const char *path, const char *filename); +int safe_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid); int mkdir_parents(const char *path, mode_t mode); int mkdir_p(const char *path, mode_t mode); @@ -263,7 +264,12 @@ void sigset_add_many(sigset_t *ss, ...); char* gethostname_malloc(void); char* getlogname_malloc(void); -char *getttyname_malloc(void); +int getttyname_malloc(char **r); +int getmachineid_malloc(char **r); + +int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); + +int rm_rf(const char *path, bool only_dirs, bool delete_root); const char *ioprio_class_to_string(int i); int ioprio_class_from_string(const char *s); diff --git a/src/utmp-wtmp.c b/src/utmp-wtmp.c index d7cda82d5a..45da79c681 100644 --- a/src/utmp-wtmp.c +++ b/src/utmp-wtmp.c @@ -296,12 +296,14 @@ int utmp_wall(const char *message) { time_t t; if (!(hn = gethostname_malloc()) || - !(un = getlogname_malloc()) || - !(tty = getttyname_malloc())) { + !(un = getlogname_malloc())) { r = -ENOMEM; goto finish; } + if ((r = getttyname_malloc(&tty)) < 0) + goto finish; + time(&t); assert_se(ctime_r(&t, date)); delete_chars(date, "\n\r"); diff --git a/systemd.pc.in b/systemd.pc.in new file mode 100644 index 0000000000..3f8ed5c3e6 --- /dev/null +++ b/systemd.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=${prefix} +systemdsystemunitdir=@systemunitdir@ +systemdsessionunitdir=@pkgdatadir@/session +systemdsystemconfdir=@pkgsysconfdir@/system +systemdsessionconfdir=@pkgsysconfdir@/session + +Name: systemd +Description: systemd System and Session Manager +URL: @PACKAGE_URL@ +Version: @PACKAGE_VERSION@ -- cgit v1.2.3-54-g00ecf From f9378423b9758861850748aeb49ae0d3300e56e6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 Jun 2010 00:31:54 +0200 Subject: man: document sd-daemon.[ch] --- .gitignore | 4 + Makefile.am | 36 ++++++- man/sd-daemon.xml | 162 +++++++++++++++++++++++++++++ man/sd_booted.xml | 122 ++++++++++++++++++++++ man/sd_is_fifo.xml | 199 ++++++++++++++++++++++++++++++++++++ man/sd_listen_fds.xml | 178 ++++++++++++++++++++++++++++++++ man/sd_notify.xml | 277 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/sd-daemon.h | 9 +- 8 files changed, 980 insertions(+), 7 deletions(-) create mode 100644 man/sd-daemon.xml create mode 100644 man/sd_booted.xml create mode 100644 man/sd_is_fifo.xml create mode 100644 man/sd_listen_fds.xml create mode 100644 man/sd_notify.xml (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index ed881d7856..7e44eaa1b4 100644 --- a/.gitignore +++ b/.gitignore @@ -16,8 +16,10 @@ systemd-logger systemctl systemadm .dirstamp +*.3 *.5 *.7 +*.8 *.html *~ *.tar.gz @@ -45,4 +47,6 @@ stamp-* Makefile ltmain.sh *.tar.bz2 +*.tar.gz +*.tar.xz libtool diff --git a/Makefile.am b/Makefile.am index f1bc036907..0f9ef8eabc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -307,7 +307,13 @@ EXTRA_DIST += \ dist_man_MANS = \ man/systemd.unit.5 \ man/systemd.service.5 \ - man/daemon.7 + man/daemon.7 \ + man/systemd.8 \ + man/sd_notify.3 \ + man/sd_booted.3 \ + man/sd_listen_fds.3 \ + man/sd_is_fifo.3 \ + man/sd-daemon.7 nodist_man_MANS = \ man/systemd.special.7 @@ -315,7 +321,13 @@ nodist_man_MANS = \ dist_noinst_DATA = \ man/systemd.unit.html \ man/systemd.service.html \ - man/daemon.html + man/daemon.html \ + man/systemd.html \ + man/sd_notify.html \ + man/sd_booted.html \ + man/sd_listen_fds.html \ + man/sd_is_fifo.html \ + man/sd-daemon.html nodist_noinst_DATA = \ man/systemd.special.html @@ -326,7 +338,13 @@ EXTRA_DIST += \ man/systemd.special.xml.in \ man/systemd.special.7.in \ man/systemd.special.html.in \ - man/daemon.xml + man/daemon.xml \ + man/systemd.xml \ + man/sd_notify.xml \ + man/sd_booted.xml \ + man/sd_listen_fds.xml \ + man/sd_is_fifo.xml \ + man/sd-daemon.xml systemd_SOURCES = \ src/main.c @@ -570,6 +588,12 @@ XSLTPROC_PROCESS_HTML_IN = \ $(XSLTPROC) -o ${@:.in=} --nonet http://docbook.sourceforge.net/release/xsl/current/xhtml-1_1/docbook.xsl $< && \ mv ${@:.in=} $@ +man/%.3: man/%.xml + $(XSLTPROC_PROCESS_MAN) + +man/%.3.in: man/%.xml.in + $(XSLTPROC_PROCESS_MAN) + man/%.5: man/%.xml $(XSLTPROC_PROCESS_MAN) @@ -582,6 +606,12 @@ man/%.7: man/%.xml man/%.7.in: man/%.xml.in $(XSLTPROC_PROCESS_MAN_IN) +man/%.8: man/%.xml + $(XSLTPROC_PROCESS_MAN) + +man/%.8.in: man/%.xml.in + $(XSLTPROC_PROCESS_MAN_IN) + man/%.html: man/%.xml $(XSLTPROC_PROCESS_HTML) diff --git a/man/sd-daemon.xml b/man/sd-daemon.xml new file mode 100644 index 0000000000..2acc021041 --- /dev/null +++ b/man/sd-daemon.xml @@ -0,0 +1,162 @@ + + + + + + + + + sd-daemon + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd-daemon + 7 + + + + sd-daemon + Reference implementation of APIs for + new-style daemons + + + + + #include "sd-daemon.h" + + + + + Description + + sd-daemon.c and + sd-daemon.h provide a reference + implementation of various APIs for new-style daemons, + as implemented by the + systemd8 + init system. + + See + sd_listen_fds3, + sd_notify3, + sd_booted3, + sd_is_fifo3 + for more information about the functions + implemented. In addition to these functions a couple + of logging prefixes are defined as macros: + + #define SD_EMERG "<0>" /* system is unusable */ +#define SD_ALERT "<1>" /* action must be taken immediately */ +#define SD_CRIT "<2>" /* critical conditions */ +#define SD_ERR "<3>" /* error conditions */ +#define SD_WARNING "<4>" /* warning conditions */ +#define SD_NOTICE "<5>" /* normal but significant condition */ +#define SD_INFO "<6>" /* informational */ +#define SD_DEBUG "<7>" /* debug-level messages */ + + These prefixes are intended to be used in + conjunction with STDERR-based logging as implemented + by systemd. If a systemd service definition file is + configured with StandardError=syslog + or StandardError=kmsg these + prefixes can be used to encode a log level in lines + printed. This is similar to the kernel + printk()-style logging. See + klogctl2 + for more information. + + The log levels are identical to + syslog3's + log level system. To use these prefixes simply prefix + every line with one of these strings. A line that is + not prefixed will be logged at the default log level + SD_INFO. + + + Hello World + + A daemon may log with the log level + NOTICE by issuing this call: + + fprintf(stderr, SD_NOTICE "Hello World!\n"); + + + + + Notes + + These interfaces are provided by the reference + implementation of APIs for new-style daemons and + distributed with the systemd package. The algorithms + they implement are simple, and can easily be + reimplemented in daemons if it is important to support + this interface without using the reference + implementation. See the respective function man pages + for details. + + In addition, for details about the algorithms + check the liberally licensed reference implementation + sources: + + resp. + + These APIs are implemented in the reference + implementation's drop-in + sd-daemon.c and + sd-daemon.h files. It is + recommended that applications consuming these APIs copy + the implementation into their source tree, either + verbatim or in excerpts. These interfaces are + currently not available in a dynamic library. + + The functions directly related to new-style + daemons become NOPs when -DDISABLE_SYSTEMD is set + during compilation. In addition, if + sd-daemon.c is compiled on + non-Linux systems they become NOPs, too. + + + + See Also + + sd_listen_fds3, + sd_notify3, + sd_booted3, + sd_is_fifo3, + daemon7, + systemd8, + systemd.service5, + systemd.socket5, + fprintf3 + + + + diff --git a/man/sd_booted.xml b/man/sd_booted.xml new file mode 100644 index 0000000000..6129a777c2 --- /dev/null +++ b/man/sd_booted.xml @@ -0,0 +1,122 @@ + + + + + + + + + sd_booted + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_booted + 3 + + + + sd_booted + Test whether the system is running the systemd init system. + + + + + #include "sd-daemon.h" + + + int sd_booted + void + + + + + + Description + sd_booted() checks whether + the system was booted up using the systemd init system. + + + + Return Value + + On failure, this call returns a negative + errno-style error code. If the system was booted up + with systemd as init system this call returns a + postive return value, zero otherwise. + + + + Notes + + This function is provided by the reference + implementation of APIs for new-style daemons and + distributed with the systemd package. The algorithm it + implements is simple, and can easily be reimplemented + in daemons if it is important to support this + interface without using the reference + implementation. + + Internally, this function checks whether the + /cgroup/systemd virtual file + system is mounted, by comparing the st_dev value of + the stat() data of + /cgroup and + /cgroup/systemd. + + For details about the algorithm check the + liberally licensed reference implementation sources: + + resp. + + sd_booted() is implemented + in the reference implementation's drop-in + sd-daemon.c and + sd-daemon.h files. It is + recommended that applications consuming these APIs + copy the implementation into their source tree. For + more details about the reference implementation see + sd_daemon7 + + If -DDISABLE_SYSTEMD is set during compilation + this function will always return 0 and otherwise + become a NOP. + + + + See Also + + sd_daemon7, + systemd8 + + + + diff --git a/man/sd_is_fifo.xml b/man/sd_is_fifo.xml new file mode 100644 index 0000000000..2389546130 --- /dev/null +++ b/man/sd_is_fifo.xml @@ -0,0 +1,199 @@ + + + + + + + + + sd_is_fifo + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_is_fifo + 3 + + + + sd_is_fifo + sd_is_socket + sd_is_socket_inet + sd_is_socket_unix + Check the type of a file descriptor + + + + + #include "sd-daemon.h" + + + int sd_is_fifo + int fd + const char *path + + + + int sd_is_socket + int fd + int family + int type + int listening + + + + int sd_is_socket_inet + int fd + int family + int type + int listening + uint16_t port + + + + int sd_is_socket_unix + int fd + int type + int listening + const char* path + size_t length + + + + + + + Description + + sd_is_fifo() may be called + to check whether the specified file descriptor refers + to a FIFO or pipe. It the path + parameter is not NULL it is checked whether the FIFO + is bound to the specified file system path. + + sd_is_socket() may be + called to check whether the specified file descriptor + refers to a socket. It the + family parameter is not + AF_UNSPEC it is checked whether the socket is of the + specified family (AF_UNIX, AF_INET, ...). If the + type parameter is not 0 it is + checked whether the socket is of the specified type + (SOCK_STREAM, SOCK_DGRAM, ...). If the + listening parameter is positive + it is checked whether the socket is in accepting mode, + i.e. listen() has been called for + it. If listening is 0, it is + checked whether the socket is not in this mode. If the + parameter is negative, no such check is made. The + listening parameter should only + be used for stream sockets and should be set to a + negative value otherwise. + + sd_is_socket_inet() is + similar to sd_is_socket(), but + optionally checks the IPv4 or IPv6 port number the + socket is bound to, unless port + is zero. For this call family + must be passed as either AF_UNSPEC, AF_INET or + AF_INET6. + + sd_is_socket_unix() is + similar to sd_is_socket(), but + optionally checks the AF_UNIX path the socket is bound + to, unless the path parameter + is NULL. For normal file system AF_UNIX sockets set + the length parameter to 0. For + Linux abstract namespace sockets set the + length to the size of the + address, including the initial 0 byte and set + path to the initial 0 byte of + the socket address. + + + + Return Value + + On failure, these calls return a negative + errno-style error code. If the file descriptor is of + the specified type and bound to the specified address + a positive return value is returned, otherwise + zero. + + + + Notes + + These functions are provided by the reference + implementation of APIs for new-style daemons and + distributed with the systemd package. The algorithms + they implement are simple, and can easily be + reimplemented in daemons if it is important to support + this interface without using the reference + implementation. + + Internally, these function use a combination of + fstat() and + getsockname() to check the file + descriptor type and where it is bound to. + + For details about the algorithm check the + liberally licensed reference implementation sources: + + resp. + + sd_is_fifo() and the + related functions are implemented in the reference + implementation's drop-in + sd-daemon.c and + sd-daemon.h files. It is + recommended that applications consuming these APIs + copy the implementation into their source tree. For + more details about the reference implementation see + sd_daemon7 + + These functions continue to work as described, + even if -DDISABLE_SYSTEMD is set during + compilation. + + + + See Also + + sd_daemon7, + sd_listen_fds3, + systemd8, + systemd.service5, + systemd.socket5 + + + + diff --git a/man/sd_listen_fds.xml b/man/sd_listen_fds.xml new file mode 100644 index 0000000000..10ea57c972 --- /dev/null +++ b/man/sd_listen_fds.xml @@ -0,0 +1,178 @@ + + + + + + + + + sd_listen_fds + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_listen_fds + 3 + + + + sd_listen_fds + Check for file descriptors passed by the init system. + + + + + #include "sd-daemon.h" + + #define SD_LISTEN_FDS_START 3 + + + int sd_listen_fds + int unset_environment + + + + + + Description + + sd_listen_fds() shall be + called by a daemon to check for file descriptors + passed by the init system as part of the socket-based + activation logic. + + If the unset_environment + parameter is non-zero + sd_listen_fds() will unset the + $LISTEN_FDS/$LISTEN_PID + environment variables before returning (regardless + whether the function call itself succeeded or + not). Further calls to + sd_listen_fds() will then fail, + but the variables are no longer inherited by child + processes. + + If a daemon receives more than one file + descriptor, they will be passed in the same order as + configured in the systemd socket definition + file. Nonetheless it is recommended to verify the + correct socket types before using them. To simplify + this checking the functions + sd_is_fifo3, + sd_is_socket3, + sd_is_socket_inet3, + sd_is_socket_unix3 + are provided. In order to maximize flexibility it is + recommened to make these checks as loose as possible + without allowing incorrect setups. i.e. often the + actual port number a socket is bound to matters little + for the service to work, hence it should not be + verified. On the other hand, whether a socket is a + datagram or stream socket matters a lot for the most + common program logics and should hence be + checked. + + This function call will set the FD_CLOEXEC flag + for all passed file descriptors to avoid further + inheritance to children of the calling process. + + + + Return Value + + On failure, this call returns a negative + errno-style error code. If + $LISTEN_FDS/$LISTEN_PID + was not set or not correctly set for this daemon and + hence no file descriptors received 0 is + returned. Otherwise the number of file descriptors + passed is returned, the application may find them + starting with file descriptor SD_LISTEN_FDS_START, + i.e. file descriptor 3. + + + + Notes + + This function is provided by the reference + implementation of APIs for new-style daemons and + distributed with the systemd package. The algorithm it + implements is simple, and can easily be reimplemented + in daemons if it is important to support this + interface without using the reference + implementation. + + Internally, this function checks whether the + $LISTEN_PID environment variable + equals the daemon PID. If not, it returns + immediately. Otherwise it parses the number passed in + the $LISTEN_FDS environment + variable, then sets the FD_CLOEXEC flag for the parsed + number of file descriptors starting from + SD_LISTEN_FDS_START. Finally it returns the parsed + number. + + For details about the algorithm check the + liberally licensed reference implementation sources: + + resp. + + sd_listen_fds() is + implemented in the reference implementation's drop-in + sd-daemon.c and + sd-daemon.h files. It is + recommended that applications consuming these APIs + copy the implementation into their source tree. For + more details about the reference implementation see + sd_daemon7 + + If -DDISABLE_SYSTEMD is set during compilation + this function will always return 0 and otherwise + become a NOP. + + + + See Also + + + sd_daemon7, + sd_is_fifo3, + sd_is_socket3, + sd_is_socket_inet3, + sd_is_socket_unix3, + daemon7, + systemd8, + systemd.service5, + systemd.socket5 + + + + diff --git a/man/sd_notify.xml b/man/sd_notify.xml new file mode 100644 index 0000000000..140e795979 --- /dev/null +++ b/man/sd_notify.xml @@ -0,0 +1,277 @@ + + + + + + + + + sd_notify + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + sd_notify + 3 + + + + sd_notify + sd_notifyf + Notify init system about start-up completion and other daemon status changes + + + + + #include "sd-daemon.h" + + + int sd_notify + int unset_environment + const char *state + + + + int sd_notifyf + int unset_environment + const char *format + ... + + + + + + Description + sd_notify() shall be called + by a daemon to notify the init system about status + changes. It can be used to send arbitrary information, + encoded in an environment-block-like string. Most + importantly it can be used for start-up completion + notification. + + If the unset_environment + parameter is non-zero sd_notify() + will unset the $NOTIFY_SOCKET + environment variable before returning (regardless + whether the function call itself succeeded or + not). Further calls to + sd_notify() will then fail, but + the variable is no longer inherited by child + processes. + + The state parameter + should contain an newline-seperated list of variable + assignments, similar in style to an environment + block. A trailing newline is implied if none is + specified. The string may contain any kind of variable + assignments, but the following shall be considered + well-known: + + + + READY=1 + + Tells the init system + that daemon startup is finished. This + is only used by systemd if the service + definition file has Type=notify + set. The passed argument is a boolean + "1" or "0". Since there is little + value in signalling non-readiness the + only value daemons should send is + "READY=1". + + + + STATUS=... + + Passes a single-line + status string back to the init system + that describes the daemon state. This + is free-from and can be used for + various purposes: general state + feedback, fsck-like programs could + pass completion percentages and + failing programs could pass a human + readable error message. Example: + "STATUS=Completed 66% of file system + check..." + + + + ERRNO=... + + If a daemon fails, the + errno-style error code, formatted as + string. Example: "ERRNO=2" for + ENOENT. + + + + BUSERROR=... + + If a daemon fails, the + D-Bus error-style error code. Example: + "BUSERROR=org.freedesktop.DBus.Error.TimedOut" + + + + MAINPID=... + + The main pid of the + daemon, in case the init system did + not fork off the process + itself. Example: + "MAINPID=4711" + + + + It is recommened to prefix variable names that + are not shown in the list above with + X_ to avoid namespace + clashes. + + Note that systemd will accept status data sent + from a daemon only if the + NotifyAccess= option is correctly + set in the service definition file. See + systemd.service5 + for details. + + sd_notifyf() is similar to + sd_notifyf() but takes a + printf()-like format string plus + arguments. + + + + Return Value + + On failure, these calls return a negative + errno-style error code. If + $NOTIFY_SOCKET was not set and + hence no status data could be sent 0 is returned. If + the status was sent these functions return with a + positive return value. In order to support both init + systems that implement this scheme and those which + don't it is generally recommended to ignore the return + value of this call. + + + + Notes + + These functions are provided by the reference + implementation of APIs for new-style daemons and + distributed with the systemd package. The algorithms + they implement are simple, and can easily be + reimplemented in daemons if it is important to support + this interface without using the reference + implementation. + + Internally, these functions send a single + datagram with the state string as payload to the + AF_UNIX socket referenced in the + $NOTIFY_SOCKET environment + variable. If the first character of + $NOTIFY_SOCKET is @ the string is + understood as Linux abstract namespace socket. The + datagram is accompanied by the process credentials of + the sending daemon, using SCM_CREDENTIALS. + + For details about the algorithm check the + liberally licensed reference implementation sources: + + resp. + + sd_notify() and + sd_notifyf() are implemented in + the reference implementation's drop-in + sd-daemon.c and + sd-daemon.h files. It is + recommended that applications consuming these APIs + copy the implementation into their source tree. For + more details about the reference implementation see + sd_daemon7 + + If -DDISABLE_SYSTEMD is set during compilation + this function will always return 0 and otherwise + become a NOP. + + + + Examples + + + Start-up Notification + + When a daemon finished starting up, it + might issue the following call call to notify + the init system: + + sd_notify(0, "READY=1"); + + + + Extended Start-up Notification + + A daemon could send the following after + completing initialization: + + sd_notifyf(0, "READY=1\n" + "STATUS=Processing requests...\n" + "MAINPID=%lu", + (unsigned long) getpid()); + + + + Error Cause Notification + + A daemon could send the following shortly before exiting, on failure + + sd_notifyf(0, "STATUS=Failed to start up: %s\n" + "ERRNO=%i", + strerror(errno), + errno); + + + + + See Also + + sd_daemon7, + daemon7, + systemd8, + systemd.service5 + + + + diff --git a/src/sd-daemon.h b/src/sd-daemon.h index dc18f42eaa..56fec98aa7 100644 --- a/src/sd-daemon.h +++ b/src/sd-daemon.h @@ -76,7 +76,7 @@ extern "C" { /* Log levels for usage on stderr: - fprintf(stderr, SD_NOTICE "Hello World!"); + fprintf(stderr, SD_NOTICE "Hello World!\n"); This is similar to printk() usage in the kernel. */ @@ -158,9 +158,9 @@ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) _sd_hidden_; /* - Informs systemd about changed daemon state. This takes a numeber of + Informs systemd about changed daemon state. This takes a number of newline seperated environment-style variable assignments in a - string. The following strings are known: + string. The following variables are known: READY=1 Tells systemd that daemon startup is finished (only relevant for services of Type=notify). The passed @@ -185,7 +185,8 @@ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t MAINPID=... The main pid of a daemon, in case systemd did not fork off the process itself. Example: "MAINPID=4711" - Daemons can choose to send additional variables. + Daemons can choose to send additional variables. However, it is + recommened to prefix variable names not listed above with X_. Returns a negative errno-style error code on failure. Returns > 0 if systemd could be notified, 0 if it couldn't possibly because -- cgit v1.2.3-54-g00ecf From 359957ee23e8dad97f6c75969d7ecfe4d3c4369f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 Jun 2010 01:49:57 +0200 Subject: man: document runlevel and systemd-install --- .gitignore | 1 + Makefile.am | 18 +++++- man/runlevel.xml | 149 +++++++++++++++++++++++++++++++++++++++++++ man/systemd-install.xml | 165 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 330 insertions(+), 3 deletions(-) create mode 100644 man/runlevel.xml create mode 100644 man/systemd-install.xml (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 7e44eaa1b4..1632e3f196 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ systemd-logger systemctl systemadm .dirstamp +*.1 *.3 *.5 *.7 diff --git a/Makefile.am b/Makefile.am index 0f9ef8eabc..68b1bd14c1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -313,7 +313,9 @@ dist_man_MANS = \ man/sd_booted.3 \ man/sd_listen_fds.3 \ man/sd_is_fifo.3 \ - man/sd-daemon.7 + man/sd-daemon.7 \ + man/runlevel.8 \ + man/systemd-install.1 nodist_man_MANS = \ man/systemd.special.7 @@ -327,7 +329,9 @@ dist_noinst_DATA = \ man/sd_booted.html \ man/sd_listen_fds.html \ man/sd_is_fifo.html \ - man/sd-daemon.html + man/sd-daemon.html \ + man/runlevel.html \ + man/systemd-install.html nodist_noinst_DATA = \ man/systemd.special.html @@ -344,7 +348,9 @@ EXTRA_DIST += \ man/sd_booted.xml \ man/sd_listen_fds.xml \ man/sd_is_fifo.xml \ - man/sd-daemon.xml + man/sd-daemon.xml \ + man/runlevel.xml \ + man/systemd-install.xml systemd_SOURCES = \ src/main.c @@ -594,6 +600,12 @@ man/%.3: man/%.xml man/%.3.in: man/%.xml.in $(XSLTPROC_PROCESS_MAN) +man/%.1: man/%.xml + $(XSLTPROC_PROCESS_MAN) + +man/%.1.in: man/%.xml.in + $(XSLTPROC_PROCESS_MAN) + man/%.5: man/%.xml $(XSLTPROC_PROCESS_MAN) diff --git a/man/runlevel.xml b/man/runlevel.xml new file mode 100644 index 0000000000..1404ff6d2f --- /dev/null +++ b/man/runlevel.xml @@ -0,0 +1,149 @@ + + + + + + + + + runlevel + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + runlevel + 8 + + + + runlevel + Print previous and current SysV runlevel + + + + + runlevel options + + + + + Description + + runlevel prints the previous + and current SysV runlevel if they are known. + + The two runlevel characters are seperated by a + single space character. If a runlevel cannot be + determined N is printed instead. If neither can be + determined the word "unknown" is printed. + + Unless overriden in the environment this will + check the utmp database for recent runlevel + changes. + + The following option is understood: + + + + --help + + Prints a short help + text and exits. + + + + + + + Exit status + + If one or both runlevels could be determined, 0 + is returned, a non-zero failure code otherwise. + + + + + Environment + + + + $RUNLEVEL + + If + $RUNLEVEL is set, + runlevel will print + this value as current runlevel and + ignore utmp. + + + + $PREVLEVEL + + If + $PREVLEVEL is set + runlevel will print + this value as previous runlevel and + ignore utmp. + + + + + + Files + + + + /var/run/utmp + + The utmp database + runlevel reads the + previous and current runlevel + from. + + + + + + + Notes + + This is a legacy call available for compatbility + only. It should not be used anymore, as the concept of + runlevels is obselete. + + + + See Also + + systemctl1, + + + + diff --git a/man/systemd-install.xml b/man/systemd-install.xml new file mode 100644 index 0000000000..9881e9819e --- /dev/null +++ b/man/systemd-install.xml @@ -0,0 +1,165 @@ + + + + + + + + + systemd-install + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-install + 1 + + + + systemd-install + Enable or disable a systemd unit + definition file + + + + + systemd-install options name + + + systemd-install options name + + + systemd-install options name + + + + + Description + + systemd-install enables or + disables systemd units, or checks whether they are + enabled. + + The following options are understood: + + + + --help + + Prints a short help + text and exits. + + + + --force + + Enable/disable a + service even if it conflicts + with/contradicts another service. This + might have the effect of disabling + another service that was + enabled. + + + + --system + + Enable/disable a + system service. + + + + --session + + Enable/disable a + session service for the calling + user. + + + + --global + + Enable/disable a + session service for all + users. + + + + The following verbs are understood: + + + + enable + + Enable a unit. This + will create a number of symlinks as + encoded in the [Install] section of a + unit file. + + + + disable + + Disable a unit. This + will removed a number of symlinks as + encoded in the [Install] section of a + unit file. + + + + test + + Checks whether any of + the units specified are + installed. This will check for the + existance of a number of symlinks as + encoded in the [Install] section of a + unit file. + + + + + + + Exit status + + On success 0 is returned, a non-zero failure + code otherwise. + + + + See Also + + systemctl1, + systemd.unit5 + + + + -- cgit v1.2.3-54-g00ecf From fa776d8e962da9d90459e2f3e86a2a0c6366ee12 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 8 Jul 2010 21:01:42 +0200 Subject: cgls: beef up control group dumping and introduce cgls tool --- .gitignore | 1 + Makefile.am | 14 ++++- man/systemd-cgls.xml | 106 +++++++++++++++++++++++++++++++ src/cgroup-show.c | 172 ++++++++++++++++++++++++++++++++++++++++++++------- src/cgroup-show.h | 3 +- src/systemctl.c | 28 +-------- src/systemd-cgls.c | 121 ++++++++++++++++++++++++++++++++++++ src/util.c | 27 ++++++++ src/util.h | 2 + 9 files changed, 424 insertions(+), 50 deletions(-) create mode 100644 man/systemd-cgls.xml create mode 100644 src/systemd-cgls.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 1632e3f196..98b13dd7e3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-cgls systemd.pc test-cgroup .libs/ diff --git a/Makefile.am b/Makefile.am index eb911fc896..debe475967 100644 --- a/Makefile.am +++ b/Makefile.am @@ -58,7 +58,8 @@ rootbin_PROGRAMS = \ systemd-notify bin_PROGRAMS = \ - systemd-install + systemd-install \ + systemd-cgls if HAVE_GTK bin_PROGRAMS += \ @@ -320,6 +321,7 @@ MANPAGES = \ man/systemctl.1 \ man/systemadm.1 \ man/systemd-install.1 \ + man/systemd-cgls.1 \ man/systemd-notify.1 \ man/sd_notify.3 \ man/sd_booted.3 \ @@ -511,6 +513,16 @@ systemd_install_CFLAGS = \ $(AM_CFLAGS) \ $(DBUS_CFLAGS) +systemd_cgls_SOURCES = \ + src/systemd-cgls.c \ + src/cgroup-show.c + +systemd_cgls_LDADD = \ + libsystemd-basic.la + +systemd_cgls_CFLAGS = \ + $(AM_CFLAGS) + systemadm_SOURCES = \ src/systemadm.vala \ src/systemd-interfaces.vala diff --git a/man/systemd-cgls.xml b/man/systemd-cgls.xml new file mode 100644 index 0000000000..da3e0cac73 --- /dev/null +++ b/man/systemd-cgls.xml @@ -0,0 +1,106 @@ + + + + + + + + + systemd-cgls + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-cgls + 1 + + + + systemd-cgls + Recursively show control group contents + + + + + systemd-cgls OPTIONS CGROUP + + + + + Description + + systemd-cgls recursively + shows the contents of the selected Linux control group + hierarchy in a tree. If arguments are specified shows + all member processes of the specified control groups + plus all their subgroups and their members. The + control groups may either be specified by their full + file paths or are assumed in the systemd control group + hierarchy. If no argument is specified and the current + working directory is beneath the control group mount + point /cgroup shows the contents + of the control group the working directory refers + to. Otherwise the full systemd control group hierarchy + is shown. + + + + Options + + The following options are understood: + + + + + + Prints a short help + text and exits. + + + + + + + + Exit status + + On success 0 is returned, a non-zero failure + code otherwise. + + + + See Also + + systemd1, + systemctl1 + + + + diff --git a/src/cgroup-show.c b/src/cgroup-show.c index b637c1c163..b44180e1fb 100644 --- a/src/cgroup-show.c +++ b/src/cgroup-show.c @@ -21,6 +21,8 @@ #include #include +#include +#include #include "util.h" #include "macro.h" @@ -36,25 +38,57 @@ static int compare(const void *a, const void *b) { return 0; } -void show_cgroup(const char *name, const char *prefix, unsigned columns) { +static char *get_cgroup_path(const char *name) { + + if (startswith(name, "name=systemd:")) + name += 13; + + if (path_startswith(name, "/cgroup")) + return strdup(name); + + return strappend("/cgroup/systemd/", name); +} + +static unsigned ilog10(unsigned long ul) { + int n = 0; + + while (ul > 0) { + n++; + ul /= 10; + } + + return n; +} + +static int show_cgroup_full(const char *name, const char *prefix, unsigned n_columns, bool more) { char *fn; FILE *f; size_t n = 0, n_allocated = 0; pid_t *pids = NULL; + char *p; + int r; + unsigned long biggest = 0; - if (!startswith(name, "name=systemd:")) - return; + if (n_columns <= 0) + n_columns = columns(); - if (asprintf(&fn, "/cgroup/systemd/%s/cgroup.procs", name + 13) < 0) { - log_error("Out of memory"); - return; - } + if (!prefix) + prefix = ""; + + if (!(p = get_cgroup_path(name))) + return -ENOMEM; - f = fopen(fn, "r"); + r = asprintf(&fn, "%s/cgroup.procs", p); + free(p); + + if (r < 0) + return -ENOMEM; + + f = fopen(fn, "re"); free(fn); if (!f) - return; + return -errno; while (!feof(f)) { unsigned long ul; @@ -71,7 +105,7 @@ void show_cgroup(const char *name, const char *prefix, unsigned columns) { n_allocated = MAX(16U, n*2U); if (!(npids = realloc(pids, sizeof(pid_t) * n_allocated))) { - log_error("Out of memory"); + r = -ENOMEM; goto finish; } @@ -80,6 +114,9 @@ void show_cgroup(const char *name, const char *prefix, unsigned columns) { assert(n < n_allocated); pids[n++] = (pid_t) ul; + + if (ul > biggest) + biggest = ul; } if (n > 0) { @@ -102,33 +139,124 @@ void show_cgroup(const char *name, const char *prefix, unsigned columns) { /* And sort */ qsort(pids, n, sizeof(pid_t), compare); - if (!prefix) - prefix = ""; - - if (columns > 8) - columns -= 8; + if (n_columns > 8) + n_columns -= 8; else - columns = 20; - - printf("%s\342\224\202\n", prefix); + n_columns = 20; for (i = 0; i < n; i++) { char *t = NULL; - get_process_cmdline(pids[i], columns, &t); + get_process_cmdline(pids[i], n_columns, &t); - printf("%s%s %5lu %s\n", + printf("%s%s %*lu %s\n", prefix, - i < n-1 ? "\342\224\234" : "\342\224\224", - (unsigned long) pids[i], strna(t)); + (more || i < n-1) ? "\342\224\234" : "\342\224\224", + ilog10(biggest), + (unsigned long) pids[i], + strna(t)); free(t); } } + r = 0; + finish: free(pids); if (f) fclose(f); + + return r; +} + +int show_cgroup(const char *name, const char *prefix, unsigned n_columns) { + return show_cgroup_full(name, prefix, n_columns, false); +} + +int show_cgroup_recursive(const char *name, const char *prefix, unsigned n_columns) { + DIR *d; + char *last = NULL; + char *p1 = NULL, *p2 = NULL, *fn = NULL; + struct dirent *de; + bool shown_pids = false; + int r; + + assert(name); + + if (n_columns <= 0) + n_columns = columns(); + + if (!prefix) + prefix = ""; + + if (!(fn = get_cgroup_path(name))) + return -ENOMEM; + + if (!(d = opendir(fn))) { + free(fn); + return -errno; + } + + while ((de = readdir(d))) { + + if (de->d_type != DT_DIR) + continue; + + if (ignore_file(de->d_name)) + continue; + + if (!shown_pids) { + show_cgroup_full(name, prefix, n_columns, true); + shown_pids = true; + } + + if (last) { + printf("%s\342\224\234 %s\n", prefix, file_name_from_path(last)); + + if (!p1) + if (!(p1 = strappend(prefix, "\342\224\202 "))) { + r = -ENOMEM; + goto finish; + } + + show_cgroup_recursive(last, p1, n_columns-2); + + free(last); + last = NULL; + } + + if (asprintf(&last, "%s/%s", name, de->d_name) < 0) { + log_error("Out of memory"); + goto finish; + } + } + + if (!shown_pids) + show_cgroup_full(name, prefix, n_columns, !!last); + + if (last) { + printf("%s\342\224\224 %s\n", prefix, file_name_from_path(last)); + + if (!p2) + if (!(p2 = strappend(prefix, " "))) { + r = -ENOMEM; + goto finish; + } + + show_cgroup_recursive(last, p2, n_columns-2); + } + + r = 0; + +finish: + free(p1); + free(p2); + free(last); + free(fn); + + closedir(d); + + return r; } diff --git a/src/cgroup-show.h b/src/cgroup-show.h index 3def2f3095..1f0c319054 100644 --- a/src/cgroup-show.h +++ b/src/cgroup-show.h @@ -22,6 +22,7 @@ along with systemd; If not, see . ***/ -void show_cgroup(const char *name, const char *prefix, unsigned columns); +int show_cgroup(const char *name, const char *prefix, unsigned columns); +int show_cgroup_recursive(const char *name, const char *prefix, unsigned columns); #endif diff --git a/src/systemctl.c b/src/systemctl.c index bb831adb5c..a9a975ee81 100644 --- a/src/systemctl.c +++ b/src/systemctl.c @@ -110,30 +110,6 @@ static int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *da return 0; } -static int columns(void) { - static int parsed_columns = 0; - const char *e; - - if (parsed_columns > 0) - return parsed_columns; - - if ((e = getenv("COLUMNS"))) - parsed_columns = atoi(e); - - if (parsed_columns <= 0) { - struct winsize ws; - zero(ws); - - if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) >= 0) - parsed_columns = ws.ws_col; - } - - if (parsed_columns <= 0) - parsed_columns = 80; - - return parsed_columns; -} - static void warn_wall(enum action action) { static const char *table[_ACTION_MAX] = { [ACTION_HALT] = "The system is going down for system halt NOW!", @@ -1076,7 +1052,7 @@ static void print_status_info(UnitStatusInfo *i) { else c = 0; - show_cgroup(i->default_control_group, "\t\t ", c); + show_cgroup_recursive(i->default_control_group, "\t\t ", c); } } @@ -3068,7 +3044,7 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[]) { { "reboot", EQUAL, 1, start_special }, { "default", EQUAL, 1, start_special }, { "rescue", EQUAL, 1, start_special }, - { "emergency", EQUAL, 1, start_special }, + { "emergency", EQUAL, 1, start_special } }; int left; diff --git a/src/systemd-cgls.c b/src/systemd-cgls.c new file mode 100644 index 0000000000..6614f0fd25 --- /dev/null +++ b/src/systemd-cgls.c @@ -0,0 +1,121 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include "cgroup-show.h" +#include "log.h" +#include "util.h" + +static void help(void) { + + printf("%s [OPTIONS...] [CGROUP...]\n\n" + "Recursively show control group contents.\n\n" + " -h --help Show this help\n", + program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { NULL, 0, NULL, 0 } + }; + + int c; + + assert(argc >= 1); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case '?': + return -EINVAL; + + default: + log_error("Unknown option code %c", c); + return -EINVAL; + } + } + + return 1; +} + +int main(int argc, char *argv[]) { + int r = 0, retval = 1; + + log_parse_environment(); + + if ((r = parse_argv(argc, argv)) < 0) + goto finish; + else if (r == 0) { + retval = 0; + goto finish; + } + + if (optind < argc) { + unsigned i; + + for (i = (unsigned) optind; i < (unsigned) argc; i++) { + int q; + printf("%s:\n", argv[i]); + + if ((q = show_cgroup_recursive(argv[i], NULL, 0)) < 0) + r = q; + } + + } else { + char *p; + + if (!(p = get_current_dir_name())) { + log_error("Cannot determine current working directory: %m"); + goto finish; + } + + if (path_startswith(p, "/cgroup")) { + printf("Working Directory %s:\n", p); + r = show_cgroup_recursive(p, NULL, 0); + } else + r = show_cgroup_recursive("", NULL, 0); + + free(p); + } + + if (r < 0) + log_error("Failed to list cgroup tree: %s", strerror(-r)); + + retval = 0; + +finish: + + return retval; +} diff --git a/src/util.c b/src/util.c index 6cbc7ce477..2e5827f32d 100644 --- a/src/util.c +++ b/src/util.c @@ -609,6 +609,9 @@ int get_process_cmdline(pid_t pid, size_t max_length, char **line) { fclose(f); + if (r[0] == 0) + return get_process_name(pid, line); + *line = r; return 0; } @@ -2798,6 +2801,30 @@ char **replace_env_argv(char **argv, char **env) { return r; } +int columns(void) { + static __thread int parsed_columns = 0; + const char *e; + + if (parsed_columns > 0) + return parsed_columns; + + if ((e = getenv("COLUMNS"))) + parsed_columns = atoi(e); + + if (parsed_columns <= 0) { + struct winsize ws; + zero(ws); + + if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) >= 0) + parsed_columns = ws.ws_col; + } + + if (parsed_columns <= 0) + parsed_columns = 80; + + return parsed_columns; +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", diff --git a/src/util.h b/src/util.h index b29f890b5f..5d51c07e2f 100644 --- a/src/util.h +++ b/src/util.h @@ -328,6 +328,8 @@ void status_vprintf(const char *format, va_list ap); void status_printf(const char *format, ...); void status_welcome(void); +int columns(void); + const char *ioprio_class_to_string(int i); int ioprio_class_from_string(const char *s); -- cgit v1.2.3-54-g00ecf From 50f2a90dae31943c99df7369008f0240a79d1dc0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 21 Jul 2010 20:26:44 +0200 Subject: update fixme --- .gitignore | 1 + fixme | 24 +++++++++++++++--------- 2 files changed, 16 insertions(+), 9 deletions(-) (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 98b13dd7e3..6ff8519805 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +test-env-replace systemd-cgls systemd.pc test-cgroup diff --git a/fixme b/fixme index a0d2db2e35..680d7f63b2 100644 --- a/fixme +++ b/fixme @@ -31,7 +31,7 @@ * follow property change dbus spec -* selinux +* selinux policy loading/socket stuff * systemctl status $PID, systemctl stop $PID! @@ -39,7 +39,7 @@ * vielleicht implizit immer auf syslog dependen? -* debian deadlock when partition auf noauto is. +* debian deadlock when partition set to noauto * fingerprint.target, wireless.target, gps.target @@ -47,11 +47,11 @@ * fix merging in .swap units -* pam: fix double session cleanup - * pahole -* io priority +* chkconfig/systemd-install glue + +* io priority during initialization External: @@ -61,14 +61,20 @@ External: * default logic for serial getty, ck logging, ssh readahead +* pam: fix double sudo session cleanup: + http://www.gratisoft.us/bugzilla/show_bug.cgi?id=421 + * patch /etc/init.d/functions with: if [ $PPID -ne 1 && mountpoint /cgroup/systemd ] ; then echo "You suck!" ; fi -https://bugzilla.redhat.com/show_bug.cgi?id=614245 -https://bugzilla.redhat.com/show_bug.cgi?id=612789 -https://bugzilla.redhat.com/show_bug.cgi?id=612728 -https://bugzilla.redhat.com/show_bug.cgi?id=612712 +https://bugzilla.redhat.com/show_bug.cgi?id=614245 -- plymouth +https://bugzilla.redhat.com/show_bug.cgi?id=612789 -- umount /cgroup on halt +https://bugzilla.redhat.com/show_bug.cgi?id=612728 -- /etc/rc.d/init.d/functions +https://bugzilla.redhat.com/show_bug.cgi?id=612712 -- pam_systemd +https://bugs.freedesktop.org/show_bug.cgi?id=29193 -- accountsservice +https://bugs.freedesktop.org/show_bug.cgi?id=29194 -- ConsoleKit +http://www.spinics.net/lists/linux-nfs/msg14371.html -- rpcbind Regularly: -- cgit v1.2.3-54-g00ecf From 4927fcae48de061393b3ce9c12d49f80d73fbf1d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 11 Aug 2010 01:43:23 +0200 Subject: audit,utmp: implement audit logic and rip utmp stuff out of the main daemon and into a helper binary --- .gitignore | 1 + Makefile.am | 33 ++++- configure.ac | 36 +++++ fixme | 4 +- src/manager.c | 82 +++-------- src/manager.h | 12 +- src/missing.h | 8 ++ src/unit.c | 26 ++-- src/update-utmp.c | 410 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/utmp-wtmp.c | 6 +- 10 files changed, 533 insertions(+), 85 deletions(-) create mode 100644 src/update-utmp.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 6ff8519805..b143ad13d5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-update-utmp test-env-replace systemd-cgls systemd.pc diff --git a/Makefile.am b/Makefile.am index b6041b5d1c..cb2318406d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -68,7 +68,8 @@ endif rootlibexec_PROGRAMS = \ systemd-logger \ systemd-cgroups-agent \ - systemd-initctl + systemd-initctl \ + systemd-update-utmp noinst_PROGRAMS = \ test-engine \ @@ -243,6 +244,13 @@ libsystemd_basic_la_SOURCES = \ src/log.c \ src/ratelimit.c +libsystemd_basic_la_CFLAGS = \ + $(AM_CFLAGS) \ + $(SELINUX_CFLAGS) + +libsystemd_basic_la_LIBADD = \ + $(SELINUX_LIBS) + libsystemd_core_la_SOURCES = \ src/unit.c \ src/job.c \ @@ -282,7 +290,6 @@ libsystemd_core_la_SOURCES = \ src/loopback-setup.c \ src/kmod-setup.c \ src/modprobe-setup.c \ - src/utmp-wtmp.c \ src/specifier.c \ src/unit-name.c \ src/fdset.c \ @@ -293,7 +300,10 @@ libsystemd_core_la_SOURCES = \ libsystemd_core_la_CFLAGS = \ $(AM_CFLAGS) \ $(DBUS_CFLAGS) \ - $(UDEV_CFLAGS) + $(UDEV_CFLAGS) \ + $(LIBWRAP_CFLAGS) \ + $(PAM_CFLAGS) \ + $(AUDIT_CFLAGS) libsystemd_core_la_LIBADD = \ libsystemd-basic.la \ @@ -301,7 +311,7 @@ libsystemd_core_la_LIBADD = \ $(UDEV_LIBS) \ $(LIBWRAP_LIBS) \ $(PAM_LIBS) \ - $(SELINUX_LIBS) + $(AUDIT_LIBS) # This is needed because automake is buggy in how it generates the # rules for C programs, but not Vala programs. We therefore can't @@ -480,6 +490,21 @@ systemd_initctl_LDADD = \ libsystemd-basic.la \ $(DBUS_LIBS) +systemd_update_utmp_SOURCES = \ + src/update-utmp.c \ + src/dbus-common.c \ + src/utmp-wtmp.c + +systemd_update_utmp_CFLAGS = \ + $(AM_CFLAGS) \ + $(DBUS_CFLAGS) \ + $(AUDIT_CFLAGS) + +systemd_update_utmp_LDADD = \ + libsystemd-basic.la \ + $(DBUS_LIBS) \ + $(AUDIT_LIBS) + systemd_cgroups_agent_SOURCES = \ src/cgroups-agent.c \ src/dbus-common.c diff --git a/configure.ac b/configure.ac index f5dba3a86f..72218e4df4 100644 --- a/configure.ac +++ b/configure.ac @@ -186,6 +186,42 @@ fi AC_SUBST(PAM_LIBS) AM_CONDITIONAL([HAVE_PAM], [test "x$have_pam" != xno]) +AC_ARG_ENABLE([audit], + AS_HELP_STRING([--disable-audit],[Disable optional AUDIT support]), + [case "${enableval}" in + yes) have_audit=yes ;; + no) have_audit=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --disable-audit) ;; + esac], + [have_audit=auto]) + +if test "x${have_audit}" != xno ; then + AC_CHECK_HEADERS( + [libaudit.h], + [have_audit=yes], + [if test "x$have_audit" = xyes ; then + AC_MSG_ERROR([*** AUDIT headers not found.]) + fi]) + + AC_CHECK_LIB( + [audit], + [audit_open], + [have_audit=yes], + [if test "x$have_audit" = xyes ; then + AC_MSG_ERROR([*** libaudit not found.]) + fi]) + + if test "x$have_audit" = xyes ; then + AUDIT_LIBS="-laudit" + AC_DEFINE(HAVE_AUDIT, 1, [AUDIT available]) + else + have_audit=no + fi +else + AUDIT_LIBS= +fi +AC_SUBST(AUDIT_LIBS) + have_gtk=no AC_ARG_ENABLE(gtk, AS_HELP_STRING([--disable-gtk], [disable GTK tools])) if test "x$enable_gtk" != "xno"; then diff --git a/fixme b/fixme index 3614c118d7..be771ab025 100644 --- a/fixme +++ b/fixme @@ -79,9 +79,9 @@ * don't show file not found msgs for irrelevant units -* audit +* j->installed issue -* env vars must be replaced by "" +* plymouth boot.log External: diff --git a/src/manager.c b/src/manager.c index ddb253ae4e..25eb4e70be 100644 --- a/src/manager.c +++ b/src/manager.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "manager.h" #include "hashmap.h" @@ -202,6 +203,10 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) { m->exit_code = _MANAGER_EXIT_CODE_INVALID; m->pin_cgroupfs_fd = -1; +#ifdef HAVE_AUDIT + m->audit_fd = -1; +#endif + m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = m->dev_autofs_fd = -1; m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */ @@ -245,6 +250,9 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) { if ((r = bus_init(m)) < 0) goto fail; + if ((m->audit_fd = audit_open()) < 0) + log_error("Failed to connect to audit log: %m"); + *_m = m; return 0; @@ -429,6 +437,11 @@ void manager_free(Manager *m) { if (m->notify_watch.fd >= 0) close_nointr_nofail(m->notify_watch.fd); +#ifdef HAVE_AUDIT + if (m->audit_fd >= 0) + audit_close(m->audit_fd); +#endif + free(m->notify_socket); lookup_paths_free(&m->lookup_paths); @@ -567,10 +580,6 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { m->n_deserializing --; } - /* Now that the initial devices are available, let's see if we - * can write the utmp file */ - manager_write_utmp_reboot(m); - return r; } @@ -2234,70 +2243,25 @@ int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j) { return 0; } -static bool manager_utmp_good(Manager *m) { - int r; - - assert(m); - - if ((r = mount_path_is_mounted(m, _PATH_UTMPX)) <= 0) { - - if (r < 0) - log_warning("Failed to determine whether " _PATH_UTMPX " is mounted: %s", strerror(-r)); - - return false; - } - - return true; -} - -void manager_write_utmp_reboot(Manager *m) { - int r; - - assert(m); - - if (m->utmp_reboot_written) - return; +void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { - if (m->running_as != MANAGER_SYSTEM) - return; +#ifdef HAVE_AUDIT + char *p; - if (!manager_utmp_good(m)) + if (m->audit_fd < 0) return; - if ((r = utmp_put_reboot(m->startup_timestamp.realtime)) < 0) { - - if (r != -ENOENT && r != -EROFS) - log_warning("Failed to write utmp/wtmp: %s", strerror(-r)); - + if (!(p = unit_name_to_prefix_and_instance(u->meta.id))) { + log_error("Failed to allocate unit name for audit message: %s", strerror(ENOMEM)); return; } - m->utmp_reboot_written = true; -} - -void manager_write_utmp_runlevel(Manager *m, Unit *u) { - int runlevel, r; - - assert(m); - assert(u); - - if (u->meta.type != UNIT_TARGET) - return; + if (audit_log_user_comm_message(m->audit_fd, type, "", p, NULL, NULL, NULL, success) < 0) + log_error("Failed to send audit message: %m"); - if (m->running_as != MANAGER_SYSTEM) - return; - - if (!manager_utmp_good(m)) - return; + free(p); +#endif - if ((runlevel = target_get_runlevel(TARGET(u))) <= 0) - return; - - if ((r = utmp_put_runlevel(0, runlevel, 0)) < 0) { - - if (r != -ENOENT && r != -EROFS) - log_warning("Failed to write utmp/wtmp: %s", strerror(-r)); - } } void manager_dispatch_bus_name_owner_changed( diff --git a/src/manager.h b/src/manager.h index a762dbc7c3..c1f787f854 100644 --- a/src/manager.h +++ b/src/manager.h @@ -185,6 +185,11 @@ struct Manager { * file system */ int pin_cgroupfs_fd; + /* Audit fd */ +#ifdef HAVE_AUDIT + int audit_fd; +#endif + /* Flags */ ManagerRunningAs running_as; ManagerExitCode exit_code:4; @@ -193,8 +198,6 @@ struct Manager { bool dispatching_run_queue:1; bool dispatching_dbus_queue:1; - bool utmp_reboot_written:1; - int n_deserializing; bool show_status; @@ -234,9 +237,6 @@ unsigned manager_dispatch_dbus_queue(Manager *m); int manager_loop(Manager *m); -void manager_write_utmp_reboot(Manager *m); -void manager_write_utmp_runlevel(Manager *m, Unit *t); - void manager_dispatch_bus_name_owner_changed(Manager *m, const char *name, const char* old_owner, const char *new_owner); void manager_dispatch_bus_query_pid_done(Manager *m, const char *name, pid_t pid); @@ -251,6 +251,8 @@ bool manager_is_booting_or_shutting_down(Manager *m); void manager_reset_maintenance(Manager *m); +void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success); + const char *manager_running_as_to_string(ManagerRunningAs i); ManagerRunningAs manager_running_as_from_string(const char *s); diff --git a/src/missing.h b/src/missing.h index 5914b01a8e..d6114cc2b2 100644 --- a/src/missing.h +++ b/src/missing.h @@ -63,4 +63,12 @@ static inline int pivot_root(const char *new_root, const char *put_old) { /* static void nss_disable_nscd(void) _weakref_(__nss_disable_nscd); */ +#ifndef AUDIT_SERVICE_START +#define AUDIT_SERVICE_START 1130 /* Service (daemon) start */ +#endif + +#ifndef AUDIT_SERVICE_STOP +#define AUDIT_SERVICE_STOP 1131 /* Service (daemon) stop */ +#endif + #endif diff --git a/src/unit.c b/src/unit.c index 9ccf7a4653..3c4bdec981 100644 --- a/src/unit.c +++ b/src/unit.c @@ -41,6 +41,7 @@ #include "dbus-unit.h" #include "special.h" #include "cgroup-util.h" +#include "missing.h" const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = { [UNIT_SERVICE] = &service_vtable, @@ -1094,12 +1095,11 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) { /* Some names are special */ if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) { - if (unit_has_name(u, SPECIAL_DBUS_SERVICE)) { + if (unit_has_name(u, SPECIAL_DBUS_SERVICE)) /* The bus just might have become available, * hence try to connect to it, if we aren't * yet connected. */ bus_init(u->meta.manager); - } if (unit_has_name(u, SPECIAL_SYSLOG_SERVICE)) /* The syslog daemon just might have become @@ -1107,17 +1107,12 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) { * we aren't yet connected. */ log_open(); - if (u->meta.type == UNIT_MOUNT) - /* Another directory became available, let's - * check if that is enough to write our utmp - * entry. */ - manager_write_utmp_reboot(u->meta.manager); - - if (u->meta.type == UNIT_TARGET) - /* A target got activated, maybe this is a runlevel? */ - manager_write_utmp_runlevel(u->meta.manager, u); + if (u->meta.type == UNIT_SERVICE && + !UNIT_IS_ACTIVE_OR_RELOADING(os)) + /* Write audit record if we have just finished starting up */ + manager_send_unit_audit(u->meta.manager, u, AUDIT_SERVICE_START, 1); - } else if (!UNIT_IS_ACTIVE_OR_RELOADING(ns)) { + } else { if (unit_has_name(u, SPECIAL_SYSLOG_SERVICE)) /* The syslog daemon might just have @@ -1127,6 +1122,13 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) { /* We don't care about D-Bus here, since we'll get an * asynchronous notification for it anyway. */ + + if (u->meta.type == UNIT_SERVICE && + UNIT_IS_INACTIVE_OR_MAINTENANCE(ns) && + !UNIT_IS_INACTIVE_OR_MAINTENANCE(os)) + + /* Write audit record if we have just finished shutting down */ + manager_send_unit_audit(u->meta.manager, u, AUDIT_SERVICE_STOP, ns == UNIT_INACTIVE); } /* Maybe we finished startup and are now ready for being diff --git a/src/update-utmp.c b/src/update-utmp.c new file mode 100644 index 0000000000..e64a819aa4 --- /dev/null +++ b/src/update-utmp.c @@ -0,0 +1,410 @@ +/*-*- Mode: C; c-basic-offset: 8 -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include + +#include "log.h" +#include "macro.h" +#include "util.h" +#include "special.h" +#include "utmp-wtmp.h" +#include "dbus-common.h" + +typedef struct Context { + DBusConnection *bus; +#ifdef HAVE_AUDIT + int audit_fd; +#endif +} Context; + +static usec_t get_startup_time(Context *c) { + const char + *interface = "org.freedesktop.systemd1.Manager", + *property = "StartupTimestamp"; + + DBusError error; + usec_t t = 0; + DBusMessage *m = NULL, *reply = NULL; + DBusMessageIter iter, sub; + + dbus_error_init(&error); + + assert(c); + + if (!(m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.DBus.Properties", + "Get"))) { + log_error("Could not allocate message."); + goto finish; + } + + if (!dbus_message_append_args(m, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_STRING, &property, + DBUS_TYPE_INVALID)) { + log_error("Could not append arguments to message."); + goto finish; + } + + if (!(reply = dbus_connection_send_with_reply_and_block(c->bus, m, -1, &error))) { + log_error("Failed to send command: %s", error.message); + goto finish; + } + + if (!dbus_message_iter_init(reply, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { + log_error("Failed to parse reply."); + goto finish; + } + + dbus_message_iter_recurse(&iter, &sub); + + if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT64) { + log_error("Failed to parse reply."); + goto finish; + } + + dbus_message_iter_get_basic(&sub, &t); + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); + + return t; +} + +static int get_current_runlevel(Context *c) { + static const struct { + const int runlevel; + const char *special; + } table[] = { + /* The first target of this list that is active or has + * a job scheduled wins */ + { '0', SPECIAL_POWEROFF_TARGET }, + { '6', SPECIAL_REBOOT_TARGET }, + { '5', SPECIAL_RUNLEVEL5_TARGET }, + { '4', SPECIAL_RUNLEVEL4_TARGET }, + { '3', SPECIAL_RUNLEVEL3_TARGET }, + { '2', SPECIAL_RUNLEVEL2_TARGET }, + { '1', SPECIAL_RESCUE_TARGET }, + }; + const char + *interface = "org.freedesktop.systemd1.Unit", + *property = "ActiveState"; + + DBusMessage *m = NULL, *reply = NULL; + int r = 0; + unsigned i; + DBusError error; + + assert(c); + + dbus_error_init(&error); + + for (i = 0; i < ELEMENTSOF(table); i++) { + const char *path = NULL, *state; + DBusMessageIter iter, sub; + + if (!(m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetUnit"))) { + log_error("Could not allocate message."); + r = -ENOMEM; + goto finish; + } + + if (!dbus_message_append_args(m, + DBUS_TYPE_STRING, &table[i].special, + DBUS_TYPE_INVALID)) { + log_error("Could not append arguments to message."); + r = -ENOMEM; + goto finish; + } + + if (!(reply = dbus_connection_send_with_reply_and_block(c->bus, m, -1, &error))) { + dbus_error_free(&error); + continue; + } + + if (!dbus_message_get_args(reply, &error, + DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID)) { + log_error("Failed to parse reply: %s", error.message); + r = -EIO; + goto finish; + } + + dbus_message_unref(m); + if (!(m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + path, + "org.freedesktop.DBus.Properties", + "Get"))) { + log_error("Could not allocate message."); + r = -ENOMEM; + goto finish; + } + + if (!dbus_message_append_args(m, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_STRING, &property, + DBUS_TYPE_INVALID)) { + log_error("Could not append arguments to message."); + r = -ENOMEM; + goto finish; + } + + dbus_message_unref(reply); + if (!(reply = dbus_connection_send_with_reply_and_block(c->bus, m, -1, &error))) { + log_error("Failed to send command: %s", error.message); + r = -EIO; + goto finish; + } + + if (!dbus_message_iter_init(reply, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { + log_error("Failed to parse reply."); + r = -EIO; + goto finish; + } + + dbus_message_iter_recurse(&iter, &sub); + + if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) { + log_error("Failed to parse reply."); + r = -EIO; + goto finish; + } + + dbus_message_iter_get_basic(&sub, &state); + + if (streq(state, "active") || streq(state, "reloading")) + r = table[i].runlevel; + + dbus_message_unref(m); + dbus_message_unref(reply); + m = reply = NULL; + + if (r) + break; + } + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); + + return r; +} + +static int on_reboot(Context *c) { + int r = 0, q; + usec_t t; + + assert(c); + + /* We finished start-up, so let's write the utmp + * record and send the audit msg */ + +#ifdef HAVE_AUDIT + if (c->audit_fd >= 0) + if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_BOOT, "", NULL, NULL, NULL, 1) < 0) { + log_error("Failed to send audit message: %m"); + r = -errno; + } +#endif + + /* If this call fails it will return 0, which + * utmp_put_reboot() will then fix to the current time */ + t = get_startup_time(c); + + if ((q = utmp_put_reboot(t)) < 0) { + log_error("Failed to write utmp record: %s", strerror(-q)); + r = q; + } + + return r; +} + +static int on_shutdown(Context *c) { + int r = 0, q; + + assert(c); + + /* We started shut-down, so let's write the utmp + * record and send the audit msg */ + +#ifdef HAVE_AUDIT + if (c->audit_fd >= 0) + if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_SHUTDOWN, "", NULL, NULL, NULL, 1) < 0) { + log_error("Failed to send audit message: %m"); + r = -errno; + } +#endif + + if ((q = utmp_put_shutdown(0)) < 0) { + log_error("Failed to write utmp record: %s", strerror(-q)); + r = q; + } + + return r; +} + +static int on_runlevel(Context *c) { + int r = 0, q, previous, runlevel; + + assert(c); + + /* We finished changing runlevel, so let's write the + * utmp record and send the audit msg */ + + /* First, get last runlevel */ + if ((q = utmp_get_runlevel(&previous, NULL)) < 0) { + + if (q != -ESRCH && q != -ENOENT) { + log_error("Failed to get current runlevel: %s", strerror(-q)); + return q; + } + + /* Hmm, we didn't find any runlevel, that means we + * have been rebooted */ + r = on_reboot(c); + previous = 0; + } + + /* Second get new runlevel */ + if ((runlevel = get_current_runlevel(c)) < 0) + return runlevel; + + if (previous == runlevel) + return 0; + +#ifdef HAVE_AUDIT + if (c->audit_fd >= 0) { + char *s = NULL; + + if (asprintf(&s, "old-level=%c new-level=%c", previous, runlevel) < 0) + return -ENOMEM; + + if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_RUNLEVEL, s, NULL, NULL, NULL, 1) < 0) { + log_error("Failed to send audit message: %m"); + r = -errno; + } + + free(s); + } +#endif + + if ((q = utmp_put_runlevel(0, runlevel, previous)) < 0) { + log_error("Failed to write utmp record: %s", strerror(-q)); + r = q; + } + + return r; +} + +int main(int argc, char *argv[]) { + int r; + DBusError error; + Context c; + + dbus_error_init(&error); + + zero(c); +#ifdef HAVE_AUDIT + c.audit_fd = -1; +#endif + + /* if (getppid() != 1) { */ + /* log_error("This program should be invoked by init only."); */ + /* return 1; */ + /* } */ + + if (argc != 2) { + log_error("This program requires one argument."); + return 1; + } + + log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_parse_environment(); + +#ifdef HAVE_AUDIT + if ((c.audit_fd = audit_open()) < 0) + log_error("Failed to connect to audit log: %m"); +#endif + + if (bus_connect(DBUS_BUS_SYSTEM, &c.bus, NULL, &error) < 0) { + log_error("Failed to get D-Bus connection: %s", error.message); + r = -EIO; + goto finish; + } + + log_info("systemd-update-utmp running as pid %lu", (unsigned long) getpid()); + + if (streq(argv[1], "reboot")) + r = on_reboot(&c); + else if (streq(argv[1], "shutdown")) + r = on_shutdown(&c); + else if (streq(argv[1], "runlevel")) + r = on_runlevel(&c); + else { + log_error("Unknown command %s", argv[1]); + r = -EINVAL; + } + + log_info("systemd-update-utmp stopped as pid %lu", (unsigned long) getpid()); +finish: + +#ifdef HAVE_AUDIT + if (c.audit_fd >= 0) + audit_close(c.audit_fd); +#endif + + if (c.bus) { + dbus_connection_close(c.bus); + dbus_connection_unref(c.bus); + } + + dbus_error_free(&error); + dbus_shutdown(); + + return r < 0 ? 1 : 0; +} diff --git a/src/utmp-wtmp.c b/src/utmp-wtmp.c index 45da79c681..46dfba336a 100644 --- a/src/utmp-wtmp.c +++ b/src/utmp-wtmp.c @@ -202,11 +202,11 @@ int utmp_put_runlevel(usec_t t, int runlevel, int previous) { previous = 0; } - - if (previous == runlevel) - return 0; } + if (previous == runlevel) + return 0; + init_entry(&store, t); store.ut_type = RUN_LVL; -- cgit v1.2.3-54-g00ecf From 6e200d55ae538fc29360cdaa9863f30cdddf58f3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 14 Aug 2010 21:23:26 +0200 Subject: random-seed: add missing random-seed.c file --- .gitignore | 1 + src/random-seed.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ units/.gitignore | 2 + 3 files changed, 115 insertions(+) create mode 100644 src/random-seed.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index b143ad13d5..f01db0a878 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-random-seed systemd-update-utmp test-env-replace systemd-cgls diff --git a/src/random-seed.c b/src/random-seed.c new file mode 100644 index 0000000000..91aa6c6b4e --- /dev/null +++ b/src/random-seed.c @@ -0,0 +1,112 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "log.h" +#include "util.h" + +int main(int argc, char *argv[]) { + int seed_fd = -1, random_fd = -1; + int ret = 1; + uint8_t buf[512]; + ssize_t r; + + if (argc != 2) { + log_error("This program requires one argument."); + return 1; + } + + log_parse_environment(); + + /* When we load the seed we read it and write it to the device + * and then immediately update the saved seed with new data, + * to make sure the next boot gets seeded differently. */ + + if (streq(argv[1], "load")) { + + if ((seed_fd = open(RANDOM_SEED, O_RDWR|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600)) < 0) { + if ((seed_fd = open(RANDOM_SEED, O_RDONLY|O_CLOEXEC|O_NOCTTY)) < 0) { + log_error("Failed to open random seed: %m"); + goto finish; + } + } + + if ((random_fd = open("/dev/urandom", O_RDWR|O_CLOEXEC|O_NOCTTY, 0600)) < 0) { + if ((random_fd = open("/dev/urandom", O_WRONLY|O_CLOEXEC|O_NOCTTY, 0600)) < 0) { + log_error("Failed to open /dev/urandom: %m"); + goto finish; + } + } + + if ((r = loop_read(seed_fd, buf, sizeof(buf), false)) != sizeof(buf)) + log_error("Failed to read seed file: %s", r < 0 ? strerror(errno) : "EOF"); + else { + lseek(seed_fd, 0, SEEK_SET); + + if ((r = loop_write(random_fd, buf, sizeof(buf), false)) != sizeof(buf)) + log_error("Failed to write seed to /dev/random: %s", r < 0 ? strerror(errno) : "short write"); + } + + } else if (streq(argv[1], "save")) { + + if ((seed_fd = open(RANDOM_SEED, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600)) < 0) { + log_error("Failed to open random seed: %m"); + goto finish; + } + + if ((random_fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY)) < 0) { + log_error("Failed to open /dev/urandom: %m"); + goto finish; + } + } else { + log_error("Unknown verb %s.", argv[1]); + goto finish; + } + + /* This is just a safety measure. Given that we are root and + * most likely created the file ourselves the mode and owner + * should be correct anyway. */ + fchmod(seed_fd, 0600); + fchown(seed_fd, 0, 0); + + if ((r = loop_read(random_fd, buf, sizeof(buf), false)) != sizeof(buf)) + log_error("Failed to read new seed from /dev/urandom: %s", r < 0 ? strerror(errno) : "EOF"); + else { + if ((r = loop_write(seed_fd, buf, sizeof(buf), false)) != sizeof(buf)) + log_error("Failed to write new random seed file: %s", r < 0 ? strerror(errno) : "short write"); + } + + ret = 0; + +finish: + if (random_fd >= 0) + close_nointr_nofail(random_fd); + + if (seed_fd >= 0) + close_nointr_nofail(seed_fd); + + return ret; +} diff --git a/units/.gitignore b/units/.gitignore index 0c3dc17fe8..0318e4825d 100644 --- a/units/.gitignore +++ b/units/.gitignore @@ -1,3 +1,5 @@ +systemd-random-seed-load.service +systemd-random-seed-save.service systemd-initctl.service systemd-logger.service syslog.target -- cgit v1.2.3-54-g00ecf From f61448083198dc0e4e0d19a916bcd478336cc85d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 16 Aug 2010 15:37:52 +0200 Subject: systemctl: add support for delayed shutdown, similar to sysv in style --- .gitignore | 1 + Makefile.am | 19 ++- src/manager.c | 2 +- src/shutdownd.c | 272 +++++++++++++++++++++++++++++++++++++ src/shutdownd.h | 35 +++++ src/socket-util.h | 16 ++- src/systemctl.c | 160 +++++++++++++++++++++- src/util.c | 11 ++ src/util.h | 2 + units/.gitignore | 1 + units/systemd-initctl.socket | 1 - units/systemd-logger.service.in | 1 - units/systemd-logger.socket | 3 - units/systemd-shutdownd.service.in | 15 ++ units/systemd-shutdownd.socket | 16 +++ 15 files changed, 534 insertions(+), 21 deletions(-) create mode 100644 src/shutdownd.c create mode 100644 src/shutdownd.h create mode 100644 units/systemd-shutdownd.service.in create mode 100644 units/systemd-shutdownd.socket (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index f01db0a878..6b8edf5ba9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-shutdownd systemd-random-seed systemd-update-utmp test-env-replace diff --git a/Makefile.am b/Makefile.am index 384778ee60..07204a9f0f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -71,7 +71,8 @@ rootlibexec_PROGRAMS = \ systemd-cgroups-agent \ systemd-initctl \ systemd-update-utmp \ - systemd-random-seed + systemd-random-seed \ + systemd-shutdownd noinst_PROGRAMS = \ test-engine \ @@ -137,6 +138,7 @@ dist_systemunit_DATA = \ units/dbus.target \ units/systemd-initctl.socket \ units/systemd-logger.socket \ + units/systemd-shutdownd.socket \ units/dev-hugepages.automount \ units/dev-hugepages.mount \ units/dev-mqueue.automount \ @@ -161,6 +163,7 @@ nodist_systemunit_DATA = \ units/multi-user.target \ units/systemd-initctl.service \ units/systemd-logger.service \ + units/systemd-shutdownd.service \ units/systemd-update-utmp-runlevel.service \ units/systemd-update-utmp-shutdown.service \ units/systemd-random-seed-save.service \ @@ -182,6 +185,7 @@ EXTRA_DIST = \ units/remote-fs.target.m4 \ units/systemd-initctl.service.in \ units/systemd-logger.service.in \ + units/systemd-shutdownd.service.in \ units/systemd-update-utmp-runlevel.service.in \ units/systemd-update-utmp-shutdown.service.in \ units/systemd-random-seed-save.service.in \ @@ -346,7 +350,8 @@ EXTRA_DIST += \ src/bus-errors.h \ src/cgroup-show.h \ src/utmp-wtmp.h \ - src/build.h + src/build.h \ + src/shutdownd.h MANPAGES = \ man/systemd.1 \ @@ -529,6 +534,16 @@ systemd_random_seed_CFLAGS = \ systemd_random_seed_LDADD = \ libsystemd-basic.la +systemd_shutdownd_SOURCES = \ + src/sd-daemon.c \ + src/shutdownd.c + +systemd_shutdownd_CFLAGS = \ + $(AM_CFLAGS) + +systemd_shutdownd_LDADD = \ + libsystemd-basic.la + systemd_cgroups_agent_SOURCES = \ src/cgroups-agent.c \ src/dbus-common.c diff --git a/src/manager.c b/src/manager.c index 4e8ddfb235..c8fdbb5dee 100644 --- a/src/manager.c +++ b/src/manager.c @@ -1786,7 +1786,7 @@ static int manager_process_notify_fd(Manager *m) { if (n >= 0) return -EIO; - if (errno == EAGAIN) + if (errno == EAGAIN || errno == EINTR) break; return -errno; diff --git a/src/shutdownd.c b/src/shutdownd.c new file mode 100644 index 0000000000..241c4327a6 --- /dev/null +++ b/src/shutdownd.c @@ -0,0 +1,272 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "shutdownd.h" +#include "log.h" +#include "macro.h" +#include "util.h" +#include "sd-daemon.h" + +static int read_packet(int fd, struct shutdownd_command *_c) { + struct msghdr msghdr; + struct iovec iovec; + struct ucred *ucred; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; + } control; + struct shutdownd_command c; + ssize_t n; + + assert(fd >= 0); + assert(_c); + + zero(iovec); + iovec.iov_base = &c; + iovec.iov_len = sizeof(c); + + zero(control); + zero(msghdr); + msghdr.msg_iov = &iovec; + msghdr.msg_iovlen = 1; + msghdr.msg_control = &control; + msghdr.msg_controllen = sizeof(control); + + if ((n = recvmsg(fd, &msghdr, MSG_DONTWAIT)) <= 0) { + if (n >= 0) { + log_error("Short read"); + return -EIO; + } + + if (errno == EAGAIN || errno == EINTR) + return 0; + + log_error("recvmsg(): %m"); + return -errno; + } + + if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) || + control.cmsghdr.cmsg_level != SOL_SOCKET || + control.cmsghdr.cmsg_type != SCM_CREDENTIALS || + control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) { + log_warning("Received message without credentials. Ignoring."); + return 0; + } + + ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr); + if (ucred->uid != 0) { + log_warning("Got request from unprivileged user. Ignoring."); + return 0; + } + + if (n != sizeof(c)) { + log_warning("Message has invaliud size. Ignoring"); + return 0; + } + + *_c = c; + return 1; +} + +int main(int argc, char *argv[]) { + enum { + FD_SOCKET, + FD_SHUTDOWN_TIMER, + FD_NOLOGIN_TIMER, + _FD_MAX + }; + + int r = 4, n; + int one = 1; + unsigned n_fds = 1; + struct shutdownd_command c; + struct pollfd pollfd[_FD_MAX]; + bool exec_shutdown = false, unlink_nologin = false; + + if (getppid() != 1) { + log_error("This program should be invoked by init only."); + return 1; + } + + if (argc > 1) { + log_error("This program does not take arguments."); + return 1; + } + + log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_parse_environment(); + + if ((n = sd_listen_fds(true)) < 0) { + log_error("Failed to read listening file descriptors from environment: %s", strerror(-r)); + return 1; + } + + if (n != 1) { + log_error("Need exactly one file descriptor."); + return 2; + } + + if (setsockopt(SD_LISTEN_FDS_START, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) { + log_error("SO_PASSCRED failed: %m"); + return 3; + } + + zero(c); + zero(pollfd); + + pollfd[FD_SOCKET].fd = SD_LISTEN_FDS_START; + pollfd[FD_SOCKET].events = POLLIN; + pollfd[FD_SHUTDOWN_TIMER].fd = -1; + pollfd[FD_SHUTDOWN_TIMER].events = POLLIN; + pollfd[FD_NOLOGIN_TIMER].fd = -1; + pollfd[FD_NOLOGIN_TIMER].events = POLLIN; + + log_debug("systemd-shutdownd running as pid %lu", (unsigned long) getpid()); + + sd_notify(false, + "READY=1\n" + "STATUS=Processing requests..."); + + do { + int k; + + if (poll(pollfd, n_fds, -1) < 0) { + + if (errno == EAGAIN || errno == EINTR) + continue; + + log_error("poll(): %m"); + goto finish; + } + + if (pollfd[FD_SOCKET].revents) { + + if ((k = read_packet(pollfd[FD_SOCKET].fd, &c)) < 0) + goto finish; + else if (k > 0 && c.elapse > 0) { + struct itimerspec its; + char buf[27]; + + if (pollfd[FD_SHUTDOWN_TIMER].fd < 0) + if ((pollfd[FD_SHUTDOWN_TIMER].fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC)) < 0) { + log_error("timerfd_create(): %m"); + goto finish; + } + + if (pollfd[FD_NOLOGIN_TIMER].fd < 0) + if ((pollfd[FD_NOLOGIN_TIMER].fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC)) < 0) { + log_error("timerfd_create(): %m"); + goto finish; + } + + /* Disallow logins 5 minutes prior to shutdown */ + zero(its); + timespec_store(&its.it_value, c.elapse > 5*USEC_PER_MINUTE ? c.elapse - 5*USEC_PER_MINUTE : 0); + if (timerfd_settime(pollfd[FD_NOLOGIN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) { + log_error("timerfd_settime(): %m"); + goto finish; + } + + /* Shutdown after the specified time is reached */ + zero(its); + timespec_store(&its.it_value, c.elapse); + if (timerfd_settime(pollfd[FD_SHUTDOWN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) { + log_error("timerfd_settime(): %m"); + goto finish; + } + + n_fds = 3; + + ctime_r(&its.it_value.tv_sec, buf); + + sd_notifyf(false, + "STATUS=Shutting down at %s...", + strstrip(buf)); + } + } + + if (pollfd[FD_NOLOGIN_TIMER].fd >= 0 && + pollfd[FD_NOLOGIN_TIMER].revents) { + int e; + + if ((e = touch("/etc/nologin")) < 0) + log_error("Failed to create /etc/nologin: %s", strerror(-e)); + else + unlink_nologin = true; + + /* Disarm nologin timer */ + close_nointr_nofail(pollfd[FD_NOLOGIN_TIMER].fd); + pollfd[FD_NOLOGIN_TIMER].fd = -1; + n_fds = 2; + + } + + if (pollfd[FD_SHUTDOWN_TIMER].fd >= 0 && + pollfd[FD_SHUTDOWN_TIMER].revents) { + exec_shutdown = true; + goto finish; + } + + } while (c.elapse > 0); + + r = 0; + + log_debug("systemd-shutdownd stopped as pid %lu", (unsigned long) getpid()); + +finish: + if (pollfd[FD_SOCKET].fd >= 0) + close_nointr_nofail(pollfd[FD_SOCKET].fd); + + if (pollfd[FD_SHUTDOWN_TIMER].fd >= 0) + close_nointr_nofail(pollfd[FD_SHUTDOWN_TIMER].fd); + + if (pollfd[FD_NOLOGIN_TIMER].fd >= 0) + close_nointr_nofail(pollfd[FD_NOLOGIN_TIMER].fd); + + if (exec_shutdown) { + char sw[3]; + + sw[0] = '-'; + sw[1] = c.mode; + sw[2] = 0; + + execl(SYSTEMCTL_BINARY_PATH, "shutdown", sw, "now", NULL); + log_error("Failed to execute /sbin/shutdown: %m"); + } + + if (unlink_nologin) + unlink("/etc/nologin"); + + sd_notify(false, + "STATUS=Exiting..."); + + return r; +} diff --git a/src/shutdownd.h b/src/shutdownd.h new file mode 100644 index 0000000000..d298b01c92 --- /dev/null +++ b/src/shutdownd.h @@ -0,0 +1,35 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef fooshutdowndhfoo +#define fooshutdowndhfoo + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include "util.h" +#include "macro.h" + +_packed_ struct shutdownd_command { + usec_t elapse; + char mode; /* H, P, r, i.e. the switches usually passed to + * shutdown to select whether to halt, power-off or + * reboot the machine */ +}; + +#endif diff --git a/src/socket-util.h b/src/socket-util.h index 86c9e47809..b5cb2a844d 100644 --- a/src/socket-util.h +++ b/src/socket-util.h @@ -30,14 +30,16 @@ #include "macro.h" #include "util.h" +union sockaddr_union { + struct sockaddr sa; + struct sockaddr_in in4; + struct sockaddr_in6 in6; + struct sockaddr_un un; + struct sockaddr_storage storage; +}; + typedef struct SocketAddress { - union { - struct sockaddr sa; - struct sockaddr_in in4; - struct sockaddr_in6 in6; - struct sockaddr_un un; - struct sockaddr_storage storage; - } sockaddr; + union sockaddr_union sockaddr; /* We store the size here explicitly due to the weird * sockaddr_un semantics for abstract sockets */ diff --git a/src/systemctl.c b/src/systemctl.c index baae019e0f..e517031cea 100644 --- a/src/systemctl.c +++ b/src/systemctl.c @@ -49,6 +49,7 @@ #include "path-lookup.h" #include "conf-parser.h" #include "sd-daemon.h" +#include "shutdownd.h" static const char *arg_type = NULL; static char **arg_property = NULL; @@ -68,6 +69,9 @@ static bool arg_full = false; static bool arg_force = false; static bool arg_defaults = false; static char **arg_wall = NULL; +static usec_t arg_when = 0; +static bool arg_skip_fsck = false; +static bool arg_force_fsck = false; static enum action { ACTION_INVALID, ACTION_SYSTEMCTL, @@ -84,6 +88,7 @@ static enum action { ACTION_RELOAD, ACTION_REEXEC, ACTION_RUNLEVEL, + ACTION_CANCEL_SHUTDOWN, _ACTION_MAX } arg_action = ACTION_SYSTEMCTL; static enum dot { @@ -3832,7 +3837,10 @@ static int shutdown_help(void) { " -r --reboot Reboot the machine\n" " -h Equivalent to --poweroff, overriden by --halt\n" " -k Don't halt/power-off/reboot, just send warnings\n" - " --no-wall Don't send wall message before halt/power-off/reboot\n", + " --no-wall Don't send wall message before halt/power-off/reboot\n" + " -f Skip fsck on reboot\n" + " -F Force fsck on reboot\n" + " -c Cancel a pending shutdown\n", program_invocation_short_name); return 0; @@ -4099,6 +4107,53 @@ static int halt_parse_argv(int argc, char *argv[]) { return 1; } +static int parse_time_spec(const char *t, usec_t *_u) { + assert(t); + assert(_u); + + if (streq(t, "now")) + *_u = 0; + else if (t[0] == '+') { + uint64_t u; + + if (safe_atou64(t + 1, &u) < 0) + return -EINVAL; + + *_u = now(CLOCK_REALTIME) + USEC_PER_MINUTE * u; + } else { + char *e = NULL; + long hour, minute; + struct tm tm; + time_t s; + usec_t n; + + errno = 0; + hour = strtol(t, &e, 10); + if (errno != 0 || *e != ':' || hour < 0 || hour > 23) + return -EINVAL; + + minute = strtol(e+1, &e, 10); + if (errno != 0 || *e != 0 || minute < 0 || minute > 59) + return -EINVAL; + + n = now(CLOCK_REALTIME); + s = (time_t) n / USEC_PER_SEC; + assert_se(localtime_r(&s, &tm)); + + tm.tm_hour = (int) hour; + tm.tm_min = (int) minute; + + assert_se(s = mktime(&tm)); + + *_u = (usec_t) s * USEC_PER_SEC; + + while (*_u <= n) + *_u += USEC_PER_DAY; + } + + return 0; +} + static int shutdown_parse_argv(int argc, char *argv[]) { enum { @@ -4115,12 +4170,12 @@ static int shutdown_parse_argv(int argc, char *argv[]) { { NULL, 0, NULL, 0 } }; - int c; + int c, r; assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "HPrhkt:a", options, NULL)) >= 0) { + while ((c = getopt_long(argc, argv, "HPrhkt:afFc", options, NULL)) >= 0) { switch (c) { case ARG_HELP: @@ -4157,6 +4212,18 @@ static int shutdown_parse_argv(int argc, char *argv[]) { /* Compatibility nops */ break; + case 'f': + arg_skip_fsck = true; + break; + + case 'F': + arg_force_fsck = true; + break; + + case 'c': + arg_action = ACTION_CANCEL_SHUTDOWN; + break; + case '?': return -EINVAL; @@ -4166,10 +4233,13 @@ static int shutdown_parse_argv(int argc, char *argv[]) { } } - if (argc > optind && !streq(argv[optind], "now")) - log_warning("First argument '%s' isn't 'now'. Ignoring.", argv[optind]); + if (argc > optind) + if ((r = parse_time_spec(argv[optind], &arg_when)) < 0) { + log_error("Failed to parse time specification: %s", argv[optind]); + return r; + } - /* We ignore the time argument */ + /* We skip the time argument */ if (argc > optind + 1) arg_wall = argv + optind + 1; @@ -4624,6 +4694,62 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError return verbs[i].dispatch(bus, argv + optind, left); } +static int send_shutdownd(usec_t t, char mode) { + int fd = -1; + struct msghdr msghdr; + struct iovec iovec; + union sockaddr_union sockaddr; + struct ucred *ucred; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; + } control; + struct shutdownd_command c; + + zero(c); + c.elapse = t; + c.mode = mode; + + if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) + return -errno; + + zero(sockaddr); + sockaddr.sa.sa_family = AF_UNIX; + sockaddr.un.sun_path[0] = 0; + strncpy(sockaddr.un.sun_path+1, "/org/freedesktop/systemd1/shutdownd", sizeof(sockaddr.un.sun_path)-1); + + zero(iovec); + iovec.iov_base = (char*) &c; + iovec.iov_len = sizeof(c); + + zero(control); + control.cmsghdr.cmsg_level = SOL_SOCKET; + control.cmsghdr.cmsg_type = SCM_CREDENTIALS; + control.cmsghdr.cmsg_len = CMSG_LEN(sizeof(struct ucred)); + + ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr); + ucred->pid = getpid(); + ucred->uid = getuid(); + ucred->gid = getgid(); + + zero(msghdr); + msghdr.msg_name = &sockaddr; + msghdr.msg_namelen = sizeof(sa_family_t) + 1 + sizeof("/org/freedesktop/systemd1/shutdownd") - 1; + + msghdr.msg_iov = &iovec; + msghdr.msg_iovlen = 1; + msghdr.msg_control = &control; + msghdr.msg_controllen = control.cmsghdr.cmsg_len; + + if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) { + close_nointr_nofail(fd); + return -errno; + } + + close_nointr_nofail(fd); + return 0; +} + static int reload_with_fallback(DBusConnection *bus) { if (bus) { @@ -4677,6 +4803,24 @@ static int halt_main(DBusConnection *bus) { return -EPERM; } + if (arg_force_fsck) { + if ((r = touch("/forcefsck")) < 0) + log_warning("Failed to create /forcefsck: %s", strerror(-r)); + } else if (arg_skip_fsck) { + if ((r = touch("/fastboot")) < 0) + log_warning("Failed to create /fastboot: %s", strerror(-r)); + } + + if (arg_when > 0) { + if ((r = send_shutdownd(arg_when, + arg_action == ACTION_HALT ? 'H' : + arg_action == ACTION_POWEROFF ? 'P' : + 'r')) < 0) + log_warning("Failed to talk to shutdownd, proceeding with immediate shutdown: %s", strerror(-r)); + else + return 0; + } + if (!arg_dry && !arg_immediate) return start_with_fallback(bus); @@ -4790,6 +4934,10 @@ int main(int argc, char*argv[]) { retval = reload_with_fallback(bus) < 0; break; + case ACTION_CANCEL_SHUTDOWN: + retval = send_shutdownd(0, 0) < 0; + break; + case ACTION_INVALID: case ACTION_RUNLEVEL: default: diff --git a/src/util.c b/src/util.c index c0b63dd574..bc227f52d5 100644 --- a/src/util.c +++ b/src/util.c @@ -2997,6 +2997,17 @@ void nss_disable_nscd(void) { log_debug("Cannot disable nscd."); } +int touch(const char *path) { + int fd; + + assert(path); + + if ((fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, 0666)) < 0) + return -errno; + + close_nointr_nofail(fd); + return 0; +} static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", diff --git a/src/util.h b/src/util.h index 66ebb46dbf..e9126c04fa 100644 --- a/src/util.h +++ b/src/util.h @@ -338,6 +338,8 @@ char *ellipsize(const char *s, unsigned length, unsigned percent); void nss_disable_nscd(void); +int touch(const char *path); + const char *ioprio_class_to_string(int i); int ioprio_class_from_string(const char *s); diff --git a/units/.gitignore b/units/.gitignore index 0318e4825d..9ac34010c5 100644 --- a/units/.gitignore +++ b/units/.gitignore @@ -1,3 +1,4 @@ +systemd-shutdownd.service systemd-random-seed-load.service systemd-random-seed-save.service systemd-initctl.service diff --git a/units/systemd-initctl.socket b/units/systemd-initctl.socket index 3db2683f6b..bcb54b3424 100644 --- a/units/systemd-initctl.socket +++ b/units/systemd-initctl.socket @@ -10,7 +10,6 @@ [Unit] Description=systemd /dev/initctl Compatibility Socket DefaultDependencies=no -After=sysinit.target Before=sockets.target [Socket] diff --git a/units/systemd-logger.service.in b/units/systemd-logger.service.in index 2a9ec21067..2004438971 100644 --- a/units/systemd-logger.service.in +++ b/units/systemd-logger.service.in @@ -9,7 +9,6 @@ [Unit] Description=systemd Logging Daemon -DefaultDependencies=no After=@SPECIAL_SYSLOG_SERVICE@ [Service] diff --git a/units/systemd-logger.socket b/units/systemd-logger.socket index 57a590dba9..f62b582d3e 100644 --- a/units/systemd-logger.socket +++ b/units/systemd-logger.socket @@ -9,9 +9,6 @@ [Unit] Description=systemd Logging Socket -DefaultDependencies=no -After=sysinit.target -Before=sockets.target [Socket] ListenStream=@/org/freedesktop/systemd1/logger diff --git a/units/systemd-shutdownd.service.in b/units/systemd-shutdownd.service.in new file mode 100644 index 0000000000..5d0bd77a8f --- /dev/null +++ b/units/systemd-shutdownd.service.in @@ -0,0 +1,15 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# See systemd.special(7) for details + +[Unit] +Description=systemd Shutdown Daemon +DefaultDependencies=no + +[Service] +ExecStart=@rootlibexecdir@/systemd-shutdownd diff --git a/units/systemd-shutdownd.socket b/units/systemd-shutdownd.socket new file mode 100644 index 0000000000..9a95fa92f0 --- /dev/null +++ b/units/systemd-shutdownd.socket @@ -0,0 +1,16 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# See systemd.special(7) for details + +[Unit] +Description=systemd Shutdown Socket +DefaultDependencies=no +Before=sockets.target + +[Socket] +ListenStream=@/org/freedesktop/systemd1/shutdownd -- cgit v1.2.3-54-g00ecf From 5192bd1945f59254b3d260ded15dd9f2b8cc2de7 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 17 Aug 2010 03:29:46 +0200 Subject: auto-getty: rework auto console getty logic to work in conjunction with single user mode --- .gitignore | 1 + Makefile.am | 23 +++- fixme | 6 +- src/auto-console-getty.c | 189 ++++++++++++++++++++++++++ src/initctl.c | 2 +- src/main.c | 39 +----- units/systemd-auto-console-getty.service.in | 16 +++ units/systemd-random-seed-load.service.in | 2 +- units/systemd-random-seed-save.service.in | 2 +- units/systemd-update-utmp-runlevel.service.in | 2 +- units/systemd-update-utmp-shutdown.service.in | 2 +- 11 files changed, 237 insertions(+), 47 deletions(-) create mode 100644 src/auto-console-getty.c create mode 100644 units/systemd-auto-console-getty.service.in (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 6b8edf5ba9..7a20505416 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-auto-console-getty systemd-shutdownd systemd-random-seed systemd-update-utmp diff --git a/Makefile.am b/Makefile.am index 013a603fc1..de85b79790 100644 --- a/Makefile.am +++ b/Makefile.am @@ -72,7 +72,8 @@ rootlibexec_PROGRAMS = \ systemd-initctl \ systemd-update-utmp \ systemd-random-seed \ - systemd-shutdownd + systemd-shutdownd \ + systemd-auto-console-getty noinst_PROGRAMS = \ test-engine \ @@ -139,6 +140,7 @@ dist_systemunit_DATA = \ units/systemd-initctl.socket \ units/systemd-logger.socket \ units/systemd-shutdownd.socket \ + units/systemd-auto-console-getty.service \ units/dev-hugepages.automount \ units/dev-hugepages.mount \ units/dev-mqueue.automount \ @@ -164,6 +166,7 @@ nodist_systemunit_DATA = \ units/systemd-initctl.service \ units/systemd-logger.service \ units/systemd-shutdownd.service \ + units/systemd-auto-console-getty.service \ units/systemd-update-utmp-runlevel.service \ units/systemd-update-utmp-shutdown.service \ units/systemd-random-seed-save.service \ @@ -186,6 +189,7 @@ EXTRA_DIST = \ units/systemd-initctl.service.in \ units/systemd-logger.service.in \ units/systemd-shutdownd.service.in \ + units/systemd-auto-console-getty.service.in \ units/systemd-update-utmp-runlevel.service.in \ units/systemd-update-utmp-shutdown.service.in \ units/systemd-random-seed-save.service.in \ @@ -545,6 +549,18 @@ systemd_shutdownd_CFLAGS = \ systemd_shutdownd_LDADD = \ libsystemd-basic.la +systemd_auto_console_getty_SOURCES = \ + src/auto-console-getty.c \ + src/dbus-common.c + +systemd_auto_console_getty_CFLAGS = \ + $(AM_CFLAGS) \ + $(DBUS_CFLAGS) + +systemd_auto_console_getty_LDADD = \ + libsystemd-basic.la \ + $(DBUS_LIBS) + systemd_cgroups_agent_SOURCES = \ src/cgroups-agent.c \ src/dbus-common.c @@ -825,13 +841,14 @@ install-data-hook: $(LN_S) $(systemunitdir)/reboot.target ctrl-alt-del.target && \ $(LN_S) $(systemunitdir)/rescue.target kbrequest.target ) ( cd $(DESTDIR)$(pkgsysconfdir)/system/getty.target.wants && \ - rm -f getty@tty1.service getty@tty2.service getty@tty3.service getty@tty4.service getty@tty5.service getty@tty6.service && \ + rm -f getty@tty1.service getty@tty2.service getty@tty3.service getty@tty4.service getty@tty5.service getty@tty6.service systemd-auto-console-getty.service && \ $(LN_S) $(systemunitdir)/getty@.service getty@tty1.service && \ $(LN_S) $(systemunitdir)/getty@.service getty@tty2.service && \ $(LN_S) $(systemunitdir)/getty@.service getty@tty3.service && \ $(LN_S) $(systemunitdir)/getty@.service getty@tty4.service && \ $(LN_S) $(systemunitdir)/getty@.service getty@tty5.service && \ - $(LN_S) $(systemunitdir)/getty@.service getty@tty6.service ) + $(LN_S) $(systemunitdir)/getty@.service getty@tty6.service && \ + $(LN_S) $(systemunitdir)/systemd-auto-console-getty.service systemd-auto-console-getty.service ) ( cd $(DESTDIR)$(pkgsysconfdir)/system/multi-user.target.wants && \ rm -f getty.target remote-fs.target && \ $(LN_S) $(systemunitdir)/getty.target getty.target && \ diff --git a/fixme b/fixme index 2df43c34b6..30f77e8f19 100644 --- a/fixme +++ b/fixme @@ -63,13 +63,13 @@ * io priority during initialization -* if a service fails too often, make the service enter maintainence mode, and the socket, too. +* if a service fails too often, make the service enter maintenance mode, and the socket, too. * Ray: plymouth after/before getty? https://bugzilla.redhat.com/show_bug.cgi?id=623430 -* be more forgiving when parsing unit files, when encountering incorrect lines with assignments +* add no-tab header to .h -* agetty darf nicht mit emergency.service kollidieren +* fix systemctl daemon-reload in part of boot External: diff --git a/src/auto-console-getty.c b/src/auto-console-getty.c new file mode 100644 index 0000000000..44d2eff732 --- /dev/null +++ b/src/auto-console-getty.c @@ -0,0 +1,189 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include + +#include "util.h" +#include "log.h" +#include "dbus-common.h" + +static int spawn_getty(DBusConnection *bus, const char *console) { + DBusMessage *m = NULL, *reply = NULL; + DBusError error; + const char *fail = "fail"; + char *name; + int r = -EIO; + + dbus_error_init(&error); + + assert(bus); + assert(console); + + /* FIXME: we probably should escape the tty name properly here */ + if (asprintf(&name, "getty@%s.service", console) < 0) + return -ENOMEM; + + if (!(m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartUnit"))) { + log_error("Could not allocate message."); + goto finish; + } + + if (!dbus_message_append_args(m, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &fail, + DBUS_TYPE_INVALID)) { + log_error("Could not attach target and flag information to message."); + goto finish; + } + + if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { + log_error("Failed to start unit: %s", error.message); + goto finish; + } + + r = 0; + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); + + free(name); + + return r; +} + +static int parse_proc_cmdline_word(const char *word, char **console) { + assert(word); + + if (startswith(word, "console=")) { + const char *k; + size_t l; + char *w = NULL; + + k = word + 8; + l = strcspn(k, ","); + + if (l < 4 || + !startswith(k, "tty") || + k[3+strspn(k+3, "0123456789")] != 0) { + + if (!(w = strndup(k, l))) + return -ENOMEM; + + } + + free(*console); + *console = w; + } + + return 0; +} + +static int parse_proc_cmdline(char **console) { + char *line; + int r; + char *w; + size_t l; + char *state; + + assert(console); + + if ((r = read_one_line_file("/tmp/cmdline", &line)) < 0) { + log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r)); + return 0; + } + + FOREACH_WORD_QUOTED(w, l, line, state) { + char *word; + + if (!(word = strndup(w, l))) { + r = -ENOMEM; + goto finish; + } + + r = parse_proc_cmdline_word(word, console); + free(word); + + if (r < 0) + goto finish; + } + + r = 0; + +finish: + free(line); + return r; +} + +int main(int argc, char *argv[]) { + DBusError error; + int r = 1; + char *console = NULL; + DBusConnection *bus = NULL; + + dbus_error_init(&error); + + if (argc > 1) { + log_error("This program does not take arguments."); + return 1; + } + + log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_parse_environment(); + log_open(); + + if (bus_connect(DBUS_BUS_SYSTEM, &bus, NULL, &error) < 0) { + log_error("Failed to get D-Bus connection: %s", error.message); + goto finish; + } + + if (parse_proc_cmdline(&console) < 0) + goto finish; + + if (console) + if (spawn_getty(bus, console) < 0) + goto finish; + + r = 0; + +finish: + free(console); + + if (bus) { + dbus_connection_close(bus); + dbus_connection_unref(bus); + } + + dbus_error_free(&error); + + dbus_shutdown(); + + return r; +} diff --git a/src/initctl.c b/src/initctl.c index 115452e86b..7b478a75d5 100644 --- a/src/initctl.c +++ b/src/initctl.c @@ -119,7 +119,7 @@ static void change_runlevel(Server *s, int runlevel) { DBUS_TYPE_STRING, &target, DBUS_TYPE_STRING, &replace, DBUS_TYPE_INVALID)) { - log_error("Could not attach target and flag information to signal message."); + log_error("Could not attach target and flag information to message."); goto finish; } diff --git a/src/main.c b/src/main.c index 5b5c05acbc..51253c66c8 100644 --- a/src/main.c +++ b/src/main.c @@ -57,7 +57,6 @@ static enum { } arg_action = ACTION_RUN; static char *arg_default_unit = NULL; -static char *arg_console = NULL; static ManagerRunningAs arg_running_as = _MANAGER_RUNNING_AS_INVALID; static bool arg_dump_core = true; @@ -245,6 +244,8 @@ static int parse_proc_cmdline_word(const char *word) { "5", SPECIAL_RUNLEVEL5_TARGET }; + assert(word); + if (startswith(word, "systemd.unit=")) return set_default_unit(word + 13); @@ -336,26 +337,8 @@ static int parse_proc_cmdline_word(const char *word) { } else if (streq(word, "nomodules")) arg_nomodules = true; - else if (startswith(word, "console=")) { - const char *k; - size_t l; - char *w = NULL; - - k = word + 8; - l = strcspn(k, ","); - - if (l < 4 || - !startswith(k, "tty") || - k[3+strspn(k+3, "0123456789")] != 0) { - - if (!(w = strndup(k, l))) - return -ENOMEM; - } - - free(arg_console); - arg_console = w; - } else if (streq(word, "quiet")) { + else if (streq(word, "quiet")) { arg_show_status = false; arg_sysv_console = false; } else { @@ -1061,21 +1044,6 @@ int main(int argc, char *argv[]) { goto finish; } - if (arg_console && arg_running_as == MANAGER_SYSTEM) { - char *name; - - if (asprintf(&name, "getty@%s.service", arg_console) < 0) - log_error("Out of memory while generating console getty service name."); - else { - if ((r = manager_add_job_by_name(m, JOB_START, name, JOB_FAIL, false, &error, NULL)) < 0) { - log_error("Failed to start console getty target: %s", bus_error(&error, r)); - dbus_error_free(&error); - } - - free(name); - } - } - if (arg_action == ACTION_TEST) { printf("-> By jobs:\n"); manager_dump_jobs(m, stdout, "\t"); @@ -1121,7 +1089,6 @@ finish: manager_free(m); free(arg_default_unit); - free(arg_console); dbus_shutdown(); diff --git a/units/systemd-auto-console-getty.service.in b/units/systemd-auto-console-getty.service.in new file mode 100644 index 0000000000..6542cab18a --- /dev/null +++ b/units/systemd-auto-console-getty.service.in @@ -0,0 +1,16 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +[Unit] +Description=Automatically Spawn getty on Kernel Console + +[Service] +Type=oneshot +ExecStart=@rootlibexecdir@/systemd-auto-console-getty + +[Install] +WantedBy=getty.target diff --git a/units/systemd-random-seed-load.service.in b/units/systemd-random-seed-load.service.in index 4d5841d6f5..965da06901 100644 --- a/units/systemd-random-seed-load.service.in +++ b/units/systemd-random-seed-load.service.in @@ -14,7 +14,7 @@ Before=shutdown.target [Service] Type=oneshot -ExecStart=-@rootlibexecdir@/systemd-random-seed load +ExecStart=@rootlibexecdir@/systemd-random-seed load [Install] WantedBy=sysinit.target diff --git a/units/systemd-random-seed-save.service.in b/units/systemd-random-seed-save.service.in index 654c859e22..70cb31d491 100644 --- a/units/systemd-random-seed-save.service.in +++ b/units/systemd-random-seed-save.service.in @@ -13,7 +13,7 @@ Conflicts=systemd-random-seed-save.service [Service] Type=oneshot -ExecStart=-@rootlibexecdir@/systemd-random-seed save +ExecStart=@rootlibexecdir@/systemd-random-seed save [Install] WantedBy=shutdown.target diff --git a/units/systemd-update-utmp-runlevel.service.in b/units/systemd-update-utmp-runlevel.service.in index 0b4c83dd86..a288d6eb21 100644 --- a/units/systemd-update-utmp-runlevel.service.in +++ b/units/systemd-update-utmp-runlevel.service.in @@ -14,4 +14,4 @@ Before=shutdown.target [Service] Type=oneshot -ExecStart=-@rootlibexecdir@/systemd-update-utmp runlevel +ExecStart=@rootlibexecdir@/systemd-update-utmp runlevel diff --git a/units/systemd-update-utmp-shutdown.service.in b/units/systemd-update-utmp-shutdown.service.in index 4c48c8b53a..e071285e3a 100644 --- a/units/systemd-update-utmp-shutdown.service.in +++ b/units/systemd-update-utmp-shutdown.service.in @@ -13,4 +13,4 @@ Conflicts=systemd-update-utmp-runlevel.service [Service] Type=oneshot -ExecStart=-@rootlibexecdir@/systemd-update-utmp shutdown +ExecStart=@rootlibexecdir@/systemd-update-utmp shutdown -- cgit v1.2.3-54-g00ecf From b2423f1f436f847d9fc96a63679be2b5552b6baf Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 19 Aug 2010 02:03:03 +0200 Subject: modules-load: add systemd-modules-load tool that can load a configured list of modules in /etc/modules.d/ on boot, replacing distro-dependent shell hacks for this --- .gitignore | 1 + Makefile.am | 15 ++- fixme | 4 +- src/modules-load.c | 167 ++++++++++++++++++++++++++++++++++ units/.gitignore | 1 + units/systemd-modules-load.service.in | 19 ++++ 6 files changed, 203 insertions(+), 4 deletions(-) create mode 100644 src/modules-load.c create mode 100644 units/systemd-modules-load.service.in (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 7a20505416..7e7b626008 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-modules-load systemd-auto-console-getty systemd-shutdownd systemd-random-seed diff --git a/Makefile.am b/Makefile.am index 4905f2dad1..a2f7e1725e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -73,7 +73,8 @@ rootlibexec_PROGRAMS = \ systemd-update-utmp \ systemd-random-seed \ systemd-shutdownd \ - systemd-auto-console-getty + systemd-auto-console-getty \ + systemd-modules-load noinst_PROGRAMS = \ test-engine \ @@ -140,7 +141,6 @@ dist_systemunit_DATA = \ units/systemd-initctl.socket \ units/systemd-logger.socket \ units/systemd-shutdownd.socket \ - units/systemd-auto-console-getty.service \ units/dev-hugepages.automount \ units/dev-hugepages.mount \ units/dev-mqueue.automount \ @@ -173,6 +173,7 @@ nodist_systemunit_DATA = \ units/systemd-logger.service \ units/systemd-shutdownd.service \ units/systemd-auto-console-getty.service \ + units/systemd-modules-load.service \ units/systemd-update-utmp-runlevel.service \ units/systemd-update-utmp-shutdown.service \ units/systemd-random-seed-save.service \ @@ -196,6 +197,7 @@ EXTRA_DIST = \ units/systemd-logger.service.in \ units/systemd-shutdownd.service.in \ units/systemd-auto-console-getty.service.in \ + units/systemd-modules-load.service.in \ units/systemd-update-utmp-runlevel.service.in \ units/systemd-update-utmp-shutdown.service.in \ units/systemd-random-seed-save.service.in \ @@ -566,6 +568,15 @@ systemd_auto_console_getty_LDADD = \ libsystemd-basic.la \ $(DBUS_LIBS) +systemd_modules_load_SOURCES = \ + src/modules-load.c + +systemd_modules_load_CFLAGS = \ + $(AM_CFLAGS) + +systemd_modules_load_LDADD = \ + libsystemd-basic.la + systemd_cgroups_agent_SOURCES = \ src/cgroups-agent.c \ src/dbus-common.c diff --git a/fixme b/fixme index b55f6e6d71..4cfa1339ae 100644 --- a/fixme +++ b/fixme @@ -63,12 +63,12 @@ * systemctl: ln -s output muss abschaltbar sein, und warning wenn [Install] leer ist. -* /etc/modules.d/*.modules in systemd-modules-load - * X-Interactive is kaputt * universal fallback for hostname +* bash completion a la gdbus + External: * procps, psmisc, sysvinit-tools, hostname → util-linux-ng diff --git a/src/modules-load.c b/src/modules-load.c new file mode 100644 index 0000000000..44ff02e7b8 --- /dev/null +++ b/src/modules-load.c @@ -0,0 +1,167 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "util.h" +#include "strv.h" + +/* This reads all module names listed in /etc/modules.d/?*.modules and + * loads them into the kernel. This follows roughly Debian's way to + * handle modules, but uses a directory of fragments instead of a + * single /etc/modules file. */ + +static int scandir_filter(const struct dirent *d) { + assert(d); + + if (ignore_file(d->d_name)) + return 0; + + if (d->d_type != DT_REG && + d->d_type != DT_LNK) + return 0; + + return endswith(d->d_name, ".modules"); +} + +int main(int argc, char *argv[]) { + struct dirent **de = NULL; + int r = 1, n, i; + char **arguments = NULL; + unsigned n_arguments = 0, n_allocated = 0; + + if (argc > 1) { + log_error("This program takes no argument."); + return 1; + } + + log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_parse_environment(); + log_open(); + + if (!(arguments = strv_new("/sbin/modprobe", "-sab", "--", NULL))) { + log_error("Failed to allocate string array"); + goto finish; + } + + n_arguments = n_allocated = 3; + + if ((n = scandir("/etc/modules.d/", &de, scandir_filter, alphasort)) < 0) { + + if (errno == ENOENT) + r = 0; + else + log_error("Failed to enumerate /etc/modules.d/ files: %m"); + + goto finish; + } + + r = 0; + + for (i = 0; i < n; i++) { + int k; + char *fn; + FILE *f; + + k = asprintf(&fn, "/etc/modules.d/%s", de[i]->d_name); + free(de[i]); + + if (k < 0) { + log_error("Failed to allocate file name."); + r = 1; + continue; + } + + f = fopen(fn, "re"); + free(fn); + + if (!f) { + log_error("Failed to open %s: %m", fn); + r = 1; + continue; + } + + for (;;) { + char line[LINE_MAX], *l, *t; + + if (!(fgets(line, sizeof(line), f))) + break; + + l = strstrip(line); + if (*l == '#' || *l == 0) + continue; + + if (!(t = strdup(l))) { + log_error("Failed to allocate module name."); + continue; + } + + if (n_arguments >= n_allocated) { + char **a; + unsigned m; + + m = MAX(16U, n_arguments*2); + + if (!(a = realloc(arguments, sizeof(char*) * (m+1)))) { + log_error("Failed to increase module array size."); + free(t); + r = 1; + continue; + } + + arguments = a; + n_allocated = m; + } + + arguments[n_arguments++] = t; + } + + if (ferror(f)) { + r = 1; + log_error("Failed to read from file: %m"); + } + + fclose(f); + } + + free(de); + +finish: + + if (n_arguments > 3) { + arguments[n_arguments] = NULL; + execv("/sbin/modprobe", arguments); + + log_error("Failed to execute /sbin/modprobe: %m"); + r = 1; + } + + strv_free(arguments); + + return r; +} diff --git a/units/.gitignore b/units/.gitignore index 2a5e24d32b..23857d2310 100644 --- a/units/.gitignore +++ b/units/.gitignore @@ -1,3 +1,4 @@ +systemd-modules-load.service systemd-auto-console-getty.service systemd-shutdownd.service systemd-random-seed-load.service diff --git a/units/systemd-modules-load.service.in b/units/systemd-modules-load.service.in new file mode 100644 index 0000000000..2390e02b3f --- /dev/null +++ b/units/systemd-modules-load.service.in @@ -0,0 +1,19 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +[Unit] +Description=Load Kernel Modules +DefaultDependencies=no +Conflicts=shutdown.target +Before=shutdown.target + +[Service] +Type=oneshot +ExecStart=@rootlibexecdir@/systemd-modules-load + +[Install] +WantedBy=sysinit.target -- cgit v1.2.3-54-g00ecf From e59077036bcf5a041e336f04cb0d2f7d18d489a1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 19 Aug 2010 03:02:22 +0200 Subject: hostname: on all distros make the name configured in /etc/hostname take precedence over distro-specific configuration In order to unify configuration across distributions we pick the simple-most option by default (Debian's /etc/hostname) and then fall back to distro-specific hacks if that doesn't exist. --- .gitignore | 1 + Makefile.am | 8 ++++++++ fixme | 2 -- src/hostname-setup.c | 42 ++++++++++++++++++++++++++++++++++++------ src/test-hostname.c | 37 +++++++++++++++++++++++++++++++++++++ 5 files changed, 82 insertions(+), 8 deletions(-) create mode 100644 src/test-hostname.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 7e7b626008..99b533854a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +test-hostname systemd-modules-load systemd-auto-console-getty systemd-shutdownd diff --git a/Makefile.am b/Makefile.am index a2f7e1725e..bf329e5083 100644 --- a/Makefile.am +++ b/Makefile.am @@ -81,6 +81,7 @@ noinst_PROGRAMS = \ test-job-type \ test-ns \ test-loopback \ + test-hostname \ test-daemon \ test-cgroup \ test-env-replace @@ -473,6 +474,13 @@ test_loopback_SOURCES = \ test_loopback_LDADD = \ libsystemd-basic.la +test_hostname_SOURCES = \ + src/test-hostname.c \ + src/hostname-setup.c + +test_hostname_LDADD = \ + libsystemd-basic.la + test_daemon_SOURCES = \ src/test-daemon.c \ src/sd-daemon.c diff --git a/fixme b/fixme index 48fc3c8f78..76267c3f2a 100644 --- a/fixme +++ b/fixme @@ -63,8 +63,6 @@ * X-Interactive is kaputt -* universal fallback for hostname - * bash completion a la gdbus External: diff --git a/src/hostname-setup.c b/src/hostname-setup.c index f52e38aef2..d8fa567901 100644 --- a/src/hostname-setup.c +++ b/src/hostname-setup.c @@ -34,8 +34,6 @@ #define FILENAME "/etc/sysconfig/network" #elif defined(TARGET_SUSE) || defined(TARGET_SLACKWARE) #define FILENAME "/etc/HOSTNAME" -#elif defined(TARGET_DEBIAN) -#define FILENAME "/etc/hostname" #elif defined(TARGET_ARCH) #define FILENAME "/etc/rc.conf" #elif defined(TARGET_GENTOO) @@ -59,7 +57,7 @@ static char* strip_bad_chars(char *s) { return s; } -static int read_hostname(char **hn) { +static int read_distro_hostname(char **hn) { #if defined(TARGET_FEDORA) || defined(TARGET_ARCH) || defined(TARGET_GENTOO) int r; @@ -111,7 +109,7 @@ finish: fclose(f); return r; -#elif defined(TARGET_SUSE) || defined(TARGET_DEBIAN) || defined(TARGET_SLACKWARE) +#elif defined(TARGET_SUSE) || defined(TARGET_SLACKWARE) int r; char *s, *k; @@ -134,12 +132,44 @@ finish: } *hn = k; + return 0; #else -#warning "Don't know how to read the hostname" - return -ENOENT; #endif +} + +static int read_hostname(char **hn) { + int r; + char *s, *k; + + assert(hn); + + /* First, try to load the generic hostname configuration file, + * that we support on all distributions */ + + if ((r = read_one_line_file("/etc/hostname", &s)) < 0) { + + if (r == -ENOENT) + return read_distro_hostname(hn); + + return r; + } + + k = strdup(strstrip(s)); + free(s); + + if (!k) + return -ENOMEM; + + strip_bad_chars(k); + + if (k[0] == 0) { + free(k); + return -ENOENT; + } + + *hn = k; return 0; } diff --git a/src/test-hostname.c b/src/test-hostname.c new file mode 100644 index 0000000000..0a08416a17 --- /dev/null +++ b/src/test-hostname.c @@ -0,0 +1,37 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "hostname-setup.h" +#include "util.h" + +int main(int argc, char* argv[]) { + int r; + + if ((r = hostname_setup()) < 0) + fprintf(stderr, "hostname: %s\n", strerror(-r)); + + return 0; +} -- cgit v1.2.3-54-g00ecf From 449ddb2d23a63ca4c8cd70d13a070fba87c1fb30 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 20 Aug 2010 03:26:15 +0200 Subject: remount: add tool that applies /etc/fstab mount options to all api mounts --- .gitignore | 1 + Makefile.am | 15 +++- fixme | 6 +- src/hashmap.c | 9 ++ src/hashmap.h | 1 + src/mount-setup.c | 4 +- src/remount-api-vfs.c | 147 +++++++++++++++++++++++++++++++ src/set.c | 7 +- units/.gitignore | 1 + units/systemd-modules-load.service.in | 1 + units/systemd-remount-api-vfs.service.in | 20 +++++ 11 files changed, 199 insertions(+), 13 deletions(-) create mode 100644 src/remount-api-vfs.c create mode 100644 units/systemd-remount-api-vfs.service.in (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 99b533854a..42d9c8c679 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-remount-api-vfs test-hostname systemd-modules-load systemd-auto-console-getty diff --git a/Makefile.am b/Makefile.am index 1a7142158d..6266a0ee0c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -74,7 +74,8 @@ rootlibexec_PROGRAMS = \ systemd-random-seed \ systemd-shutdownd \ systemd-auto-console-getty \ - systemd-modules-load + systemd-modules-load \ + systemd-remount-api-vfs noinst_PROGRAMS = \ test-engine \ @@ -175,6 +176,7 @@ nodist_systemunit_DATA = \ units/systemd-shutdownd.service \ units/systemd-auto-console-getty.service \ units/systemd-modules-load.service \ + units/systemd-remount-api-vfs.service \ units/systemd-update-utmp-runlevel.service \ units/systemd-update-utmp-shutdown.service \ units/systemd-random-seed-save.service \ @@ -199,6 +201,7 @@ EXTRA_DIST = \ units/systemd-shutdownd.service.in \ units/systemd-auto-console-getty.service.in \ units/systemd-modules-load.service.in \ + units/systemd-remount-api-vfs.service.in \ units/systemd-update-utmp-runlevel.service.in \ units/systemd-update-utmp-shutdown.service.in \ units/systemd-random-seed-save.service.in \ @@ -586,6 +589,16 @@ systemd_modules_load_CFLAGS = \ systemd_modules_load_LDADD = \ libsystemd-basic.la +systemd_remount_api_vfs_SOURCES = \ + src/remount-api-vfs.c \ + src/mount-setup.c + +systemd_remount_api_vfs_CFLAGS = \ + $(AM_CFLAGS) + +systemd_remount_api_vfs_LDADD = \ + libsystemd-basic.la + systemd_cgroups_agent_SOURCES = \ src/cgroups-agent.c \ src/dbus-common.c diff --git a/fixme b/fixme index b43a102130..2ff9a80ae3 100644 --- a/fixme +++ b/fixme @@ -42,8 +42,6 @@ * selinux policy loading -* place /etc/inittab with explaining blurb. - * fingerprint.target, wireless.target, gps.target * set_put(), hashmap_put() return values checken. i.e. == 0 macht kein free()! @@ -60,10 +58,10 @@ * bash completion a la gdbus -* api mounts gegen fstab mergen und remounten - External: +* place /etc/inittab with explaining blurb. + * procps, psmisc, sysvinit-tools, hostname → util-linux-ng * nologin nach /var/run https://bugzilla.redhat.com/show_bug.cgi?id=624489 diff --git a/src/hashmap.c b/src/hashmap.c index 6b6e909ba3..51f00131f4 100644 --- a/src/hashmap.c +++ b/src/hashmap.c @@ -173,6 +173,15 @@ void hashmap_free(Hashmap*h) { free(h); } +void hashmap_free_free(Hashmap *h) { + void *p; + + while ((p = hashmap_steal_first(h))) + free(p); + + hashmap_free(h); +} + void hashmap_clear(Hashmap *h) { if (!h) return; diff --git a/src/hashmap.h b/src/hashmap.h index 9ab66c51b8..c48d6b31f6 100644 --- a/src/hashmap.h +++ b/src/hashmap.h @@ -47,6 +47,7 @@ int trivial_compare_func(const void *a, const void *b); Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func); void hashmap_free(Hashmap *h); +void hashmap_free_free(Hashmap *h); Hashmap *hashmap_copy(Hashmap *h); int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func); diff --git a/src/mount-setup.c b/src/mount-setup.c index ecd9e181c5..8b4d8c7f44 100644 --- a/src/mount-setup.c +++ b/src/mount-setup.c @@ -68,11 +68,11 @@ bool mount_point_is_api(const char *path) { * should be ignored */ for (i = 0; i < ELEMENTSOF(mount_table); i ++) - if (path_startswith(path, mount_table[i].where)) + if (path_equal(path, mount_table[i].where)) return true; for (i = 0; i < ELEMENTSOF(ignore_paths); i++) - if (path_startswith(path, ignore_paths[i])) + if (path_equal(path, ignore_paths[i])) return true; return path_startswith(path, "/cgroup/"); diff --git a/src/remount-api-vfs.c b/src/remount-api-vfs.c new file mode 100644 index 0000000000..d51a584f2f --- /dev/null +++ b/src/remount-api-vfs.c @@ -0,0 +1,147 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "util.h" +#include "set.h" +#include "mount-setup.h" + +/* Goes through /etc/fstab and remounts all API file systems, applying + * options that are in /etc/fstab that systemd might not have + * respected */ + +int main(int argc, char *argv[]) { + int ret = 1; + FILE *f = NULL; + struct mntent* me; + Hashmap *pids = NULL; + + if (argc > 1) { + log_error("This program takes no argument."); + return 1; + } + + log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_parse_environment(); + log_open(); + + if (!(f = setmntent("/etc/fstab", "r"))) { + log_error("Failed to open /etc/fstab: %m"); + goto finish; + } + + if (!(pids = hashmap_new(trivial_hash_func, trivial_compare_func))) { + log_error("Failed to allocate set"); + goto finish; + } + + ret = 0; + + while ((me = getmntent(f))) { + pid_t pid; + int k; + char *s; + + if (!mount_point_is_api(me->mnt_dir)) + continue; + + log_debug("Remounting %s", me->mnt_dir); + + if ((pid = fork()) < 0) { + log_error("Failed to fork: %m"); + ret = 1; + continue; + } + + if (pid == 0) { + const char *arguments[5]; + /* Child */ + + arguments[0] = "/bin/mount"; + arguments[1] = me->mnt_dir; + arguments[2] = "-o"; + arguments[3] = "remount"; + arguments[4] = NULL; + + execv("/bin/mount", (char **) arguments); + + log_error("Failed to execute /bin/mount: %m"); + _exit(1); + } + + /* Parent */ + + s = strdup(me->mnt_dir); + + if ((k = hashmap_put(pids, UINT_TO_PTR(pid), s)) < 0) { + log_error("Failed to add PID to set: %s", strerror(-k)); + ret = 1; + continue; + } + } + + while (!hashmap_isempty(pids)) { + siginfo_t si; + char *s; + + zero(si); + if (waitid(P_ALL, 0, &si, WEXITED) < 0) { + + if (errno == EINTR) + continue; + + log_error("waitid() failed: %m"); + ret = 1; + break; + } + + if ((s = hashmap_remove(pids, UINT_TO_PTR(si.si_pid)))) { + if (!is_clean_exit(si.si_code, si.si_status)) { + if (si.si_code == CLD_EXITED) + log_error("/bin/mount for %s exited with exit status %i.", s, si.si_status); + else + log_error("/bin/mount for %s terminated by signal %s.", s, signal_to_string(si.si_status)); + + ret = 1; + } + + free(s); + } + } + +finish: + + if (pids) + hashmap_free_free(pids); + + if (f) + endmntent(f); + + return ret; +} diff --git a/src/set.c b/src/set.c index 331d37e3de..097b9d3aae 100644 --- a/src/set.c +++ b/src/set.c @@ -38,12 +38,7 @@ void set_free(Set* s) { } void set_free_free(Set *s) { - void *p; - - while ((p = set_steal_first(s))) - free(p); - - set_free(s); + hashmap_free_free(MAKE_HASHMAP(s)); } int set_ensure_allocated(Set **s, hash_func_t hash_func, compare_func_t compare_func) { diff --git a/units/.gitignore b/units/.gitignore index 23857d2310..801e9d61e0 100644 --- a/units/.gitignore +++ b/units/.gitignore @@ -1,4 +1,5 @@ systemd-modules-load.service +systemd-remount-api-vfs.service systemd-auto-console-getty.service systemd-shutdownd.service systemd-random-seed-load.service diff --git a/units/systemd-modules-load.service.in b/units/systemd-modules-load.service.in index 2390e02b3f..7a94040fae 100644 --- a/units/systemd-modules-load.service.in +++ b/units/systemd-modules-load.service.in @@ -13,6 +13,7 @@ Before=shutdown.target [Service] Type=oneshot +RemainAfterExit=yes ExecStart=@rootlibexecdir@/systemd-modules-load [Install] diff --git a/units/systemd-remount-api-vfs.service.in b/units/systemd-remount-api-vfs.service.in new file mode 100644 index 0000000000..4aba863aa9 --- /dev/null +++ b/units/systemd-remount-api-vfs.service.in @@ -0,0 +1,20 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +[Unit] +Description=Remount API VFS +DefaultDependencies=no +Conflicts=shutdown.target +Before=shutdown.target local-fs.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-remount-vfs + +[Install] +WantedBy=sysinit.target -- cgit v1.2.3-54-g00ecf From addab137cd8d318e4f543ca56018ee23d51aaca9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 21 Aug 2010 03:57:47 +0200 Subject: syslog: add minimal syslog/kmsg bridge syslogd --- .gitignore | 1 + Makefile.am | 17 +- src/fdset.c | 2 +- src/kmsg-syslogd.c | 545 ++++++++++++++++++++++++++++++++++ src/log.c | 5 +- src/logger.c | 7 +- units/.gitignore | 1 + units/systemd-kmsg-syslogd.service.in | 16 + units/systemd-kmsg-syslogd.socket | 20 ++ 9 files changed, 605 insertions(+), 9 deletions(-) create mode 100644 src/kmsg-syslogd.c create mode 100644 units/systemd-kmsg-syslogd.service.in create mode 100644 units/systemd-kmsg-syslogd.socket (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 42d9c8c679..1cee1b6328 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-kmsg-syslogd systemd-remount-api-vfs test-hostname systemd-modules-load diff --git a/Makefile.am b/Makefile.am index ca25bafa7d..aa2998d99d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -75,7 +75,8 @@ rootlibexec_PROGRAMS = \ systemd-shutdownd \ systemd-auto-console-getty \ systemd-modules-load \ - systemd-remount-api-vfs + systemd-remount-api-vfs \ + systemd-kmsg-syslogd noinst_PROGRAMS = \ test-engine \ @@ -143,6 +144,7 @@ dist_systemunit_DATA = \ units/systemd-initctl.socket \ units/systemd-logger.socket \ units/systemd-shutdownd.socket \ + units/systemd-kmsg-syslogd.socket \ units/dev-hugepages.automount \ units/dev-hugepages.mount \ units/dev-mqueue.automount \ @@ -175,6 +177,7 @@ nodist_systemunit_DATA = \ units/systemd-initctl.service \ units/systemd-logger.service \ units/systemd-shutdownd.service \ + units/systemd-kmsg-syslogd.service \ units/systemd-auto-console-getty.service \ units/systemd-modules-load.service \ units/systemd-remount-api-vfs.service \ @@ -200,6 +203,7 @@ EXTRA_DIST = \ units/systemd-initctl.service.in \ units/systemd-logger.service.in \ units/systemd-shutdownd.service.in \ + units/systemd-kmsg-syslogd.service.in \ units/systemd-auto-console-getty.service.in \ units/systemd-modules-load.service.in \ units/systemd-remount-api-vfs.service.in \ @@ -612,6 +616,17 @@ systemd_cgroups_agent_LDADD = \ libsystemd-basic.la \ $(DBUS_LIBS) +systemd_kmsg_syslogd_SOURCES = \ + src/kmsg-syslogd.c \ + src/sd-daemon.c \ + src/fdset.c + +systemd_kmsg_syslogd_CFLAGS = \ + $(AM_CFLAGS) + +systemd_kmsg_syslogd_LDADD = \ + libsystemd-basic.la + systemctl_SOURCES = \ src/systemctl.c \ src/utmp-wtmp.c \ diff --git a/src/fdset.c b/src/fdset.c index b482f0b244..29e75a0346 100644 --- a/src/fdset.c +++ b/src/fdset.c @@ -49,7 +49,7 @@ void fdset_free(FDSet *s) { * here, so that the EBADFD that valgrind will return * us on close() doesn't influence us */ - log_warning("Closing left-over fd %i", PTR_TO_FD(p)); + /* log_warning("Closing left-over fd %i", PTR_TO_FD(p)); */ close_nointr(PTR_TO_FD(p)); } diff --git a/src/kmsg-syslogd.c b/src/kmsg-syslogd.c new file mode 100644 index 0000000000..f5c8e7121d --- /dev/null +++ b/src/kmsg-syslogd.c @@ -0,0 +1,545 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "log.h" +#include "sd-daemon.h" +#include "fdset.h" + +#define SERVER_FD_MAX 16 +#define TIMEOUT ((int) (10*MSEC_PER_SEC)) + +typedef struct Stream Stream; + +typedef struct Server { + FDSet *syslog_fds; + int kmsg_fd; + int epoll_fd; + int signal_fd; +} Server; + +static void server_done(Server *s) { + assert(s); + + if (s->epoll_fd >= 0) + close_nointr_nofail(s->epoll_fd); + + if (s->kmsg_fd >= 0) + close_nointr_nofail(s->kmsg_fd); + + if (s->signal_fd >= 0) + close_nointr_nofail(s->signal_fd); + + if (s->syslog_fds) + fdset_free(s->syslog_fds); +} + +static int server_init(Server *s, unsigned n_sockets) { + int r; + unsigned i; + struct epoll_event ev; + sigset_t mask; + + assert(s); + assert(n_sockets > 0); + + zero(*s); + + s->kmsg_fd = s->signal_fd = -1; + + if ((s->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) { + r = -errno; + log_error("Failed to create epoll object: %s", strerror(errno)); + goto fail; + } + + if (!(s->syslog_fds = fdset_new())) { + r = -ENOMEM; + log_error("Failed to allocate file descriptor set: %s", strerror(errno)); + goto fail; + } + + for (i = 0; i < n_sockets; i++) { + int fd, one = 1; + + fd = SD_LISTEN_FDS_START+i; + + if ((r = sd_is_socket(fd, AF_UNSPEC, SOCK_DGRAM, -1)) < 0) { + log_error("Failed to determine file descriptor type: %s", strerror(-r)); + goto fail; + } + + if (!r) { + log_error("Wrong file descriptor type."); + r = -EINVAL; + goto fail; + } + + if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) + log_error("SO_PASSCRED failed: %m"); + + zero(ev); + ev.events = EPOLLIN; + ev.data.fd = fd; + if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) { + r = -errno; + log_error("Failed to add server fd to epoll object: %s", strerror(errno)); + goto fail; + } + + if ((r = fdset_put(s->syslog_fds, fd)) < 0) { + log_error("Failed to store file descriptor in set: %s", strerror(-r)); + goto fail; + } + } + + if ((s->kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC)) < 0) { + log_error("Failed to open /dev/kmsg for logging: %m"); + return -errno; + } + + assert_se(sigemptyset(&mask) == 0); + sigset_add_many(&mask, SIGINT, SIGTERM, -1); + assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0); + + if ((s->signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) { + log_error("signalfd(): %m"); + return -errno; + } + + zero(ev); + ev.events = EPOLLIN; + ev.data.fd = s->signal_fd; + + if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->signal_fd, &ev) < 0) { + log_error("epoll_ctl(): %m"); + return -errno; + } + + return 0; + +fail: + server_done(s); + return r; +} + +static int read_priority(const char **buf) { + int priority; + size_t n; + const char *p; + int a, b, c; + + assert(buf); + assert(*buf); + + p = *buf; + n = strlen(p); + + if (n < 3 || p[0] != '<') + goto fail; + + if (p[2] == '>') { + a = b = 0; + c = undecchar(p[1]); + p += 3; + } else if (n >= 4 && p[3] == '>') { + a = 0; + b = undecchar(p[1]); + c = undecchar(p[2]); + p += 4; + } else if (n >= 5 && p[4] == '>') { + a = undecchar(p[1]); + b = undecchar(p[2]); + c = undecchar(p[3]); + p += 5; + } else + goto fail; + + if (a < 0 || b < 0 || c < 0) + goto fail; + + *buf = p; + + priority = 100*a + 10*b + c; + return LOG_PRI(priority); + +fail: + return LOG_INFO; +} + +static void skip_date(const char **buf) { + enum { + LETTER, + SPACE, + NUMBER, + SPACE_OR_NUMBER, + COLON + } sequence[] = { + LETTER, LETTER, LETTER, + SPACE, + SPACE_OR_NUMBER, NUMBER, + SPACE, + SPACE_OR_NUMBER, NUMBER, + COLON, + SPACE_OR_NUMBER, NUMBER, + COLON, + SPACE_OR_NUMBER, NUMBER, + SPACE + }; + + const char *p; + unsigned i; + + assert(buf); + assert(*buf); + + p = *buf; + + for (i = 0; i < ELEMENTSOF(sequence); i++, p++) { + + if (!*p) + return; + + switch (sequence[i]) { + + case SPACE: + if (*p != ' ') + return; + break; + + case SPACE_OR_NUMBER: + if (*p == ' ') + break; + + /* fall through */ + + case NUMBER: + if (*p < '0' || *p > '9') + return; + + break; + + case LETTER: + if (!(*p >= 'A' && *p <= 'Z') && + !(*p >= 'a' && *p <= 'z')) + return; + + break; + + case COLON: + if (*p != ':') + return; + break; + + } + } + + *buf = p; +} + +static int read_process(const char **buf, struct iovec *iovec) { + const char *p; + size_t l; + + assert(buf); + assert(*buf); + assert(iovec); + + p = *buf; + + p += strspn(p, WHITESPACE); + l = strcspn(p, WHITESPACE); + + if (l <= 0 || + p[l-1] != ':') + return 0; + + l--; + + if (p[l-1] == ']') { + size_t k = l-1; + + for (;;) { + + if (p[k] == '[') { + l = k; + break; + } + + if (k == 0) + break; + + k--; + } + } + + iovec->iov_base = (char*) p; + iovec->iov_len = l; + *buf = p + l; + return 1; +} + +static void skip_pid(const char **buf) { + const char *p; + + assert(buf); + assert(*buf); + + p = *buf; + + if (*p != '[') + return; + + p++; + p += strspn(p, "0123456789"); + + if (*p != ']') + return; + + p++; + + *buf = p; +} + +static int write_message(Server *s, const char *buf, struct ucred *ucred) { + ssize_t k; + char priority[4], pid[16]; + struct iovec iovec[5]; + unsigned i = 0; + char *process = NULL; + int r = 0; + + assert(s); + assert(buf); + + /* First, set priority field */ + snprintf(priority, sizeof(priority), "<%i>", read_priority(&buf)); + char_array_0(priority); + IOVEC_SET_STRING(iovec[i++], priority); + + /* Second, skip date */ + skip_date(&buf); + + /* Then, add process if set */ + if (read_process(&buf, &iovec[i]) > 0) + i++; + else if (ucred && get_process_name(ucred->pid, &process) >= 0) + IOVEC_SET_STRING(iovec[i++], process); + + /* Skip the stored PID if we have a better one */ + if (ucred) { + snprintf(pid, sizeof(pid), "[%lu]: ", (unsigned long) ucred->pid); + char_array_0(pid); + IOVEC_SET_STRING(iovec[i++], pid); + + skip_pid(&buf); + + if (*buf == ':') + buf++; + + buf += strspn(buf, WHITESPACE); + } + + /* Is the remaining message empty? */ + if (*buf) { + + /* And the rest is the message */ + IOVEC_SET_STRING(iovec[i++], buf); + IOVEC_SET_STRING(iovec[i++], "\n"); + + if ((k = writev(s->kmsg_fd, iovec, i)) <= 0) { + log_error("Failed to write log message to kmsg: %s", k < 0 ? strerror(errno) : "short write"); + r = k < 0 ? -errno : -EIO; + } + } + + free(process); + + return r; +} + +static int process_event(Server *s, struct epoll_event *ev) { + assert(s); + + if (ev->events != EPOLLIN) { + log_info("Got invalid event from epoll."); + return -EIO; + } + + if (ev->data.fd == s->signal_fd) { + struct signalfd_siginfo sfsi; + ssize_t n; + + if ((n = read(s->signal_fd, &sfsi, sizeof(sfsi))) != sizeof(sfsi)) { + + if (n >= 0) + return -EIO; + + if (errno == EINTR || errno == EAGAIN) + return 0; + + return -errno; + } + + log_debug("Received SIG%s", strna(signal_to_string(sfsi.ssi_signo))); + return 0; + + } else { + for (;;) { + char buf[LINE_MAX+1]; + struct msghdr msghdr; + struct iovec iovec; + struct ucred *ucred; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; + } control; + ssize_t n; + int k; + char *e; + + zero(iovec); + iovec.iov_base = buf; + iovec.iov_len = sizeof(buf)-1; + + zero(control); + zero(msghdr); + msghdr.msg_iov = &iovec; + msghdr.msg_iovlen = 1; + msghdr.msg_control = &control; + msghdr.msg_controllen = sizeof(control); + + if ((n = recvmsg(ev->data.fd, &msghdr, MSG_DONTWAIT)) < 0) { + + if (errno == EINTR || errno == EAGAIN) + return 1; + + log_error("recvmsg() failed: %m"); + return -errno; + } + + if (msghdr.msg_controllen >= CMSG_LEN(sizeof(struct ucred)) && + control.cmsghdr.cmsg_level == SOL_SOCKET && + control.cmsghdr.cmsg_type == SCM_CREDENTIALS && + control.cmsghdr.cmsg_len == CMSG_LEN(sizeof(struct ucred))) + ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr); + else + ucred = NULL; + + if ((e = memchr(buf, '\n', n))) + *e = 0; + else + buf[n] = 0; + + if ((k = write_message(s, strstrip(buf), ucred)) < 0) + return k; + } + } + + return 1; +} + +int main(int argc, char *argv[]) { + Server server; + int r = 3, n; + + if (getppid() != 1) { + log_error("This program should be invoked by init only."); + return 1; + } + + if (argc > 1) { + log_error("This program does not take arguments."); + return 1; + } + + log_set_target(LOG_TARGET_KMSG); + log_parse_environment(); + log_open(); + + if ((n = sd_listen_fds(true)) < 0) { + log_error("Failed to read listening file descriptors from environment: %s", strerror(-r)); + return 1; + } + + if (n <= 0 || n > SERVER_FD_MAX) { + log_error("No or too many file descriptors passed."); + return 2; + } + + if (server_init(&server, (unsigned) n) < 0) + return 3; + + log_debug("systemd-kmsg-syslogd running as pid %lu", (unsigned long) getpid()); + + sd_notify(false, + "READY=1\n" + "STATUS=Processing messages..."); + + for (;;) { + struct epoll_event event; + int k; + + if ((k = epoll_wait(server.epoll_fd, &event, 1, TIMEOUT)) < 0) { + + if (errno == EINTR) + continue; + + log_error("epoll_wait() failed: %m"); + goto fail; + } + + if (k <= 0) + break; + + if ((k = process_event(&server, &event)) < 0) + goto fail; + + if (k == 0) + break; + } + + r = 0; + + log_debug("systemd-kmsg-syslogd stopped as pid %lu", (unsigned long) getpid()); + +fail: + sd_notify(false, + "STATUS=Shutting down..."); + + server_done(&server); + + return r; +} diff --git a/src/log.c b/src/log.c index 729b9ea4f6..3c42e8e517 100644 --- a/src/log.c +++ b/src/log.c @@ -32,7 +32,6 @@ #include "macro.h" #define SYSLOG_TIMEOUT_USEC (5*USEC_PER_SEC) -#define LOG_BUFFER_MAX 1024 static LogTarget log_target = LOG_TARGET_CONSOLE; static int log_max_level = LOG_INFO; @@ -417,7 +416,7 @@ int log_meta( const char *func, const char *format, ...) { - char buffer[LOG_BUFFER_MAX]; + char buffer[LINE_MAX]; int saved_errno, r; va_list ap; @@ -444,7 +443,7 @@ void log_assert( const char *func, const char *format, ...) { - static char buffer[LOG_BUFFER_MAX]; + static char buffer[LINE_MAX]; int saved_errno = errno; va_list ap; diff --git a/src/logger.c b/src/logger.c index 95d58fc1b3..5a28c2197f 100644 --- a/src/logger.c +++ b/src/logger.c @@ -38,7 +38,6 @@ #include "sd-daemon.h" #include "tcpwrap.h" -#define STREAM_BUFFER 2048 #define STREAMS_MAX 256 #define SERVER_FD_MAX 16 #define TIMEOUT ((int) (10*MSEC_PER_SEC)) @@ -85,7 +84,7 @@ struct Stream { bool prefix; - char buffer[STREAM_BUFFER]; + char buffer[LINE_MAX]; size_t length; LIST_FIELDS(Stream, stream); @@ -297,7 +296,7 @@ static int stream_process(Stream *s, usec_t ts) { int r; assert(s); - if ((l = read(s->fd, s->buffer+s->length, STREAM_BUFFER-s->length)) < 0) { + if ((l = read(s->fd, s->buffer+s->length, LINE_MAX-s->length)) < 0) { if (errno == EAGAIN) return 0; @@ -617,7 +616,7 @@ int main(int argc, char *argv[]) { r = 0; - log_info("systemd-logger stopped as pid %lu", (unsigned long) getpid()); + log_debug("systemd-logger stopped as pid %lu", (unsigned long) getpid()); fail: sd_notify(false, diff --git a/units/.gitignore b/units/.gitignore index 801e9d61e0..82a24170cf 100644 --- a/units/.gitignore +++ b/units/.gitignore @@ -1,3 +1,4 @@ +systemd-kmsg-syslogd.service systemd-modules-load.service systemd-remount-api-vfs.service systemd-auto-console-getty.service diff --git a/units/systemd-kmsg-syslogd.service.in b/units/systemd-kmsg-syslogd.service.in new file mode 100644 index 0000000000..e2e0f55b78 --- /dev/null +++ b/units/systemd-kmsg-syslogd.service.in @@ -0,0 +1,16 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# See systemd.special(7) for details + +[Unit] +Description=systemd Syslog Kernel Log Buffer Bridge +DefaultDependencies=no + +[Service] +ExecStart=@rootlibexecdir@/systemd-kmsg-syslogd +NotifyAccess=all diff --git a/units/systemd-kmsg-syslogd.socket b/units/systemd-kmsg-syslogd.socket new file mode 100644 index 0000000000..7487cd5def --- /dev/null +++ b/units/systemd-kmsg-syslogd.socket @@ -0,0 +1,20 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# See systemd.special(7) for details + +[Unit] +Description=Syslog Socket +DefaultDependencies=no +Before=sockets.target + +[Socket] +ListenDatagram=/dev/log +SocketMode=0666 + +[Install] +WantedBy=sysinit.target -- cgit v1.2.3-54-g00ecf From 93a45c562a1989dfbb2dd08c65f8a21b02959934 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 25 Aug 2010 03:09:57 +0200 Subject: serial: use seperate getty template for serial ttys --- .gitignore | 2 +- Makefile.am | 20 +-- src/auto-console-getty.c | 189 ---------------------------- src/auto-serial-getty.c | 189 ++++++++++++++++++++++++++++ units/.gitignore | 1 + units/getty@.service.m4 | 2 +- units/serial-getty@.service.m4 | 37 ++++++ units/systemd-auto-console-getty.service.in | 16 --- units/systemd-auto-serial-getty.service | 16 +++ 9 files changed, 256 insertions(+), 216 deletions(-) delete mode 100644 src/auto-console-getty.c create mode 100644 src/auto-serial-getty.c create mode 100644 units/serial-getty@.service.m4 delete mode 100644 units/systemd-auto-console-getty.service.in create mode 100644 units/systemd-auto-serial-getty.service (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 1cee1b6328..2c92ae05b7 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ systemd-kmsg-syslogd systemd-remount-api-vfs test-hostname systemd-modules-load -systemd-auto-console-getty +systemd-auto-serial-getty systemd-shutdownd systemd-random-seed systemd-update-utmp diff --git a/Makefile.am b/Makefile.am index aa2998d99d..859d332679 100644 --- a/Makefile.am +++ b/Makefile.am @@ -73,7 +73,7 @@ rootlibexec_PROGRAMS = \ systemd-update-utmp \ systemd-random-seed \ systemd-shutdownd \ - systemd-auto-console-getty \ + systemd-auto-serial-getty \ systemd-modules-load \ systemd-remount-api-vfs \ systemd-kmsg-syslogd @@ -171,6 +171,7 @@ dist_systemunit_DATA = \ nodist_systemunit_DATA = \ units/sysinit.target \ units/getty@.service \ + units/serial-getty@.service \ units/graphical.target \ units/remote-fs.target \ units/multi-user.target \ @@ -178,7 +179,7 @@ nodist_systemunit_DATA = \ units/systemd-logger.service \ units/systemd-shutdownd.service \ units/systemd-kmsg-syslogd.service \ - units/systemd-auto-console-getty.service \ + units/systemd-auto-serial-getty.service \ units/systemd-modules-load.service \ units/systemd-remount-api-vfs.service \ units/systemd-update-utmp-runlevel.service \ @@ -197,6 +198,7 @@ nodist_sessionunit_DATA = \ EXTRA_DIST = \ units/sysinit.target.m4 \ units/getty@.service.m4 \ + units/serial-getty@.service.m4 \ units/graphical.target.m4 \ units/multi-user.target.m4 \ units/remote-fs.target.m4 \ @@ -204,7 +206,7 @@ EXTRA_DIST = \ units/systemd-logger.service.in \ units/systemd-shutdownd.service.in \ units/systemd-kmsg-syslogd.service.in \ - units/systemd-auto-console-getty.service.in \ + units/systemd-auto-serial-getty.service.in \ units/systemd-modules-load.service.in \ units/systemd-remount-api-vfs.service.in \ units/systemd-update-utmp-runlevel.service.in \ @@ -573,15 +575,15 @@ systemd_shutdownd_CFLAGS = \ systemd_shutdownd_LDADD = \ libsystemd-basic.la -systemd_auto_console_getty_SOURCES = \ - src/auto-console-getty.c \ +systemd_auto_serial_getty_SOURCES = \ + src/auto-serial-getty.c \ src/dbus-common.c -systemd_auto_console_getty_CFLAGS = \ +systemd_auto_serial_getty_CFLAGS = \ $(AM_CFLAGS) \ $(DBUS_CFLAGS) -systemd_auto_console_getty_LDADD = \ +systemd_auto_serial_getty_LDADD = \ libsystemd-basic.la \ $(DBUS_LIBS) @@ -896,14 +898,14 @@ install-data-hook: $(LN_S) $(systemunitdir)/reboot.target ctrl-alt-del.target && \ $(LN_S) $(systemunitdir)/rescue.target kbrequest.target ) ( cd $(DESTDIR)$(pkgsysconfdir)/system/getty.target.wants && \ - rm -f getty@tty1.service getty@tty2.service getty@tty3.service getty@tty4.service getty@tty5.service getty@tty6.service systemd-auto-console-getty.service && \ + rm -f getty@tty1.service getty@tty2.service getty@tty3.service getty@tty4.service getty@tty5.service getty@tty6.service systemd-auto-serial-getty.service && \ $(LN_S) $(systemunitdir)/getty@.service getty@tty1.service && \ $(LN_S) $(systemunitdir)/getty@.service getty@tty2.service && \ $(LN_S) $(systemunitdir)/getty@.service getty@tty3.service && \ $(LN_S) $(systemunitdir)/getty@.service getty@tty4.service && \ $(LN_S) $(systemunitdir)/getty@.service getty@tty5.service && \ $(LN_S) $(systemunitdir)/getty@.service getty@tty6.service && \ - $(LN_S) $(systemunitdir)/systemd-auto-console-getty.service systemd-auto-console-getty.service ) + $(LN_S) $(systemunitdir)/systemd-auto-serial-getty.service systemd-auto-serial-getty.service ) ( cd $(DESTDIR)$(pkgsysconfdir)/system/multi-user.target.wants && \ rm -f getty.target remote-fs.target && \ $(LN_S) $(systemunitdir)/getty.target getty.target && \ diff --git a/src/auto-console-getty.c b/src/auto-console-getty.c deleted file mode 100644 index 44d2eff732..0000000000 --- a/src/auto-console-getty.c +++ /dev/null @@ -1,189 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include - -#include - -#include "util.h" -#include "log.h" -#include "dbus-common.h" - -static int spawn_getty(DBusConnection *bus, const char *console) { - DBusMessage *m = NULL, *reply = NULL; - DBusError error; - const char *fail = "fail"; - char *name; - int r = -EIO; - - dbus_error_init(&error); - - assert(bus); - assert(console); - - /* FIXME: we probably should escape the tty name properly here */ - if (asprintf(&name, "getty@%s.service", console) < 0) - return -ENOMEM; - - if (!(m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartUnit"))) { - log_error("Could not allocate message."); - goto finish; - } - - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_STRING, &fail, - DBUS_TYPE_INVALID)) { - log_error("Could not attach target and flag information to message."); - goto finish; - } - - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to start unit: %s", error.message); - goto finish; - } - - r = 0; - -finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - - dbus_error_free(&error); - - free(name); - - return r; -} - -static int parse_proc_cmdline_word(const char *word, char **console) { - assert(word); - - if (startswith(word, "console=")) { - const char *k; - size_t l; - char *w = NULL; - - k = word + 8; - l = strcspn(k, ","); - - if (l < 4 || - !startswith(k, "tty") || - k[3+strspn(k+3, "0123456789")] != 0) { - - if (!(w = strndup(k, l))) - return -ENOMEM; - - } - - free(*console); - *console = w; - } - - return 0; -} - -static int parse_proc_cmdline(char **console) { - char *line; - int r; - char *w; - size_t l; - char *state; - - assert(console); - - if ((r = read_one_line_file("/tmp/cmdline", &line)) < 0) { - log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r)); - return 0; - } - - FOREACH_WORD_QUOTED(w, l, line, state) { - char *word; - - if (!(word = strndup(w, l))) { - r = -ENOMEM; - goto finish; - } - - r = parse_proc_cmdline_word(word, console); - free(word); - - if (r < 0) - goto finish; - } - - r = 0; - -finish: - free(line); - return r; -} - -int main(int argc, char *argv[]) { - DBusError error; - int r = 1; - char *console = NULL; - DBusConnection *bus = NULL; - - dbus_error_init(&error); - - if (argc > 1) { - log_error("This program does not take arguments."); - return 1; - } - - log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); - log_parse_environment(); - log_open(); - - if (bus_connect(DBUS_BUS_SYSTEM, &bus, NULL, &error) < 0) { - log_error("Failed to get D-Bus connection: %s", error.message); - goto finish; - } - - if (parse_proc_cmdline(&console) < 0) - goto finish; - - if (console) - if (spawn_getty(bus, console) < 0) - goto finish; - - r = 0; - -finish: - free(console); - - if (bus) { - dbus_connection_close(bus); - dbus_connection_unref(bus); - } - - dbus_error_free(&error); - - dbus_shutdown(); - - return r; -} diff --git a/src/auto-serial-getty.c b/src/auto-serial-getty.c new file mode 100644 index 0000000000..7bc2fba8da --- /dev/null +++ b/src/auto-serial-getty.c @@ -0,0 +1,189 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include + +#include "util.h" +#include "log.h" +#include "dbus-common.h" + +static int spawn_getty(DBusConnection *bus, const char *console) { + DBusMessage *m = NULL, *reply = NULL; + DBusError error; + const char *fail = "fail"; + char *name; + int r = -EIO; + + dbus_error_init(&error); + + assert(bus); + assert(console); + + /* FIXME: we probably should escape the tty name properly here */ + if (asprintf(&name, "serial-getty@%s.service", console) < 0) + return -ENOMEM; + + if (!(m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartUnit"))) { + log_error("Could not allocate message."); + goto finish; + } + + if (!dbus_message_append_args(m, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &fail, + DBUS_TYPE_INVALID)) { + log_error("Could not attach target and flag information to message."); + goto finish; + } + + if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { + log_error("Failed to start unit: %s", error.message); + goto finish; + } + + r = 0; + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); + + free(name); + + return r; +} + +static int parse_proc_cmdline_word(const char *word, char **console) { + assert(word); + + if (startswith(word, "console=")) { + const char *k; + size_t l; + char *w = NULL; + + k = word + 8; + l = strcspn(k, ","); + + if (l < 4 || + !startswith(k, "tty") || + k[3+strspn(k+3, "0123456789")] != 0) { + + if (!(w = strndup(k, l))) + return -ENOMEM; + + } + + free(*console); + *console = w; + } + + return 0; +} + +static int parse_proc_cmdline(char **console) { + char *line; + int r; + char *w; + size_t l; + char *state; + + assert(console); + + if ((r = read_one_line_file("/proc/cmdline", &line)) < 0) { + log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r)); + return 0; + } + + FOREACH_WORD_QUOTED(w, l, line, state) { + char *word; + + if (!(word = strndup(w, l))) { + r = -ENOMEM; + goto finish; + } + + r = parse_proc_cmdline_word(word, console); + free(word); + + if (r < 0) + goto finish; + } + + r = 0; + +finish: + free(line); + return r; +} + +int main(int argc, char *argv[]) { + DBusError error; + int r = 1; + char *console = NULL; + DBusConnection *bus = NULL; + + dbus_error_init(&error); + + if (argc > 1) { + log_error("This program does not take arguments."); + return 1; + } + + log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_parse_environment(); + log_open(); + + if (bus_connect(DBUS_BUS_SYSTEM, &bus, NULL, &error) < 0) { + log_error("Failed to get D-Bus connection: %s", error.message); + goto finish; + } + + if (parse_proc_cmdline(&console) < 0) + goto finish; + + if (console) + if (spawn_getty(bus, console) < 0) + goto finish; + + r = 0; + +finish: + free(console); + + if (bus) { + dbus_connection_close(bus); + dbus_connection_unref(bus); + } + + dbus_error_free(&error); + + dbus_shutdown(); + + return r; +} diff --git a/units/.gitignore b/units/.gitignore index 82a24170cf..35f790849e 100644 --- a/units/.gitignore +++ b/units/.gitignore @@ -1,3 +1,4 @@ +serial-getty@.service systemd-kmsg-syslogd.service systemd-modules-load.service systemd-remount-api-vfs.service diff --git a/units/getty@.service.m4 b/units/getty@.service.m4 index 01343c497f..4b65d5b906 100644 --- a/units/getty@.service.m4 +++ b/units/getty@.service.m4 @@ -30,7 +30,7 @@ Before=getty.target [Service] Environment=TERM=linux -ExecStart=GETTY %I +ExecStart=-GETTY %I Restart=restart-always RestartSec=0 KillMode=process-group diff --git a/units/serial-getty@.service.m4 b/units/serial-getty@.service.m4 new file mode 100644 index 0000000000..b91ed98330 --- /dev/null +++ b/units/serial-getty@.service.m4 @@ -0,0 +1,37 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +[Unit] +Description=Serial Getty on %I +Requires=dev-%i.device +After=dev-%i.device +m4_ifdef(`TARGET_FEDORA', +After=rc-local.service +)m4_dnl +m4_ifdef(`TARGET_ARCH', +After=rc-local.service +)m4_dnl + +# If additional gettys are spawned during boot (possibly by +# systemd-auto-console-getty) then we should make sure that this is +# synchronized before getty.target, even though getty.target didn't +# actually pull it in. +Before=getty.target + +[Service] +Environment=TERM=vt100-nav +m4_ifdef(`TARGET_FEDORA', +ExecStartPre=-/sbin/securetty %I +)m4_dnl +ExecStart=-/sbin/agetty -s %I 115200,38400,9600 +Restart=restart-always +RestartSec=0 +KillMode=process-group + +# Some login implementations ignore SIGTERM, so we send SIGHUP +# instead, to ensure that login terminates cleanly. +KillSignal=SIGHUP diff --git a/units/systemd-auto-console-getty.service.in b/units/systemd-auto-console-getty.service.in deleted file mode 100644 index 6542cab18a..0000000000 --- a/units/systemd-auto-console-getty.service.in +++ /dev/null @@ -1,16 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. - -[Unit] -Description=Automatically Spawn getty on Kernel Console - -[Service] -Type=oneshot -ExecStart=@rootlibexecdir@/systemd-auto-console-getty - -[Install] -WantedBy=getty.target diff --git a/units/systemd-auto-serial-getty.service b/units/systemd-auto-serial-getty.service new file mode 100644 index 0000000000..8051c7446a --- /dev/null +++ b/units/systemd-auto-serial-getty.service @@ -0,0 +1,16 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +[Unit] +Description=Automatically Spawn getty on Serial Kernel Console + +[Service] +Type=oneshot +ExecStart=@rootlibexecdir@/systemd-auto-serial-getty + +[Install] +WantedBy=getty.target -- cgit v1.2.3-54-g00ecf From 97c4a07df982ee967705022feaba9be33947abf0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 16 Sep 2010 00:36:41 +0200 Subject: vconsole: add new utility to initialize the virtual console --- .gitignore | 2 +- Makefile.am | 14 +- src/conf-parser.c | 2 - src/kmod-setup.c | 25 +--- src/load-fragment.c | 2 - src/mount.c | 4 +- src/util.c | 182 ++++++++++++++++++++++++- src/util.h | 8 +- src/vconsole-setup.c | 231 ++++++++++++++++++++++++++++++++ units/.gitignore | 1 + units/systemd-vconsole-setup.service.in | 20 +++ 11 files changed, 457 insertions(+), 34 deletions(-) create mode 100644 src/vconsole-setup.c create mode 100644 units/systemd-vconsole-setup.service.in (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 2c92ae05b7..9e2fb86b57 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ systemd-kmsg-syslogd systemd-remount-api-vfs test-hostname systemd-modules-load -systemd-auto-serial-getty +systemd-vconsole-setup systemd-shutdownd systemd-random-seed systemd-update-utmp diff --git a/Makefile.am b/Makefile.am index 55b6dc8519..8711a52ea8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -75,7 +75,8 @@ rootlibexec_PROGRAMS = \ systemd-shutdownd \ systemd-modules-load \ systemd-remount-api-vfs \ - systemd-kmsg-syslogd + systemd-kmsg-syslogd \ + systemd-vconsole-setup noinst_PROGRAMS = \ test-engine \ @@ -180,6 +181,7 @@ nodist_systemunit_DATA = \ units/systemd-shutdownd.service \ units/systemd-kmsg-syslogd.service \ units/systemd-modules-load.service \ + units/systemd-vconsole-setup.service \ units/systemd-remount-api-vfs.service \ units/systemd-update-utmp-runlevel.service \ units/systemd-update-utmp-shutdown.service \ @@ -206,6 +208,7 @@ EXTRA_DIST = \ units/systemd-shutdownd.service.in \ units/systemd-kmsg-syslogd.service.in \ units/systemd-modules-load.service.in \ + units/systemd-vconsole-setup.service.in \ units/systemd-remount-api-vfs.service.in \ units/systemd-update-utmp-runlevel.service.in \ units/systemd-update-utmp-shutdown.service.in \ @@ -594,6 +597,15 @@ systemd_modules_load_CFLAGS = \ systemd_modules_load_LDADD = \ libsystemd-basic.la +systemd_vconsole_setup_SOURCES = \ + src/vconsole-setup.c + +systemd_vconsole_setup_CFLAGS = \ + $(AM_CFLAGS) + +systemd_vconsole_setup_LDADD = \ + libsystemd-basic.la + systemd_remount_api_vfs_SOURCES = \ src/remount-api-vfs.c \ src/mount-setup.c diff --git a/src/conf-parser.c b/src/conf-parser.c index 438b4cc3d5..d18b2a150d 100644 --- a/src/conf-parser.c +++ b/src/conf-parser.c @@ -31,8 +31,6 @@ #include "strv.h" #include "log.h" -#define COMMENTS "#;\n" - /* Run the user supplied parser for an assignment */ static int next_assignment( const char *filename, diff --git a/src/kmod-setup.c b/src/kmod-setup.c index 44d843db12..97b7c870b1 100644 --- a/src/kmod-setup.c +++ b/src/kmod-setup.c @@ -42,7 +42,6 @@ int kmod_setup(void) { ExecContext context; pid_t pid; int r; - siginfo_t status; for (i = 0; i < ELEMENTSOF(kmod_table); i += 2) { @@ -74,28 +73,10 @@ int kmod_setup(void) { r = exec_spawn(&command, NULL, &context, NULL, 0, NULL, false, false, false, false, NULL, &pid); exec_context_done(&context); - if (r < 0) + if (r < 0) { + log_error("Failed to spawn %s: %s", cmdline[0], strerror(-r)); return r; - - if ((r = wait_for_terminate(pid, &status)) < 0) - return -errno; - - if (status.si_code == CLD_EXITED) { - if (status.si_status != 0) { - log_warning("/sbin/modprobe failed with error code %i.", status.si_status); - return -EPROTO; - } - - log_debug("/sbin/modprobe succeeded."); - return 0; - - } else if (status.si_code == CLD_KILLED || - status.si_code == CLD_DUMPED) { - - log_warning("/sbin/modprobe terminated by signal %s.", signal_to_string(status.si_status)); - return -EPROTO; } - log_warning("/sbin/modprobe failed due to unknown reason."); - return -EPROTO; + return wait_for_terminate_and_warn(cmdline[0], pid); } diff --git a/src/load-fragment.c b/src/load-fragment.c index 636de8d103..1190ef4acc 100644 --- a/src/load-fragment.c +++ b/src/load-fragment.c @@ -42,8 +42,6 @@ #include "unit-name.h" #include "bus-errors.h" -#define COMMENTS "#;\n" - static int config_parse_deps( const char *filename, unsigned line, diff --git a/src/mount.c b/src/mount.c index d651e8714b..5a05e2c189 100644 --- a/src/mount.c +++ b/src/mount.c @@ -1228,7 +1228,7 @@ static char *fstab_node_to_udev_node(char *p) { if (startswith(p, "LABEL=")) { - if (!(u = unquote(p+6, '"'))) + if (!(u = unquote(p+6, "\"\'"))) return NULL; t = xescape(u, "/ "); @@ -1248,7 +1248,7 @@ static char *fstab_node_to_udev_node(char *p) { if (startswith(p, "UUID=")) { - if (!(u = unquote(p+5, '"'))) + if (!(u = unquote(p+5, "\"\'"))) return NULL; t = xescape(u, "/ "); diff --git a/src/util.c b/src/util.c index a3d6b49710..47da11c1b8 100644 --- a/src/util.c +++ b/src/util.c @@ -504,7 +504,7 @@ finish: int read_one_line_file(const char *fn, char **line) { FILE *f; int r; - char t[2048], *c; + char t[LINE_MAX], *c; assert(fn); assert(line); @@ -530,6 +530,149 @@ finish: return r; } +int read_full_file(const char *fn, char **contents) { + FILE *f; + int r; + size_t n, l; + char *buf = NULL; + struct stat st; + + if (!(f = fopen(fn, "re"))) + return -errno; + + if (fstat(fileno(f), &st) < 0) { + r = -errno; + goto finish; + } + + n = st.st_size > 0 ? st.st_size : LINE_MAX; + l = 0; + + for (;;) { + char *t; + size_t k; + + if (!(t = realloc(buf, n+1))) { + r = -ENOMEM; + goto finish; + } + + buf = t; + k = fread(buf + l, 1, n - l, f); + + if (k <= 0) { + if (ferror(f)) { + r = -errno; + goto finish; + } + + break; + } + + l += k; + n *= 2; + + /* Safety check */ + if (n > 4*1024*1024) { + r = -E2BIG; + goto finish; + } + } + + if (buf) + buf[l] = 0; + else if (!(buf = calloc(1, 1))) { + r = -errno; + goto finish; + } + + *contents = buf; + buf = NULL; + + r = 0; + +finish: + fclose(f); + free(buf); + + return r; +} + +int parse_env_file( + const char *fname, + const char *seperator, ...) { + + int r; + char *contents, *p; + + assert(fname); + assert(seperator); + + if ((r = read_full_file(fname, &contents)) < 0) + return r; + + p = contents; + for (;;) { + const char *key = NULL; + + p += strspn(p, seperator); + p += strspn(p, WHITESPACE); + + if (!*p) + break; + + if (!strchr(COMMENTS, *p)) { + va_list ap; + char **value; + + va_start(ap, seperator); + while ((key = va_arg(ap, char *))) { + size_t n; + char *v; + + value = va_arg(ap, char **); + + n = strlen(key); + if (strncmp(p, key, n) != 0 || + p[n] != '=') + continue; + + p += n + 1; + n = strcspn(p, seperator); + + if (n >= 2 && + strchr(QUOTES, v[0]) && + v[n-1] == v[0]) + v = strndup(p+1, n-2); + else + v = strndup(p, n); + + if (!v) { + r = -ENOMEM; + va_end(ap); + goto fail; + } + + free(*value); + *value = v; + + p += n; + break; + } + va_end(ap); + } + + if (!key) + p += strcspn(p, seperator); + } + + r = 0; + +fail: + free(contents); + return r; +} + char *truncate_nl(char *s) { assert(s); @@ -3088,14 +3231,14 @@ int touch(const char *path) { return 0; } -char *unquote(const char *s, const char quote) { +char *unquote(const char *s, const char* quotes) { size_t l; assert(s); if ((l = strlen(s)) < 2) return strdup(s); - if (s[0] == quote && s[l-1] == quote) + if (strchr(quotes, s[0]) && s[l-1] == s[0]) return strndup(s+1, l-2); return strdup(s); @@ -3120,6 +3263,39 @@ int wait_for_terminate(pid_t pid, siginfo_t *status) { } } +int wait_for_terminate_and_warn(const char *name, pid_t pid) { + int r; + siginfo_t status; + + assert(name); + assert(pid > 1); + + if ((r = wait_for_terminate(pid, &status)) < 0) { + log_warning("Failed to wait for %s: %s", name, strerror(-r)); + return r; + } + + if (status.si_code == CLD_EXITED) { + if (status.si_status != 0) { + log_warning("%s failed with error code %i.", name, status.si_status); + return -EPROTO; + } + + log_debug("%s succeeded.", name); + return 0; + + } else if (status.si_code == CLD_KILLED || + status.si_code == CLD_DUMPED) { + + log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status)); + return -EPROTO; + } + + log_warning("%s failed due to unknown reason.", name); + return -EPROTO; + +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", diff --git a/src/util.h b/src/util.h index bdd4f15cf2..7ea163f214 100644 --- a/src/util.h +++ b/src/util.h @@ -58,6 +58,8 @@ typedef struct dual_timestamp { /* What is interpreted as whitespace? */ #define WHITESPACE " \t\n\r" #define NEWLINE "\n\r" +#define QUOTES "\"\'" +#define COMMENTS "#;\n" #define FORMAT_TIMESTAMP_MAX 64 #define FORMAT_TIMESTAMP_PRETTY_MAX 256 @@ -185,6 +187,9 @@ pid_t get_parent_of_pid(pid_t pid, pid_t *ppid); int write_one_line_file(const char *fn, const char *line); int read_one_line_file(const char *fn, char **line); +int read_full_file(const char *fn, char **contents); + +int parse_env_file(const char *fname, const char *seperator, ...) _sentinel_; char *strappend(const char *s, const char *suffix); char *strnappend(const char *s, const char *suffix, size_t length); @@ -343,9 +348,10 @@ char *ellipsize(const char *s, unsigned length, unsigned percent); int touch(const char *path); -char *unquote(const char *s, const char quote); +char *unquote(const char *s, const char *quotes); int wait_for_terminate(pid_t pid, siginfo_t *status); +int wait_for_terminate_and_warn(const char *name, pid_t pid); #define NULSTR_FOREACH(i, l) \ for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1) diff --git a/src/vconsole-setup.c b/src/vconsole-setup.c new file mode 100644 index 0000000000..8e4fedef0e --- /dev/null +++ b/src/vconsole-setup.c @@ -0,0 +1,231 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "log.h" +#include "macro.h" + +static bool is_console(int fd) { + unsigned char data[1]; + + data[0] = TIOCL_GETFGCONSOLE; + return ioctl(fd, TIOCLINUX, data) >= 0; +} + +static bool is_locale_utf8(void) { + const char *set; + + if (!setlocale(LC_ALL, "")) + return true; + + set = nl_langinfo(CODESET); + if (!set) + return true; + + return streq(set, "UTF-8"); +} + +static int disable_utf8(int fd) { + int r = 0, k; + + if (ioctl(fd, KDSKBMODE, K_XLATE) < 0) + r = -errno; + + if (loop_write(fd, "\033%@", 3, false) < 0) + r = -errno; + + if ((k = write_one_line_file("/sys/module/vt/parameters/default_utf8", "0")) < 0) + r = k; + + if (r < 0) + log_warning("Failed to disable UTF-8: %s", strerror(errno)); + + return r; +} + +static int load_keymap(const char *vc, const char *map, bool utf8, pid_t *_pid) { + const char *args[7]; + int i = 0; + pid_t pid; + + args[i++] = "/bin/loadkeys"; + args[i++] = "-q"; + args[i++] = "-C"; + args[i++] = vc; + if (utf8) + args[i++] = "-u"; + args[i++] = map; + args[i++] = NULL; + + if ((pid = fork()) < 0) { + log_error("Failed to fork: %m"); + return -errno; + } else if (pid == 0) { + execv(args[0], (char **) args); + _exit(EXIT_FAILURE); + } + + *_pid = pid; + return 0; +} + +static int load_font(const char *vc, const char *font, const char *map, const char *unimap, pid_t *_pid) { + const char *args[9]; + int i = 0; + pid_t pid; + + args[i++] = "/bin/setfont"; + args[i++] = "-C"; + args[i++] = vc; + args[i++] = font; + if (map) { + args[i++] = "-m"; + args[i++] = map; + } + if (unimap) { + args[i++] = "-u"; + args[i++] = unimap; + } + args[i++] = NULL; + + if ((pid = fork()) < 0) { + log_error("Failed to fork: %m"); + return -errno; + } else if (pid == 0) { + execv(args[0], (char **) args); + _exit(EXIT_FAILURE); + } + + *_pid = pid; + return 0; +} + +int main(int argc, char **argv) { + const char *vc; + char *vc_keymap = NULL; + char *vc_font = NULL; + char *vc_font_map = NULL; + char *vc_font_unimap = NULL; + int fd = -1; + bool utf8; + int r = EXIT_FAILURE; + pid_t font_pid = 0, keymap_pid = 0; + + log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_parse_environment(); + log_open(); + + if (argv[1]) + vc = argv[1]; + else + vc = "/dev/tty0"; + + if ((fd = open(vc, O_RDWR|O_CLOEXEC)) < 0) { + log_error("Failed to open %s: %m", vc); + goto finish; + } + + if (!is_console(fd)) { + log_error("Device %s is not a virtual console.", vc); + goto finish; + } + + if (!(utf8 = is_locale_utf8())) + disable_utf8(fd); + + if ((r = parse_env_file( + "/etc/vconsole", + NEWLINE, + "VCONSOLE_KEYMAP", &vc_keymap, + "VCONSOLE_FONT", &vc_font, + "VCONSOLE_FONT_MAP", &vc_font_map, + "VCONSOLE_FONT_UNIMAP", &vc_font_unimap, + NULL)) < 0) { + + if (r != -ENOENT) + log_warning("Failed to read /etc/vconsole: %s", strerror(-r)); + } + + if ((r = parse_env_file( + "/proc/cmdline", + WHITESPACE, +#ifdef TARGET_FEDORA + "SYSFONT", &vc_font, + "KEYTABLE", &vc_keymap, +#endif + "vconsole.keymap", &vc_keymap, + "vconsole.font", &vc_font, + "vconsole.font.map", &vc_font_map, + "vconsole.font.unimap", &vc_font_unimap, + NULL)) < 0) { + + if (r != -ENOENT) + log_warning("Failed to read /proc/cmdline: %s", strerror(-r)); + } + + if (!vc_keymap) + vc_keymap = strdup("us"); + if (!vc_font) + vc_font = strdup("latarcyrheb-sun16"); + + if (!vc_keymap || !vc_font) { + log_error("Failed to allocate strings."); + goto finish; + } + + if (load_keymap(vc, vc_keymap, utf8, &keymap_pid) >= 0 && + load_font(vc, vc_font, vc_font_map, vc_font_unimap, &font_pid) >= 0) + r = EXIT_SUCCESS; + +finish: + if (keymap_pid > 0) + wait_for_terminate_and_warn("/bin/loadkeys", keymap_pid); + + if (font_pid > 0) + wait_for_terminate_and_warn("/bin/setfont", font_pid); + + free(vc_keymap); + free(vc_font); + free(vc_font_map); + free(vc_font_unimap); + + if (fd >= 0) + close_nointr_nofail(fd); + + return r; +} diff --git a/units/.gitignore b/units/.gitignore index a02b7f11eb..816eeb3c1e 100644 --- a/units/.gitignore +++ b/units/.gitignore @@ -2,6 +2,7 @@ serial-getty@.service systemd-kmsg-syslogd.service systemd-modules-load.service systemd-remount-api-vfs.service +systemd-vconsole-setup.service systemd-auto-serial-getty.service systemd-shutdownd.service systemd-random-seed-load.service diff --git a/units/systemd-vconsole-setup.service.in b/units/systemd-vconsole-setup.service.in new file mode 100644 index 0000000000..c113be578e --- /dev/null +++ b/units/systemd-vconsole-setup.service.in @@ -0,0 +1,20 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +[Unit] +Description=Setup Virtual Console +DefaultDependencies=no +Conflicts=shutdown.target +Before=shutdown.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-vconsole-setup + +[Install] +WantedBy=sysinit.target -- cgit v1.2.3-54-g00ecf From 490aed584944b684026a3fd01f8d81f2881e38d6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 17 Sep 2010 01:26:29 +0200 Subject: ask-password: add minimal framework to allow services query SSL/harddisk passphrases from the user --- .gitignore | 3 + Makefile.am | 49 ++++- configure.ac | 2 +- src/.gitignore | 1 + src/ask-password-agent.vala | 250 +++++++++++++++++++++++++ src/ask-password.c | 352 ++++++++++++++++++++++++++++++++++++ src/main.c | 2 + src/org.freedesktop.systemd1.policy | 30 +++ src/reply-password.c | 108 +++++++++++ src/systemadm.vala | 2 +- 10 files changed, 794 insertions(+), 5 deletions(-) create mode 100644 src/ask-password-agent.vala create mode 100644 src/ask-password.c create mode 100644 src/org.freedesktop.systemd1.policy create mode 100644 src/reply-password.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 9e2fb86b57..7cecf1c31b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +systemd-reply-password +systemd-ask-password-agent +systemd-ask-password systemd-kmsg-syslogd systemd-remount-api-vfs test-hostname diff --git a/Makefile.am b/Makefile.am index 7632e553d8..5f11e4fb7a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -25,6 +25,7 @@ dbusinterfacedir=@dbusinterfacedir@ udevrulesdir=@udevrulesdir@ pamlibdir=@pamlibdir@ pkgconfigdatadir=$(datadir)/pkgconfig +polkitpolicydir=$(datadir)/polkit-1/actions # Our own, non-special dirs pkgsysconfdir=$(sysconfdir)/systemd @@ -56,14 +57,16 @@ AM_CPPFLAGS = \ rootbin_PROGRAMS = \ systemd \ systemctl \ - systemd-notify + systemd-notify \ + systemd-ask-password bin_PROGRAMS = \ systemd-cgls if HAVE_GTK bin_PROGRAMS += \ - systemadm + systemadm \ + systemd-ask-password-agent endif rootlibexec_PROGRAMS = \ @@ -76,7 +79,8 @@ rootlibexec_PROGRAMS = \ systemd-modules-load \ systemd-remount-api-vfs \ systemd-kmsg-syslogd \ - systemd-vconsole-setup + systemd-vconsole-setup \ + systemd-reply-password noinst_PROGRAMS = \ test-engine \ @@ -282,6 +286,9 @@ dist_doc_DATA = \ pkgconfigdata_DATA = \ systemd.pc +dist_polkitpolicy_DATA = \ + src/org.freedesktop.systemd1.policy + noinst_LTLIBRARIES = \ libsystemd-basic.la \ libsystemd-core.la @@ -665,6 +672,18 @@ systemd_notify_SOURCES = \ systemd_notify_LDADD = \ libsystemd-basic.la +systemd_ask_password_SOURCES = \ + src/ask-password.c + +systemd_ask_password_LDADD = \ + libsystemd-basic.la + +systemd_reply_password_SOURCES = \ + src/reply-password.c + +systemd_reply_password_LDADD = \ + libsystemd-basic.la + systemd_cgls_SOURCES = \ src/cgls.c \ src/cgroup-show.c \ @@ -699,6 +718,30 @@ systemadm_LDADD = \ $(DBUSGLIB_LIBS) \ $(GTK_LIBS) +systemd_ask_password_agent_SOURCES = \ + src/ask-password-agent.vala + +systemd_ask_password_agent_CFLAGS = \ + $(AM_CFLAGS) \ + $(DBUSGLIB_CFLAGS) \ + $(GTK_CFLAGS) \ + -Wno-unused-variable \ + -Wno-unused-function \ + -Wno-shadow \ + -Wno-format-nonliteral + +systemd_ask_password_agent_VALAFLAGS = \ + --pkg=dbus-glib-1 \ + --pkg=posix \ + --pkg=gtk+-2.0 \ + --pkg=linux \ + --pkg=gio-unix-2.0 \ + -g + +systemd_ask_password_agent_LDADD = \ + $(DBUSGLIB_LIBS) \ + $(GTK_LIBS) + pam_systemd_la_SOURCES = \ src/pam-module.c \ src/cgroup-util.c \ diff --git a/configure.ac b/configure.ac index 2ff0c1ffc8..334b1e263c 100644 --- a/configure.ac +++ b/configure.ac @@ -226,7 +226,7 @@ AC_SUBST(AUDIT_LIBS) have_gtk=no AC_ARG_ENABLE(gtk, AS_HELP_STRING([--disable-gtk], [disable GTK tools])) if test "x$enable_gtk" != "xno"; then - PKG_CHECK_MODULES(GTK, [ gtk+-2.0 ], + PKG_CHECK_MODULES(GTK, [ gtk+-2.0 gio-unix-2.0 ], [AC_DEFINE(HAVE_GTK, 1, [Define if GTK is available]) have_gtk=yes], have_gtk=no) AC_SUBST(GTK_CFLAGS) AC_SUBST(GTK_LIBS) diff --git a/src/.gitignore b/src/.gitignore index 1841bf597d..389f26daa2 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,2 +1,3 @@ +ask-password-agent.c systemd-interfaces.c systemadm.c diff --git a/src/ask-password-agent.vala b/src/ask-password-agent.vala new file mode 100644 index 0000000000..5355bb4694 --- /dev/null +++ b/src/ask-password-agent.vala @@ -0,0 +1,250 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +using Gtk; +using GLib; +using DBus; +using Linux; +using Posix; + +[CCode (cheader_filename = "time.h")] +extern int clock_gettime(int id, out timespec ts); + +public class PasswordDialog : Dialog { + + public Entry entry; + + public PasswordDialog(string message, string icon) { + set_title("System Password"); + set_has_separator(false); + set_border_width(8); + set_default_response(ResponseType.OK); + set_icon_name(icon); + + add_button(STOCK_CANCEL, ResponseType.CANCEL); + add_button(STOCK_OK, ResponseType.OK); + + Container content = (Container) get_content_area(); + + Box hbox = new HBox(false, 16); + hbox.set_border_width(8); + content.add(hbox); + + Image image = new Image.from_icon_name(icon, IconSize.DIALOG); + hbox.pack_start(image, false, false); + + Box vbox = new VBox(false, 8); + hbox.pack_start(vbox, true, true); + + Label label = new Label(message); + vbox.pack_start(label, false, false); + + entry = new Entry(); + entry.set_visibility(false); + entry.set_activates_default(true); + vbox.pack_start(entry, false, false); + + entry.activate.connect(on_entry_activated); + + show_all(); + } + + public void on_entry_activated() { + response(ResponseType.OK); + } +} + +public class MyStatusIcon : StatusIcon { + + File directory; + File current; + FileMonitor file_monitor; + + string message; + string icon; + string socket; + + PasswordDialog password_dialog; + + public MyStatusIcon() throws GLib.Error { + GLib.Object(icon_name : "dialog-password"); + set_title("System Password Agent"); + + directory = File.new_for_path("/dev/.systemd/ask-password/"); + file_monitor = directory.monitor_directory(0); + file_monitor.changed.connect(file_monitor_changed); + + current = null; + look_for_password(); + + activate.connect(status_icon_activate); + } + + void file_monitor_changed(GLib.File file, GLib.File? other_file, GLib.FileMonitorEvent event_type) throws GLib.Error { + + if (!file.get_basename().has_prefix("ask.")) + return; + + if (event_type == FileMonitorEvent.CREATED || + event_type == FileMonitorEvent.DELETED) + look_for_password(); + } + + void look_for_password() throws GLib.Error { + + if (current != null) { + if (!current.query_exists()) { + current = null; + if (password_dialog != null) + password_dialog.response(ResponseType.REJECT); + } + } + + if (current == null) { + FileEnumerator enumerator = directory.enumerate_children("standard::name", FileQueryInfoFlags.NOFOLLOW_SYMLINKS); + + FileInfo i; + while ((i = enumerator.next_file()) != null) { + if (!i.get_name().has_prefix("ask.")) + continue; + + current = directory.get_child(i.get_name()); + + if (load_password()) + break; + + current = null; + } + } + + if (current == null) + set_visible(false); + + } + + bool load_password() { + + KeyFile key_file = new KeyFile(); + + try { + timespec ts; + + key_file.load_from_file(current.get_path(), KeyFileFlags.NONE); + + string not_after_as_string = key_file.get_string("Ask", "NotAfter"); + + clock_gettime(1, out ts); + uint64 now = (ts.tv_sec * 1000000) + (ts.tv_nsec / 1000); + + uint64 not_after; + if (not_after_as_string.scanf("%llu", out not_after) != 1) + return false; + + if (not_after < now) + return false; + + socket = key_file.get_string("Ask", "Socket"); + } catch (GLib.Error e) { + return false; + } + + try { + message = key_file.get_string("Ask", "Message").compress(); + } catch (GLib.Error e) { + message = "Please Enter System Password!"; + } + set_tooltip_text(message); + + try { + icon = key_file.get_string("Ask", "Icon"); + } catch (GLib.Error e) { + icon = "dialog-password"; + } + set_from_icon_name(icon); + + set_visible(true); + return true; + } + + void status_icon_activate() throws GLib.Error { + + if (current == null) + return; + + if (password_dialog != null) { + password_dialog.present(); + return; + } + + password_dialog = new PasswordDialog(message, icon); + + int result = password_dialog.run(); + string password = password_dialog.entry.get_text(); + + password_dialog.destroy(); + password_dialog = null; + + if (result == ResponseType.REJECT || + result == ResponseType.DELETE_EVENT) + return; + + int to_process; + + Process.spawn_async_with_pipes( + null, + { "/usr/bin/pkexec", "/lib/systemd/systemd-reply-password", result == ResponseType.OK ? "1" : "0", socket }, + null, + 0, + null, + null, + out to_process, + null, + null); + + OutputStream stream = new UnixOutputStream(to_process, true); + + stream.write(password, password.length, null); + } +} + +static const OptionEntry entries[] = { + { null } +}; + +void show_error(string e) { + var m = new MessageDialog(null, 0, MessageType.ERROR, ButtonsType.CLOSE, "%s", e); + m.run(); + m.destroy(); +} + +int main(string[] args) { + try { + Gtk.init_with_args(ref args, "[OPTION...]", entries, "systemd-ask-password-agent"); + + MyStatusIcon i = new MyStatusIcon(); + Gtk.main(); + + } catch (DBus.Error e) { + show_error(e.message); + } catch (GLib.Error e) { + show_error(e.message); + } + + return 0; +} diff --git a/src/ask-password.c b/src/ask-password.c new file mode 100644 index 0000000000..2c9b027d00 --- /dev/null +++ b/src/ask-password.c @@ -0,0 +1,352 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "macro.h" +#include "util.h" + +static const char *arg_icon = NULL; +static const char *arg_message = NULL; +static usec_t arg_timeout = 60 * USEC_PER_SEC; + +static int create_socket(char **name) { + int fd; + union { + struct sockaddr sa; + struct sockaddr_un un; + } sa; + int one = 1, r; + char *c; + + assert(name); + + if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) { + log_error("socket() failed: %m"); + return -errno; + } + + zero(sa); + sa.un.sun_family = AF_UNIX; + snprintf(sa.un.sun_path+1, sizeof(sa.un.sun_path)-1, "/org/freedesktop/systemd1/ask-password/%llu", random_ull()); + + if (bind(fd, &sa.sa, sizeof(sa_family_t) + 1 + strlen(sa.un.sun_path+1)) < 0) { + r = -errno; + log_error("bind() failed: %m"); + goto fail; + } + + if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) { + r = -errno; + log_error("SO_PASSCRED failed: %m"); + goto fail; + } + + if (!(c = strdup(sa.un.sun_path+1))) { + r = -ENOMEM; + log_error("Out of memory"); + goto fail; + } + + *name = c; + return fd; + +fail: + close_nointr_nofail(fd); + + return r; +} + +static int help(void) { + + printf("%s [OPTIONS...] MESSAGE\n\n" + "Query the user for a passphrase.\n\n" + " -h --help Show this help\n" + " --icon=NAME Icon name\n" + " --timeout=USEC Timeout in usec\n", + program_invocation_short_name); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_ICON = 0x100, + ARG_TIMEOUT + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "icon", required_argument, NULL, ARG_ICON }, + { "timeout", required_argument, NULL, ARG_TIMEOUT }, + { NULL, 0, NULL, 0 } + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_ICON: + arg_icon = optarg; + break; + + case ARG_TIMEOUT: + if (parse_usec(optarg, &arg_timeout) < 0 || arg_timeout <= 0) { + log_error("Failed to parse --timeout parameter %s", optarg); + return -EINVAL; + } + break; + + case '?': + return -EINVAL; + + default: + log_error("Unknown option code %c", c); + return -EINVAL; + } + } + + if (optind != argc-1) { + help(); + return -EINVAL; + } + + arg_message = argv[optind]; + return 0; +} + +int main(int argc, char *argv[]) { + char temp[] = "/dev/.systemd/ask-password/tmp.XXXXXX"; + char final[sizeof(temp)] = ""; + int fd = -1, r = EXIT_FAILURE, k; + FILE *f = NULL; + char *socket_name = NULL; + int socket_fd, signal_fd; + sigset_t mask; + usec_t not_after; + + log_parse_environment(); + log_open(); + + if ((k = parse_argv(argc, argv)) < 0) { + r = k < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + goto finish; + } + + if ((fd = mkostemp(temp, O_CLOEXEC|O_CREAT|O_WRONLY)) < 0) { + log_error("Failed to create password file: %m"); + goto finish; + } + + fchmod(fd, 0644); + + if (!(f = fdopen(fd, "w"))) { + log_error("Failed to allocate FILE: %m"); + goto finish; + } + + fd = -1; + + assert_se(sigemptyset(&mask) == 0); + sigset_add_many(&mask, SIGINT, SIGTERM, -1); + assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0); + + if ((signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) { + log_error("signalfd(): %m"); + goto finish; + } + + if ((socket_fd = create_socket(&socket_name)) < 0) + goto finish; + + not_after = now(CLOCK_MONOTONIC) + arg_timeout; + + fprintf(f, + "[Ask]\n" + "Socket=%s\n" + "NotAfter=%llu\n", + socket_name, + (unsigned long long) not_after); + + if (arg_message) + fprintf(f, "Message=%s\n", arg_message); + + if (arg_icon) + fprintf(f, "Icon=%s\n", arg_icon); + + fflush(f); + + if (ferror(f)) { + log_error("Failed to write query file: %m"); + goto finish; + } + + memcpy(final, temp, sizeof(temp)); + + final[sizeof(final)-11] = 'a'; + final[sizeof(final)-10] = 's'; + final[sizeof(final)-9] = 'k'; + + if (rename(temp, final) < 0) { + log_error("Failed to rename query file: %m"); + goto finish; + } + + for (;;) { + enum { + FD_SOCKET, + FD_SIGNAL, + _FD_MAX + }; + + char passphrase[LINE_MAX+1]; + struct msghdr msghdr; + struct iovec iovec; + struct ucred *ucred; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; + } control; + ssize_t n; + struct pollfd pollfd[_FD_MAX]; + + zero(pollfd); + pollfd[FD_SOCKET].fd = socket_fd; + pollfd[FD_SOCKET].events = POLLIN; + pollfd[FD_SIGNAL].fd = signal_fd; + pollfd[FD_SIGNAL].events = POLLIN; + + if ((k = poll(pollfd, 2, arg_timeout/USEC_PER_MSEC)) < 0) { + + if (errno == EINTR) + continue; + + log_error("poll() failed: %s", strerror(-r)); + goto finish; + } + + if (k <= 0) { + log_notice("Timed out"); + goto finish; + } + + if (pollfd[FD_SIGNAL].revents & POLLIN) + break; + + if (pollfd[FD_SOCKET].revents != POLLIN) { + log_error("Unexpected poll() event."); + goto finish; + } + + zero(iovec); + iovec.iov_base = passphrase; + iovec.iov_len = sizeof(passphrase)-1; + + zero(control); + zero(msghdr); + msghdr.msg_iov = &iovec; + msghdr.msg_iovlen = 1; + msghdr.msg_control = &control; + msghdr.msg_controllen = sizeof(control); + + if ((n = recvmsg(socket_fd, &msghdr, 0)) < 0) { + + if (errno == EAGAIN || + errno == EINTR) + continue; + + log_error("recvmsg() failed: %m"); + goto finish; + } + + if (n <= 0) { + log_error("Message too short"); + continue; + } + + if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) || + control.cmsghdr.cmsg_level != SOL_SOCKET || + control.cmsghdr.cmsg_type != SCM_CREDENTIALS || + control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) { + log_warning("Received message without credentials. Ignoring."); + continue; + } + + ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr); + if (ucred->uid != 0) { + log_warning("Got request from unprivileged user. Ignoring."); + continue; + } + + if (passphrase[0] == '+') { + passphrase[n] = 0; + fputs(passphrase+1, stdout); + } else if (passphrase[0] == '-') + goto finish; + else { + log_error("Invalid packet"); + continue; + } + + break; + } + + r = EXIT_SUCCESS; + +finish: + if (fd >= 0) + close_nointr_nofail(fd); + + if (socket_fd >= 0) + close_nointr_nofail(socket_fd); + + if (f) + fclose(f); + + unlink(temp); + + if (final[0]) + unlink(final); + + return r; +} diff --git a/src/main.c b/src/main.c index 671f2bbf1d..fcb6e8f9db 100644 --- a/src/main.c +++ b/src/main.c @@ -1006,6 +1006,8 @@ int main(int argc, char *argv[]) { kmod_setup(); hostname_setup(); loopback_setup(); + + mkdir_p("/dev/.systemd/ask-password/", 0755); } if ((r = manager_new(arg_running_as, &m)) < 0) { diff --git a/src/org.freedesktop.systemd1.policy b/src/org.freedesktop.systemd1.policy new file mode 100644 index 0000000000..bb07b827fc --- /dev/null +++ b/src/org.freedesktop.systemd1.policy @@ -0,0 +1,30 @@ + + + + + + + + The systemd Project + http://www.freedesktop.org/wiki/Software/systemd + + + Send passphrase back to system + Authentication is required to send the entered passphrase back to the system. + + no + no + auth_admin_keep + + /lib/systemd/systemd-reply-password + + + diff --git a/src/reply-password.c b/src/reply-password.c new file mode 100644 index 0000000000..236fdcc94c --- /dev/null +++ b/src/reply-password.c @@ -0,0 +1,108 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "macro.h" +#include "util.h" + +static int send_on_socket(int fd, const char *socket_name, const void *packet, size_t size) { + union { + struct sockaddr sa; + struct sockaddr_un un; + } sa; + + assert(fd >= 0); + assert(socket_name); + assert(packet); + + zero(sa); + sa.un.sun_family = AF_UNIX; + strncpy(sa.un.sun_path+1, socket_name, sizeof(sa.un.sun_path)-1); + + if (sendto(fd, packet, size, MSG_NOSIGNAL, &sa.sa, sizeof(sa_family_t) + 1 + strlen(socket_name)) < 0) { + log_error("Failed to send: %m"); + return -1; + } + + return 0; +} + +int main(int argc, char *argv[]) { + int fd = -1, r = EXIT_FAILURE; + char packet[LINE_MAX]; + size_t length; + + log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_parse_environment(); + log_open(); + + if (argc != 3) { + log_error("Wrong number of arguments."); + goto finish; + } + + if (streq(argv[1], "1")) { + + packet[0] = '+'; + if (!fgets(packet+1, sizeof(packet)-1, stdin)) { + log_error("Failed to read password: %m"); + goto finish; + } + + truncate_nl(packet+1); + length = strlen(packet+1) + 1; + } else if (streq(argv[1], "0")) { + packet[0] = '-'; + length = 1; + } else { + log_error("Invalid first argument %s", argv[1]); + goto finish; + } + + if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) { + log_error("socket() failed: %m"); + goto finish; + } + + if (send_on_socket(fd, argv[2], packet, length) < 0) + goto finish; + + r = EXIT_SUCCESS; + +finish: + if (fd >= 0) + close_nointr_nofail(fd); + + return r; +} diff --git a/src/systemadm.vala b/src/systemadm.vala index 4aee1d35e6..2f3aed205a 100644 --- a/src/systemadm.vala +++ b/src/systemadm.vala @@ -978,7 +978,7 @@ void show_error(string e) { m.destroy(); } -int main (string[] args) { +int main(string[] args) { try { Gtk.init_with_args(ref args, "[OPTION...]", entries, "systemadm"); -- cgit v1.2.3-54-g00ecf From 22be093ffb403a1c474037939ca9b88b1ee39f77 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 23 Sep 2010 15:01:41 +0200 Subject: readahead: implement minimal readahead logic based on fanotify(), mincore() and readahead() --- .gitignore | 2 + Makefile.am | 28 +++- configure.ac | 1 + src/hashmap.c | 15 ++ src/hashmap.h | 1 + src/linux/fanotify.h | 98 +++++++++++ src/macro.h | 6 + src/missing.h | 25 +++ src/readahead-collect.c | 437 ++++++++++++++++++++++++++++++++++++++++++++++++ src/readahead-common.c | 107 ++++++++++++ src/readahead-common.h | 33 ++++ src/readahead-replay.c | 214 ++++++++++++++++++++++++ 12 files changed, 966 insertions(+), 1 deletion(-) create mode 100644 src/linux/fanotify.h create mode 100644 src/readahead-collect.c create mode 100644 src/readahead-common.c create mode 100644 src/readahead-common.h create mode 100644 src/readahead-replay.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 7cecf1c31b..b5fc8d72b0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +systemd-readahead-collect +systemd-readahead-replay systemd-reply-password systemd-ask-password-agent systemd-ask-password diff --git a/Makefile.am b/Makefile.am index 0ac6b1acea..2cd3debf83 100644 --- a/Makefile.am +++ b/Makefile.am @@ -92,7 +92,9 @@ rootlibexec_PROGRAMS = \ systemd-remount-api-vfs \ systemd-kmsg-syslogd \ systemd-vconsole-setup \ - systemd-reply-password + systemd-reply-password \ + systemd-readahead-collect \ + systemd-readahead-replay noinst_PROGRAMS = \ test-engine \ @@ -699,6 +701,30 @@ systemd_reply_password_SOURCES = \ systemd_reply_password_LDADD = \ libsystemd-basic.la +systemd_readahead_collect_SOURCES = \ + src/readahead-collect.c \ + src/sd-daemon.c \ + src/readahead-common.c + +systemd_readahead_collect_CFLAGS = \ + $(UDEV_CFLAGS) + +systemd_readahead_collect_LDADD = \ + libsystemd-basic.la \ + $(UDEV_LIBS) + +systemd_readahead_replay_SOURCES = \ + src/readahead-replay.c \ + src/sd-daemon.c \ + src/readahead-common.c + +systemd_readahead_replay_CFLAGS = \ + $(UDEV_CFLAGS) + +systemd_readahead_replay_LDADD = \ + libsystemd-basic.la \ + $(UDEV_LIBS) + systemd_cgls_SOURCES = \ src/cgls.c \ src/cgroup-show.c \ diff --git a/configure.ac b/configure.ac index e3c1fdde0f..da627a0028 100644 --- a/configure.ac +++ b/configure.ac @@ -27,6 +27,7 @@ AM_INIT_AUTOMAKE([foreign 1.11 -Wall -Wno-portability silent-rules tar-pax subdi AC_SUBST(PACKAGE_URL, [http://www.freedesktop.org/wiki/Software/systemd]) AC_CANONICAL_HOST +AC_DEFINE_UNQUOTED([CANONICAL_HOST], "$host", [Canonical host string.]) AM_SILENT_RULES([yes]) diff --git a/src/hashmap.c b/src/hashmap.c index 51f00131f4..4b187057ec 100644 --- a/src/hashmap.c +++ b/src/hashmap.c @@ -476,6 +476,21 @@ void* hashmap_steal_first(Hashmap *h) { return data; } +void* hashmap_steal_first_key(Hashmap *h) { + void *key; + + if (!h) + return NULL; + + if (!h->iterate_list_head) + return NULL; + + key = (void*) h->iterate_list_head->key; + remove_entry(h, h->iterate_list_head); + + return key; +} + unsigned hashmap_size(Hashmap *h) { if (!h) diff --git a/src/hashmap.h b/src/hashmap.h index c48d6b31f6..ac5a8ae085 100644 --- a/src/hashmap.h +++ b/src/hashmap.h @@ -72,6 +72,7 @@ void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i); void hashmap_clear(Hashmap *h); void *hashmap_steal_first(Hashmap *h); +void *hashmap_steal_first_key(Hashmap *h); void* hashmap_first(Hashmap *h); void* hashmap_last(Hashmap *h); diff --git a/src/linux/fanotify.h b/src/linux/fanotify.h new file mode 100644 index 0000000000..63531a6b4d --- /dev/null +++ b/src/linux/fanotify.h @@ -0,0 +1,98 @@ +#ifndef _LINUX_FANOTIFY_H +#define _LINUX_FANOTIFY_H + +#include + +/* the following events that user-space can register for */ +#define FAN_ACCESS 0x00000001 /* File was accessed */ +#define FAN_MODIFY 0x00000002 /* File was modified */ +#define FAN_CLOSE_WRITE 0x00000008 /* Unwrittable file closed */ +#define FAN_CLOSE_NOWRITE 0x00000010 /* Writtable file closed */ +#define FAN_OPEN 0x00000020 /* File was opened */ + +#define FAN_EVENT_ON_CHILD 0x08000000 /* interested in child events */ + +/* FIXME currently Q's have no limit.... */ +#define FAN_Q_OVERFLOW 0x00004000 /* Event queued overflowed */ + +#define FAN_OPEN_PERM 0x00010000 /* File open in perm check */ +#define FAN_ACCESS_PERM 0x00020000 /* File accessed in perm check */ + +/* helper events */ +#define FAN_CLOSE (FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE) /* close */ + +/* flags used for fanotify_init() */ +#define FAN_CLOEXEC 0x00000001 +#define FAN_NONBLOCK 0x00000002 + +#define FAN_ALL_INIT_FLAGS (FAN_CLOEXEC | FAN_NONBLOCK) + +/* flags used for fanotify_modify_mark() */ +#define FAN_MARK_ADD 0x00000001 +#define FAN_MARK_REMOVE 0x00000002 +#define FAN_MARK_DONT_FOLLOW 0x00000004 +#define FAN_MARK_ONLYDIR 0x00000008 +#define FAN_MARK_MOUNT 0x00000010 +#define FAN_MARK_IGNORED_MASK 0x00000020 +#define FAN_MARK_IGNORED_SURV_MODIFY 0x00000040 +#define FAN_MARK_FLUSH 0x00000080 + +#define FAN_ALL_MARK_FLAGS (FAN_MARK_ADD |\ + FAN_MARK_REMOVE |\ + FAN_MARK_DONT_FOLLOW |\ + FAN_MARK_ONLYDIR |\ + FAN_MARK_MOUNT |\ + FAN_MARK_IGNORED_MASK |\ + FAN_MARK_IGNORED_SURV_MODIFY) + +/* + * All of the events - we build the list by hand so that we can add flags in + * the future and not break backward compatibility. Apps will get only the + * events that they originally wanted. Be sure to add new events here! + */ +#define FAN_ALL_EVENTS (FAN_ACCESS |\ + FAN_MODIFY |\ + FAN_CLOSE |\ + FAN_OPEN) + +/* + * All events which require a permission response from userspace + */ +#define FAN_ALL_PERM_EVENTS (FAN_OPEN_PERM |\ + FAN_ACCESS_PERM) + +#define FAN_ALL_OUTGOING_EVENTS (FAN_ALL_EVENTS |\ + FAN_ALL_PERM_EVENTS |\ + FAN_Q_OVERFLOW) + +#define FANOTIFY_METADATA_VERSION 2 + +struct fanotify_event_metadata { + __u32 event_len; + __u32 vers; + __u64 mask; + __s32 fd; + __s32 pid; +} __attribute__ ((packed)); + +struct fanotify_response { + __s32 fd; + __u32 response; +} __attribute__ ((packed)); + +/* Legit userspace responses to a _PERM event */ +#define FAN_ALLOW 0x01 +#define FAN_DENY 0x02 + +/* Helper functions to deal with fanotify_event_metadata buffers */ +#define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata)) + +#define FAN_EVENT_NEXT(meta, len) ((len) -= (meta)->event_len, \ + (struct fanotify_event_metadata*)(((char *)(meta)) + \ + (meta)->event_len)) + +#define FAN_EVENT_OK(meta, len) ((long)(len) >= (long)FAN_EVENT_METADATA_LEN && \ + (long)(meta)->event_len >= (long)FAN_EVENT_METADATA_LEN && \ + (long)(meta)->event_len <= (long)(len)) + +#endif /* _LINUX_FANOTIFY_H */ diff --git a/src/macro.h b/src/macro.h index 44b481775d..12ccbb1513 100644 --- a/src/macro.h +++ b/src/macro.h @@ -27,6 +27,8 @@ #include #include +#define PAGE_SIZE 4096 + #define _printf_attr_(a,b) __attribute__ ((format (printf, a, b))) #define _sentinel_ __attribute__ ((sentinel)) #define _noreturn_ __attribute__((noreturn)) @@ -49,6 +51,10 @@ static inline size_t ALIGN(size_t l) { return ((l + sizeof(void*) - 1) & ~(sizeof(void*) - 1)); } +static inline size_t PAGE_ALIGN(size_t l) { + return ((l + PAGE_SIZE - 1) & ~(PAGE_SIZE -1)); +} + #define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0])) #define MAX(a,b) \ diff --git a/src/missing.h b/src/missing.h index 418dbb8c0d..defe885ecc 100644 --- a/src/missing.h +++ b/src/missing.h @@ -76,4 +76,29 @@ static inline int pivot_root(const char *new_root, const char *put_old) { return syscall(SYS_pivot_root, new_root, put_old); } +#ifdef __x86_64__ +#ifndef __NR_fanotify_init +#define __NR_fanotify_init 300 +#endif +#ifndef __NR_fanotify_mark +#define __NR_fanotify_mark 301 +#endif +#else +#ifndef __NR_fanotify_init +#define __NR_fanotify_init 338 +#endif +#ifndef __NR_fanotify_mark +#define __NR_fanotify_mark 339 +#endif +#endif + +static inline int fanotify_init(unsigned int flags, unsigned int event_f_flags) { + return syscall(__NR_fanotify_init, flags, event_f_flags); +} + +static inline int fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, + int dfd, const char *pathname) { + return syscall(__NR_fanotify_mark, fanotify_fd, flags, mask, dfd, pathname); +} + #endif diff --git a/src/readahead-collect.c b/src/readahead-collect.c new file mode 100644 index 0000000000..93a04521f3 --- /dev/null +++ b/src/readahead-collect.c @@ -0,0 +1,437 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "missing.h" +#include "util.h" +#include "set.h" +#include "sd-daemon.h" +#include "ioprio.h" +#include "readahead-common.h" + +/* + fixme: + + - BTRFS_IOC_DEFRAG +*/ + +#define MINCORE_VEC_SIZE (READAHEAD_FILE_SIZE_MAX/PAGE_SIZE) + +static int pack_file(FILE *pack, const char *fn) { + struct stat st; + void *start = MAP_FAILED; + uint8_t vec[MINCORE_VEC_SIZE]; + uint32_t b, c; + size_t l, pages; + bool mapped; + int r = 0, fd = -1, k; + + assert(pack); + assert(fn); + + if ((fd = open(fn, O_RDONLY|O_CLOEXEC|O_NOATIME|O_NOCTTY|O_NOFOLLOW)) < 0) { + log_warning("open(%s) failed: %m", fn); + r = -errno; + goto finish; + } + + if ((k = file_verify(fd, fn, &st)) <= 0) { + r = k; + goto finish; + } + + l = PAGE_ALIGN(st.st_size); + if ((start = mmap(NULL, l, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) { + log_warning("mmap(%s) failed: %m", fn); + r = -errno; + goto finish; + } + + if (mincore(start, l, vec) < 0) { + log_warning("mincore(%s) failed: %m", fn); + r = -errno; + goto finish; + } + + fputs(fn, pack); + fputc('\n', pack); + + pages = l / PAGE_SIZE; + mapped = false; + for (c = 0; c < pages; c++) { + bool new_mapped = (vec[c] & 1); + + if (!mapped && new_mapped) + b = c; + else if (mapped && !new_mapped) { + fwrite(&b, sizeof(b), 1, pack); + fwrite(&c, sizeof(c), 1, pack); + + log_debug("%s: page %u to %u", fn, b, c); + } + + mapped = new_mapped; + } + + /* We don't write any range data if we should read the entire file */ + if (mapped && b > 0) { + fwrite(&b, sizeof(b), 1, pack); + fwrite(&c, sizeof(c), 1, pack); + + log_debug("%s: page %u to %u", fn, b, c); + } + + /* End marker */ + b = 0; + fwrite(&b, sizeof(b), 1, pack); + fwrite(&b, sizeof(b), 1, pack); + +finish: + if (start != MAP_FAILED) + munmap(start, l); + + if (fd >= 0) + close_nointr_nofail(fd); + + return r; +} + +static unsigned long fd_first_block(int fd) { + struct { + struct fiemap fiemap; + struct fiemap_extent extent; + } data; + + zero(data); + data.fiemap.fm_length = ~0ULL; + data.fiemap.fm_extent_count = 1; + + if (ioctl(fd, FS_IOC_FIEMAP, &data) < 0) + return 0; + + if (data.fiemap.fm_mapped_extents <= 0) + return 0; + + if (data.fiemap.fm_extents[0].fe_flags & FIEMAP_EXTENT_UNKNOWN) + return 0; + + return (unsigned long) data.fiemap.fm_extents[0].fe_physical; +} + +struct item { + const char *path; + unsigned long block; +}; + +static int qsort_compare(const void *a, const void *b) { + const struct item *i, *j; + + i = a; + j = b; + + if (i->block < j->block) + return -1; + if (i->block > j->block) + return 1; + + return strcmp(i->path, j->path); +} + +static int collect(const char *root) { + enum { + FD_FANOTIFY, + FD_SIGNAL, + _FD_MAX + }; + struct pollfd pollfd[_FD_MAX]; + int fanotify_fd = -1, signal_fd = -1, r = 0; + pid_t my_pid; + Hashmap *files = NULL; + Iterator i; + char *p, *q; + sigset_t mask; + FILE *pack = NULL; + char *pack_fn_new = NULL, *pack_fn = NULL; + bool on_ssd; + + assert(root); + + if (ioprio_set(IOPRIO_WHO_PROCESS, getpid(), IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0)) < 0) + log_warning("Failed to set IDLE IO priority class: %m"); + + assert_se(sigemptyset(&mask) == 0); + sigset_add_many(&mask, SIGINT, SIGTERM, -1); + assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0); + + if ((signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) { + log_error("signalfd(): %m"); + r = -errno; + goto finish; + } + + if (!(files = hashmap_new(string_hash_func, string_compare_func))) { + log_error("Failed to allocate set."); + r = -ENOMEM; + goto finish; + } + + if ((fanotify_fd = fanotify_init(FAN_CLOEXEC, O_RDONLY|O_LARGEFILE|O_CLOEXEC|O_NOATIME)) < 0) { + log_error("Failed to create fanotify object: %m"); + r = -errno; + goto finish; + } + + if (fanotify_mark(fanotify_fd, FAN_MARK_ADD|FAN_MARK_MOUNT, FAN_OPEN, AT_FDCWD, root) < 0) { + log_error("Failed to mark %s: %m", root); + r = -errno; + goto finish; + } + + my_pid = getpid(); + + zero(pollfd); + pollfd[FD_FANOTIFY].fd = fanotify_fd; + pollfd[FD_FANOTIFY].events = POLLIN; + pollfd[FD_SIGNAL].fd = signal_fd; + pollfd[FD_SIGNAL].events = POLLIN; + + sd_notify(0, + "READY=1\n" + "STATUS=Collecting readahead data"); + + log_debug("Collecting..."); + + for (;;) { + union { + struct fanotify_event_metadata metadata; + char buffer[4096]; + } data; + ssize_t n; + struct fanotify_event_metadata *m; + + if (poll(pollfd, _FD_MAX, -1) < 0) { + + if (errno == EINTR) + continue; + + log_error("poll(): %m"); + r = -errno; + goto finish; + } + + if (pollfd[FD_SIGNAL].revents != 0) + break; + + if ((n = read(fanotify_fd, &data, sizeof(data))) < 0) { + + if (errno == EINTR || errno == EAGAIN) + continue; + + log_error("Failed to read event: %m"); + r = -errno; + goto finish; + } + + m = &data.metadata; + while (FAN_EVENT_OK(m, n)) { + + if (m->pid != my_pid && m->fd >= 0) { + char fn[PATH_MAX]; + int k; + + snprintf(fn, sizeof(fn), "/proc/self/fd/%i", m->fd); + char_array_0(fn); + + if ((k = readlink_malloc(fn, &p)) >= 0) { + + if (hashmap_get(files, p)) + /* Already read */ + free(p); + else { + unsigned long ul; + + ul = fd_first_block(m->fd); + + if ((k = hashmap_put(files, p, ULONG_TO_PTR(ul))) < 0) { + + if (k != -EEXIST) + log_warning("set_put() failed: %s", strerror(-k)); + + free(p); + } + } + + } else + log_warning("readlink(%s) failed: %s", fn, strerror(-k)); + } + + if (m->fd) + close_nointr_nofail(m->fd); + + m = FAN_EVENT_NEXT(m, n); + } + + } + + if (fanotify_fd >= 0) { + close_nointr_nofail(fanotify_fd); + fanotify_fd = -1; + } + + log_debug("Writing Pack File..."); + + on_ssd = fs_on_ssd(root); + log_debug("On SSD: %s", yes_no(on_ssd)); + + asprintf(&pack_fn, "%s/.readahead", root); + asprintf(&pack_fn_new, "%s/.readahead.new", root); + + if (!pack_fn || !pack_fn_new) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + if (!(pack = fopen(pack_fn_new, "we"))) { + log_error("Failed to open pack file: %m"); + r = -errno; + goto finish; + } + + fputs(CANONICAL_HOST "\n", pack); + putc(on_ssd ? 'S' : 'R', pack); + + if (on_ssd) { + + /* On SSD, just write things out in the order the + * files where accessed */ + + HASHMAP_FOREACH_KEY(q, p, files, i) + pack_file(pack, p); + } else { + struct item *ordered, *j; + unsigned k, n; + + /* On rotating media, order things by the block + * numbers */ + + log_debug("Ordering..."); + + n = hashmap_size(files); + if (!(ordered = new(struct item, n))) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + j = ordered; + HASHMAP_FOREACH_KEY(q, p, files, i) { + j->path = p; + j->block = PTR_TO_ULONG(q); + j++; + } + + assert(ordered + n == j); + + qsort(ordered, n, sizeof(struct item), qsort_compare); + + for (k = 0; k < n; k++) + pack_file(pack, ordered[k].path); + + free(ordered); + } + + log_debug("Finalizing..."); + + fflush(pack); + + if (ferror(pack)) { + log_error("Failed to write pack file."); + r = -EIO; + goto finish; + } + + if (rename(pack_fn_new, pack_fn) < 0) { + log_error("Failed to rename readahead file: %m"); + r = -errno; + goto finish; + } + + fclose(pack); + pack = NULL; + + log_debug("Done."); + +finish: + if (fanotify_fd >= 0) + close_nointr_nofail(fanotify_fd); + + if (signal_fd >= 0) + close_nointr_nofail(signal_fd); + + if (pack) { + fclose(pack); + unlink(pack_fn_new); + } + + free(pack_fn_new); + free(pack_fn); + + while ((p = hashmap_steal_first_key(files))) + free(q); + + hashmap_free(files); + + return r; +} + +int main(int argc, char *argv[]) { + /* log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); */ + log_parse_environment(); + log_open(); + + log_set_max_level(LOG_DEBUG); + + if (collect("/") < 0) + return 1; + + return 0; +} diff --git a/src/readahead-common.c b/src/readahead-common.c new file mode 100644 index 0000000000..8533717d47 --- /dev/null +++ b/src/readahead-common.c @@ -0,0 +1,107 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "log.h" +#include "readahead-common.h" +#include "util.h" + +int file_verify(int fd, const char *fn, struct stat *st) { + assert(fd >= 0); + assert(fn); + assert(st); + + if (fstat(fd, st) < 0) { + log_warning("fstat(%s) failed: %m", fn); + return -errno; + } + + if (!S_ISREG(st->st_mode)) { + log_debug("Not preloading special file %s", fn); + return 0; + } + + if (st->st_size <= 0 || st->st_size > READAHEAD_FILE_SIZE_MAX) { + log_debug("Not preloading file %s with size out of bounds %zi", fn, st->st_size); + return 0; + } + + return 1; +} + +int fs_on_ssd(const char *p) { + struct stat st; + struct udev *udev = NULL; + struct udev_device *udev_device = NULL, *look_at = NULL; + bool b = false; + const char *devtype, *rotational, *model, *id; + + assert(p); + + if (stat(p, &st) < 0) + return -errno; + + if (!(udev = udev_new())) + return -ENOMEM; + + if (!(udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev))) + goto finish; + + if ((devtype = udev_device_get_property_value(udev_device, "DEVTYPE")) && + streq(devtype, "partition")) + look_at = udev_device_get_parent(udev_device); + else + look_at = udev_device; + + if (!look_at) + goto finish; + + /* First, try high-level property */ + if ((id = udev_device_get_property_value(look_at, "ID_SSD"))) { + b = streq(id, "1"); + goto finish; + } + + /* Second, try kernel attribute */ + if ((rotational = udev_device_get_sysattr_value(look_at, "queue/rotational"))) + if ((b = streq(rotational, "0"))) + goto finish; + + /* Finally, fallback to heuristics */ + if (!(look_at = udev_device_get_parent(look_at))) + goto finish; + + if ((model = udev_device_get_sysattr_value(look_at, "model"))) + b = !!strstr(model, "SSD"); + +finish: + if (udev_device) + udev_device_unref(udev_device); + + if (udev) + udev_unref(udev); + + return b; +} diff --git a/src/readahead-common.h b/src/readahead-common.h new file mode 100644 index 0000000000..da6a74ea03 --- /dev/null +++ b/src/readahead-common.h @@ -0,0 +1,33 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef fooreadaheadcommonhfoo +#define fooreadaheadcommonhfoo + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include + +#define READAHEAD_FILE_SIZE_MAX (128*1024*1024) + +int file_verify(int fd, const char *fn, struct stat *st); + +int fs_on_ssd(const char *p); + +#endif diff --git a/src/readahead-replay.c b/src/readahead-replay.c new file mode 100644 index 0000000000..b886857ffb --- /dev/null +++ b/src/readahead-replay.c @@ -0,0 +1,214 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "missing.h" +#include "util.h" +#include "set.h" +#include "sd-daemon.h" +#include "ioprio.h" +#include "readahead-common.h" + +static int unpack_file(FILE *pack) { + char fn[PATH_MAX]; + int r = 0, fd = -1; + bool any = false; + struct stat st; + + assert(pack); + + if (!fgets(fn, sizeof(fn), pack)) + return 0; + + char_array_0(fn); + truncate_nl(fn); + + if ((fd = open(fn, O_RDONLY|O_CLOEXEC|O_NOATIME|O_NOCTTY|O_NOFOLLOW)) < 0) + log_warning("open(%s) failed: %m", fn); + else if (file_verify(fd, fn, &st) <= 0) { + close_nointr_nofail(fd); + fd = -1; + } + + for (;;) { + uint32_t b, c; + + if (fread(&b, sizeof(b), 1, pack) != 1 || + fread(&c, sizeof(c), 1, pack) != 1) { + log_error("Premature end of pack file."); + r = -EIO; + goto finish; + } + + if (b == 0 && c == 0) + break; + + if (c <= b) { + log_error("Invalid pack file."); + r = -EIO; + goto finish; + } + + log_debug("%s: page %u to %u", fn, b, c); + + any = true; + + if (fd >= 0) + if (readahead(fd, b * PAGE_SIZE, (c - b) * PAGE_SIZE) < 0) { + log_warning("readahead() failed: %m"); + goto finish; + } + } + + if (!any && fd >= 0) { + /* if no range is encoded in the pack file this is + * intended to mean that the whole file shall be + * read */ + + if (readahead(fd, 0, st.st_size) < 0) { + log_warning("readahead() failed: %m"); + goto finish; + } + } + +finish: + if (fd >= 0) + close_nointr_nofail(fd); + + return r; +} + +static int replay(const char *root) { + FILE *pack; + char line[LINE_MAX]; + int r = 0; + char *pack_fn = NULL, c; + bool on_ssd; + int prio; + + assert(root); + + if (asprintf(&pack_fn, "%s/.readahead", root) < 0) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + if ((!(pack = fopen(pack_fn, "re")))) { + if (errno == -ENOENT) + log_debug("No pack file found."); + else { + log_error("Failed to open pack file: %m"); + r = -errno; + } + + goto finish; + } + + if (!(fgets(line, sizeof(line), pack))) { + log_error("Premature end of pack file."); + r = -EIO; + goto finish; + } + + char_array_0(line); + + if (!streq(line, CANONICAL_HOST "\n")) { + log_debug("Pack file host type mismatch."); + goto finish; + } + + if ((c = getc(pack)) == EOF) { + log_debug("Premature end of pack file."); + r = -EIO; + goto finish; + } + + /* We do not retest SSD here, so that we can start replaying + * before udev is up.*/ + on_ssd = c == 'S'; + log_debug("On SSD: %s", yes_no(on_ssd)); + + if (on_ssd) + prio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0); + else + prio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_RT, 7); + + if (ioprio_set(IOPRIO_WHO_PROCESS, getpid(), prio) < 0) + log_warning("Failed to set IDLE IO priority class: %m"); + + sd_notify(0, + "READY=1\n" + "STATUS=Replaying readahead data"); + + log_debug("Replaying..."); + + while (!feof(pack) && !ferror(pack)) { + int k; + + if ((k = unpack_file(pack)) < 0) { + r = k; + goto finish; + } + } + + if (ferror(pack)) { + log_error("Failed to read pack file."); + r = -EIO; + goto finish; + } + + log_debug("Done."); + +finish: + if (pack) + fclose(pack); + + free(pack_fn); + + return r; +} + +int main(int argc, char*argv[]) { + /* log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); */ + log_parse_environment(); + log_open(); + + log_set_max_level(LOG_DEBUG); + + if (replay("/") < 0) + return 1; + + return 0; +} -- cgit v1.2.3-54-g00ecf From 5008d5815a6223f01c9fc4c803ec6ec18c8f4e54 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 28 Sep 2010 02:34:02 +0200 Subject: tempfiles: add little utility for creating volatile files/dirs in tmpfs hierarchies --- .gitignore | 1 + Makefile.am | 12 ++- fixme | 8 +- src/tempfiles.c | 307 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 322 insertions(+), 6 deletions(-) create mode 100644 src/tempfiles.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index b5fc8d72b0..4536356383 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-tempfiles systemd-readahead-collect systemd-readahead-replay systemd-reply-password diff --git a/Makefile.am b/Makefile.am index 70a6c190f9..342b2b241b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -94,7 +94,8 @@ rootlibexec_PROGRAMS = \ systemd-vconsole-setup \ systemd-reply-password \ systemd-readahead-collect \ - systemd-readahead-replay + systemd-readahead-replay \ + systemd-tempfiles noinst_PROGRAMS = \ test-engine \ @@ -620,6 +621,15 @@ systemd_modules_load_SOURCES = \ systemd_modules_load_CFLAGS = \ $(AM_CFLAGS) +systemd_tempfiles_LDADD = \ + libsystemd-basic.la + +systemd_tempfiles_SOURCES = \ + src/tempfiles.c + +systemd_tempfiles_CFLAGS = \ + $(AM_CFLAGS) + systemd_modules_load_LDADD = \ libsystemd-basic.la diff --git a/fixme b/fixme index 68a64890e2..f66f07a036 100644 --- a/fixme +++ b/fixme @@ -89,19 +89,17 @@ * ask-password tty timeout -* readahead() vs. fadvise() vs. ioprio - * properly handle multiple inotify events per read() in path.c and util.c -* tmpwatch: what does "-umc" actually mean? - * tmpwatch: lower ioprio * readahead: btrfs/LVM SSD detection * LSB provides should only create targets, never aliases -* d /var/run/screen 0755 root utmp +* s/tempfiles/volatile-files/ + +* use .conf suffix for locale/vconsole/... External: diff --git a/src/tempfiles.c b/src/tempfiles.c new file mode 100644 index 0000000000..80e424f7b6 --- /dev/null +++ b/src/tempfiles.c @@ -0,0 +1,307 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "util.h" +#include "strv.h" +#include "label.h" + +/* This reads all files listed in /etc/tempfiles.d/?*.conf and creates + * them in the file system. This is intended to be used to create + * properly owned directories beneath /tmp, /var/tmp, /var/run and + * /var/lock which are volatile and hence need to be recreated on + * bootup. */ + +static void process_line(const char *fname, unsigned line, const char *buffer, const char *prefix) { + char type; + char *path = NULL; + unsigned mode; + char *user = NULL, *group = NULL; + uid_t uid; + gid_t gid; + bool uid_set = false, gid_set = false; + int n, fd = -1; + + assert(fname); + assert(line >= 1); + assert(buffer); + + if ((n = sscanf(buffer, + "%c " + "%ms " + "%o " + "%ms " + "%ms ", + &type, + &path, + &mode, + &user, + &group)) < 2) { + log_error("[%s:%u] Syntax error.", fname, line); + goto finish; + } + + if (type != 'f' && type != 'd') { + log_error("[%s:%u] Unknown file type '%c'.", fname, line, type); + goto finish; + } + + if (prefix && !path_startswith(path, prefix)) + goto finish; + + if (user && !streq(user, "-")) { + unsigned long lu; + struct passwd *p; + + if (streq(user, "root") || streq(user, "0")) + uid = 0; + else if (safe_atolu(user, &lu) >= 0) + uid = (uid_t) lu; + else if ((p = getpwnam(user))) + uid = p->pw_uid; + else { + log_error("[%s:%u] Unknown user '%s'.", fname, line, user); + goto finish; + } + + uid_set = true; + } + + if (group && !streq(group, "-")) { + unsigned long lu; + struct group *g; + + if (streq(group, "root") || streq(group, "0")) + gid = 0; + else if (safe_atolu(group, &lu) >= 0) + gid = (gid_t) lu; + else if ((g = getgrnam(group))) + gid = g->gr_gid; + else { + log_error("[%s:%u] Unknown group '%s'.", fname, line, group); + goto finish; + } + + gid_set = true; + } + + if (n < 3) + mode = type == 'f' ? 0644 : 0755; + + if (type == 'f') { + mode_t u; + struct stat st; + + u = umask(0); + fd = open(path, O_CREAT|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW, mode); + umask(u); + + if (fd < 0) { + log_error("Failed to create file %s: %m", path); + goto finish; + } + + if (fstat(fd, &st) < 0) { + log_error("stat(%s) failed: %m", path); + goto finish; + } + + if (!S_ISREG(st.st_mode)) { + log_error("%s is not a file.", path); + goto finish; + } + + if (fchmod(fd, mode) < 0) { + log_error("chmod(%s) failed: %m", path); + goto finish; + } + + if (uid_set || gid_set) { + + if (fchown(fd, + uid_set ? uid : (uid_t) -1, + gid_set ? gid : (gid_t) -1) < 0) { + log_error("chown(%s) failed: %m", path); + goto finish; + } + } + + } else if (type == 'd') { + mode_t u; + int r; + struct stat st; + + u = umask(0); + r = mkdir(path, mode); + umask(u); + + if (r < 0 && errno != EEXIST) { + log_error("Failed to create directory %s: %m", path); + goto finish; + } + + if (stat(path, &st) < 0) { + log_error("stat(%s) failed: %m", path); + goto finish; + } + + if (!S_ISDIR(st.st_mode)) { + log_error("%s is not a directory.", path); + goto finish; + } + + if (chmod(path, mode) < 0) { + log_error("chmod(%s) failed: %m", path); + goto finish; + } + + if (uid_set || gid_set) { + + if (chown(path, + uid_set ? uid : (uid_t) -1, + gid_set ? gid : (gid_t) -1) < 0) { + log_error("chown(%s) failed: %m", path); + goto finish; + } + } + } + + label_fix(path); + + log_debug("%s created successfully.", path); + +finish: + free(path); + free(user); + free(group); + + if (fd >= 0) + close_nointr_nofail(fd); +} + +static int scandir_filter(const struct dirent *d) { + assert(d); + + if (ignore_file(d->d_name)) + return 0; + + if (d->d_type != DT_REG && + d->d_type != DT_LNK) + return 0; + + return endswith(d->d_name, ".conf"); +} + +int main(int argc, char *argv[]) { + struct dirent **de = NULL; + int r = EXIT_FAILURE, n, i; + const char *prefix = NULL; + + if (argc > 2) { + log_error("This program takes no more than one argument."); + return EXIT_FAILURE; + } else if (argc > 1) + prefix = argv[1]; + else + prefix = "/"; + + log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_parse_environment(); + log_open(); + + label_init(); + + if ((n = scandir("/etc/tempfiles.d/", &de, scandir_filter, alphasort)) < 0) { + + if (errno == ENOENT) + r = EXIT_SUCCESS; + else + log_error("Failed to enumerate /etc/tempfiles.d/ files: %m"); + + goto finish; + } + + r = EXIT_SUCCESS; + + for (i = 0; i < n; i++) { + int k; + char *fn; + FILE *f; + unsigned j; + + k = asprintf(&fn, "/etc/tempfiles.d/%s", de[i]->d_name); + free(de[i]); + + if (k < 0) { + log_error("Failed to allocate file name."); + r = EXIT_FAILURE; + continue; + } + + f = fopen(fn, "re"); + free(fn); + + if (!f) { + log_error("Failed to open %s: %m", fn); + r = EXIT_FAILURE; + continue; + } + + j = 0; + for (;;) { + char line[LINE_MAX], *l; + + if (!(fgets(line, sizeof(line), f))) + break; + + j++; + + l = strstrip(line); + if (*l == '#' || *l == 0) + continue; + + process_line(de[i]->d_name, j, l, prefix); + } + + if (ferror(f)) { + r = EXIT_FAILURE; + log_error("Failed to read from file: %m"); + } + + fclose(f); + } + + free(de); + +finish: + + return r; +} -- cgit v1.2.3-54-g00ecf From bfaf42d22bbd72a286b519ea121dbf8e799b1fe5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 28 Sep 2010 22:11:27 +0200 Subject: tmpfiles: rename tempfiles to tmpfiles since this isn't windows --- .gitignore | 2 +- Makefile.am | 10 +- src/tempfiles.c | 307 -------------------------------------------------------- src/tmpfiles.c | 307 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 313 insertions(+), 313 deletions(-) delete mode 100644 src/tempfiles.c create mode 100644 src/tmpfiles.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 4536356383..9ba0758a1e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -systemd-tempfiles +systemd-tmpfiles systemd-readahead-collect systemd-readahead-replay systemd-reply-password diff --git a/Makefile.am b/Makefile.am index 980cb8f170..44a2779f09 100644 --- a/Makefile.am +++ b/Makefile.am @@ -95,7 +95,7 @@ rootlibexec_PROGRAMS = \ systemd-reply-password \ systemd-readahead-collect \ systemd-readahead-replay \ - systemd-tempfiles + systemd-tmpfiles noinst_PROGRAMS = \ test-engine \ @@ -627,13 +627,13 @@ systemd_modules_load_SOURCES = \ systemd_modules_load_CFLAGS = \ $(AM_CFLAGS) -systemd_tempfiles_LDADD = \ +systemd_tmpfiles_LDADD = \ libsystemd-basic.la -systemd_tempfiles_SOURCES = \ - src/tempfiles.c +systemd_tmpfiles_SOURCES = \ + src/tmpfiles.c -systemd_tempfiles_CFLAGS = \ +systemd_tmpfiles_CFLAGS = \ $(AM_CFLAGS) systemd_modules_load_LDADD = \ diff --git a/src/tempfiles.c b/src/tempfiles.c deleted file mode 100644 index 80e424f7b6..0000000000 --- a/src/tempfiles.c +++ /dev/null @@ -1,307 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "log.h" -#include "util.h" -#include "strv.h" -#include "label.h" - -/* This reads all files listed in /etc/tempfiles.d/?*.conf and creates - * them in the file system. This is intended to be used to create - * properly owned directories beneath /tmp, /var/tmp, /var/run and - * /var/lock which are volatile and hence need to be recreated on - * bootup. */ - -static void process_line(const char *fname, unsigned line, const char *buffer, const char *prefix) { - char type; - char *path = NULL; - unsigned mode; - char *user = NULL, *group = NULL; - uid_t uid; - gid_t gid; - bool uid_set = false, gid_set = false; - int n, fd = -1; - - assert(fname); - assert(line >= 1); - assert(buffer); - - if ((n = sscanf(buffer, - "%c " - "%ms " - "%o " - "%ms " - "%ms ", - &type, - &path, - &mode, - &user, - &group)) < 2) { - log_error("[%s:%u] Syntax error.", fname, line); - goto finish; - } - - if (type != 'f' && type != 'd') { - log_error("[%s:%u] Unknown file type '%c'.", fname, line, type); - goto finish; - } - - if (prefix && !path_startswith(path, prefix)) - goto finish; - - if (user && !streq(user, "-")) { - unsigned long lu; - struct passwd *p; - - if (streq(user, "root") || streq(user, "0")) - uid = 0; - else if (safe_atolu(user, &lu) >= 0) - uid = (uid_t) lu; - else if ((p = getpwnam(user))) - uid = p->pw_uid; - else { - log_error("[%s:%u] Unknown user '%s'.", fname, line, user); - goto finish; - } - - uid_set = true; - } - - if (group && !streq(group, "-")) { - unsigned long lu; - struct group *g; - - if (streq(group, "root") || streq(group, "0")) - gid = 0; - else if (safe_atolu(group, &lu) >= 0) - gid = (gid_t) lu; - else if ((g = getgrnam(group))) - gid = g->gr_gid; - else { - log_error("[%s:%u] Unknown group '%s'.", fname, line, group); - goto finish; - } - - gid_set = true; - } - - if (n < 3) - mode = type == 'f' ? 0644 : 0755; - - if (type == 'f') { - mode_t u; - struct stat st; - - u = umask(0); - fd = open(path, O_CREAT|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW, mode); - umask(u); - - if (fd < 0) { - log_error("Failed to create file %s: %m", path); - goto finish; - } - - if (fstat(fd, &st) < 0) { - log_error("stat(%s) failed: %m", path); - goto finish; - } - - if (!S_ISREG(st.st_mode)) { - log_error("%s is not a file.", path); - goto finish; - } - - if (fchmod(fd, mode) < 0) { - log_error("chmod(%s) failed: %m", path); - goto finish; - } - - if (uid_set || gid_set) { - - if (fchown(fd, - uid_set ? uid : (uid_t) -1, - gid_set ? gid : (gid_t) -1) < 0) { - log_error("chown(%s) failed: %m", path); - goto finish; - } - } - - } else if (type == 'd') { - mode_t u; - int r; - struct stat st; - - u = umask(0); - r = mkdir(path, mode); - umask(u); - - if (r < 0 && errno != EEXIST) { - log_error("Failed to create directory %s: %m", path); - goto finish; - } - - if (stat(path, &st) < 0) { - log_error("stat(%s) failed: %m", path); - goto finish; - } - - if (!S_ISDIR(st.st_mode)) { - log_error("%s is not a directory.", path); - goto finish; - } - - if (chmod(path, mode) < 0) { - log_error("chmod(%s) failed: %m", path); - goto finish; - } - - if (uid_set || gid_set) { - - if (chown(path, - uid_set ? uid : (uid_t) -1, - gid_set ? gid : (gid_t) -1) < 0) { - log_error("chown(%s) failed: %m", path); - goto finish; - } - } - } - - label_fix(path); - - log_debug("%s created successfully.", path); - -finish: - free(path); - free(user); - free(group); - - if (fd >= 0) - close_nointr_nofail(fd); -} - -static int scandir_filter(const struct dirent *d) { - assert(d); - - if (ignore_file(d->d_name)) - return 0; - - if (d->d_type != DT_REG && - d->d_type != DT_LNK) - return 0; - - return endswith(d->d_name, ".conf"); -} - -int main(int argc, char *argv[]) { - struct dirent **de = NULL; - int r = EXIT_FAILURE, n, i; - const char *prefix = NULL; - - if (argc > 2) { - log_error("This program takes no more than one argument."); - return EXIT_FAILURE; - } else if (argc > 1) - prefix = argv[1]; - else - prefix = "/"; - - log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); - log_parse_environment(); - log_open(); - - label_init(); - - if ((n = scandir("/etc/tempfiles.d/", &de, scandir_filter, alphasort)) < 0) { - - if (errno == ENOENT) - r = EXIT_SUCCESS; - else - log_error("Failed to enumerate /etc/tempfiles.d/ files: %m"); - - goto finish; - } - - r = EXIT_SUCCESS; - - for (i = 0; i < n; i++) { - int k; - char *fn; - FILE *f; - unsigned j; - - k = asprintf(&fn, "/etc/tempfiles.d/%s", de[i]->d_name); - free(de[i]); - - if (k < 0) { - log_error("Failed to allocate file name."); - r = EXIT_FAILURE; - continue; - } - - f = fopen(fn, "re"); - free(fn); - - if (!f) { - log_error("Failed to open %s: %m", fn); - r = EXIT_FAILURE; - continue; - } - - j = 0; - for (;;) { - char line[LINE_MAX], *l; - - if (!(fgets(line, sizeof(line), f))) - break; - - j++; - - l = strstrip(line); - if (*l == '#' || *l == 0) - continue; - - process_line(de[i]->d_name, j, l, prefix); - } - - if (ferror(f)) { - r = EXIT_FAILURE; - log_error("Failed to read from file: %m"); - } - - fclose(f); - } - - free(de); - -finish: - - return r; -} diff --git a/src/tmpfiles.c b/src/tmpfiles.c new file mode 100644 index 0000000000..80e424f7b6 --- /dev/null +++ b/src/tmpfiles.c @@ -0,0 +1,307 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "util.h" +#include "strv.h" +#include "label.h" + +/* This reads all files listed in /etc/tempfiles.d/?*.conf and creates + * them in the file system. This is intended to be used to create + * properly owned directories beneath /tmp, /var/tmp, /var/run and + * /var/lock which are volatile and hence need to be recreated on + * bootup. */ + +static void process_line(const char *fname, unsigned line, const char *buffer, const char *prefix) { + char type; + char *path = NULL; + unsigned mode; + char *user = NULL, *group = NULL; + uid_t uid; + gid_t gid; + bool uid_set = false, gid_set = false; + int n, fd = -1; + + assert(fname); + assert(line >= 1); + assert(buffer); + + if ((n = sscanf(buffer, + "%c " + "%ms " + "%o " + "%ms " + "%ms ", + &type, + &path, + &mode, + &user, + &group)) < 2) { + log_error("[%s:%u] Syntax error.", fname, line); + goto finish; + } + + if (type != 'f' && type != 'd') { + log_error("[%s:%u] Unknown file type '%c'.", fname, line, type); + goto finish; + } + + if (prefix && !path_startswith(path, prefix)) + goto finish; + + if (user && !streq(user, "-")) { + unsigned long lu; + struct passwd *p; + + if (streq(user, "root") || streq(user, "0")) + uid = 0; + else if (safe_atolu(user, &lu) >= 0) + uid = (uid_t) lu; + else if ((p = getpwnam(user))) + uid = p->pw_uid; + else { + log_error("[%s:%u] Unknown user '%s'.", fname, line, user); + goto finish; + } + + uid_set = true; + } + + if (group && !streq(group, "-")) { + unsigned long lu; + struct group *g; + + if (streq(group, "root") || streq(group, "0")) + gid = 0; + else if (safe_atolu(group, &lu) >= 0) + gid = (gid_t) lu; + else if ((g = getgrnam(group))) + gid = g->gr_gid; + else { + log_error("[%s:%u] Unknown group '%s'.", fname, line, group); + goto finish; + } + + gid_set = true; + } + + if (n < 3) + mode = type == 'f' ? 0644 : 0755; + + if (type == 'f') { + mode_t u; + struct stat st; + + u = umask(0); + fd = open(path, O_CREAT|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW, mode); + umask(u); + + if (fd < 0) { + log_error("Failed to create file %s: %m", path); + goto finish; + } + + if (fstat(fd, &st) < 0) { + log_error("stat(%s) failed: %m", path); + goto finish; + } + + if (!S_ISREG(st.st_mode)) { + log_error("%s is not a file.", path); + goto finish; + } + + if (fchmod(fd, mode) < 0) { + log_error("chmod(%s) failed: %m", path); + goto finish; + } + + if (uid_set || gid_set) { + + if (fchown(fd, + uid_set ? uid : (uid_t) -1, + gid_set ? gid : (gid_t) -1) < 0) { + log_error("chown(%s) failed: %m", path); + goto finish; + } + } + + } else if (type == 'd') { + mode_t u; + int r; + struct stat st; + + u = umask(0); + r = mkdir(path, mode); + umask(u); + + if (r < 0 && errno != EEXIST) { + log_error("Failed to create directory %s: %m", path); + goto finish; + } + + if (stat(path, &st) < 0) { + log_error("stat(%s) failed: %m", path); + goto finish; + } + + if (!S_ISDIR(st.st_mode)) { + log_error("%s is not a directory.", path); + goto finish; + } + + if (chmod(path, mode) < 0) { + log_error("chmod(%s) failed: %m", path); + goto finish; + } + + if (uid_set || gid_set) { + + if (chown(path, + uid_set ? uid : (uid_t) -1, + gid_set ? gid : (gid_t) -1) < 0) { + log_error("chown(%s) failed: %m", path); + goto finish; + } + } + } + + label_fix(path); + + log_debug("%s created successfully.", path); + +finish: + free(path); + free(user); + free(group); + + if (fd >= 0) + close_nointr_nofail(fd); +} + +static int scandir_filter(const struct dirent *d) { + assert(d); + + if (ignore_file(d->d_name)) + return 0; + + if (d->d_type != DT_REG && + d->d_type != DT_LNK) + return 0; + + return endswith(d->d_name, ".conf"); +} + +int main(int argc, char *argv[]) { + struct dirent **de = NULL; + int r = EXIT_FAILURE, n, i; + const char *prefix = NULL; + + if (argc > 2) { + log_error("This program takes no more than one argument."); + return EXIT_FAILURE; + } else if (argc > 1) + prefix = argv[1]; + else + prefix = "/"; + + log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_parse_environment(); + log_open(); + + label_init(); + + if ((n = scandir("/etc/tempfiles.d/", &de, scandir_filter, alphasort)) < 0) { + + if (errno == ENOENT) + r = EXIT_SUCCESS; + else + log_error("Failed to enumerate /etc/tempfiles.d/ files: %m"); + + goto finish; + } + + r = EXIT_SUCCESS; + + for (i = 0; i < n; i++) { + int k; + char *fn; + FILE *f; + unsigned j; + + k = asprintf(&fn, "/etc/tempfiles.d/%s", de[i]->d_name); + free(de[i]); + + if (k < 0) { + log_error("Failed to allocate file name."); + r = EXIT_FAILURE; + continue; + } + + f = fopen(fn, "re"); + free(fn); + + if (!f) { + log_error("Failed to open %s: %m", fn); + r = EXIT_FAILURE; + continue; + } + + j = 0; + for (;;) { + char line[LINE_MAX], *l; + + if (!(fgets(line, sizeof(line), f))) + break; + + j++; + + l = strstrip(line); + if (*l == '#' || *l == 0) + continue; + + process_line(de[i]->d_name, j, l, prefix); + } + + if (ferror(f)) { + r = EXIT_FAILURE; + log_error("Failed to read from file: %m"); + } + + fclose(f); + } + + free(de); + +finish: + + return r; +} -- cgit v1.2.3-54-g00ecf From e3478379751fda30316204c68112b5a2b9ffd5a7 Mon Sep 17 00:00:00 2001 From: Fabiano Fidencio Date: Thu, 7 Oct 2010 05:43:57 -0700 Subject: umount: Adding unmount functions to be used in shutdown This functions will: - umount all mount points that aren't API - remount read-only all mount points that can't be umounted - umount all swap devices. - detach all loopback devices TODO: - umount dms Mountpoints are being read from /proc/self/mountinfo. Swaps are being read from /proc/swaps. Loop devices from /sys/class/block/loop*. --- .gitignore | 1 + src/umount.c | 410 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/umount.h | 31 +++++ 3 files changed, 442 insertions(+) create mode 100644 src/umount.c create mode 100644 src/umount.h (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 9ba0758a1e..2ba000c0b8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-shutdown systemd-tmpfiles systemd-readahead-collect systemd-readahead-replay diff --git a/src/umount.c b/src/umount.c new file mode 100644 index 0000000000..4842d403ae --- /dev/null +++ b/src/umount.c @@ -0,0 +1,410 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 ProFUSION embedded systems + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "list.h" +#include "mount-setup.h" +#include "umount.h" +#include "util.h" + +typedef struct MountPoint { + char *path; + bool read_only; + LIST_FIELDS (struct MountPoint, mount_point); +} MountPoint; + +static MountPoint *mount_point_alloc(char *path) { + MountPoint *mp; + + if (!(mp = new(MountPoint, 1))) + return NULL; + + mp->path = path; + mp->read_only = false; + + return mp; +} + +static void mount_point_remove_and_free(MountPoint *mount_point, MountPoint **mount_point_list_head) { + LIST_REMOVE(MountPoint, mount_point, *mount_point_list_head, mount_point); + + free(mount_point->path); + free(mount_point); +} + +static void mount_points_list_free(MountPoint **mount_point_list_head) { + while (*mount_point_list_head) + mount_point_remove_and_free(*mount_point_list_head, mount_point_list_head); +} + +static int mount_points_list_get(MountPoint **mount_point_list_head) { + FILE *proc_self_mountinfo; + char *path, *p; + unsigned int i; + int r; + + if (!(proc_self_mountinfo = fopen("/proc/self/mountinfo", "re"))) + return -errno; + + for (i = 1;; i++) { + int k; + MountPoint *mp; + + path = p = NULL; + + if ((k = fscanf(proc_self_mountinfo, + "%*s " /* (1) mount id */ + "%*s " /* (2) parent id */ + "%*s " /* (3) major:minor */ + "%*s " /* (4) root */ + "%ms " /* (5) mount point */ + "%*s" /* (6) mount options */ + "%*[^-]" /* (7) optional fields */ + "- " /* (8) separator */ + "%*s " /* (9) file system type */ + "%*s" /* (10) mount source */ + "%*s" /* (11) mount options 2 */ + "%*[^\n]", /* some rubbish at the end */ + &path)) != 1) { + if (k == EOF) + break; + + log_warning("Failed to parse /proc/self/mountinfo:%u.", i); + + free(path); + continue; + } + + if (mount_point_is_api(path)) { + free(path); + continue; + } + + if (!(p = cunescape(path))) { + r = -ENOMEM; + goto finish; + } + + if (!(mp = mount_point_alloc(p))) { + r = -ENOMEM; + goto finish; + } + LIST_PREPEND(MountPoint, mount_point, *mount_point_list_head, mp); + + free(path); + } + + r = 0; + +finish: + fclose(proc_self_mountinfo); + + free(path); + + return r; +} + +static int swap_list_get(MountPoint **swap_list_head) { + FILE *proc_swaps; + unsigned int i; + int r; + + if (!(proc_swaps = fopen("/proc/swaps", "re"))) + return -errno; + + (void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n"); + + for (i = 2;; i++) { + MountPoint *swap; + char *dev = NULL, *d; + int k; + + if ((k = fscanf(proc_swaps, + "%ms " /* device/file */ + "%*s " /* type of swap */ + "%*s " /* swap size */ + "%*s " /* used */ + "%*s\n", /* priority */ + &dev)) != 1) { + + if (k == EOF) + break; + + log_warning("Failed to parse /proc/swaps:%u.", i); + + free(dev); + continue; + } + + if (endswith(dev, "(deleted)")) { + free(dev); + continue; + } + + d = cunescape(dev); + free(dev); + + if (!d) { + r = -ENOMEM; + goto finish; + } + + swap = mount_point_alloc(d); + if (!swap) { + free(d); + r = -ENOMEM; + goto finish; + } + + LIST_PREPEND(MountPoint, mount_point, *swap_list_head, swap); + } + + r = 0; + +finish: + fclose(proc_swaps); + + return r; +} + +static int loopback_list_get(MountPoint **loopback_list_head) { + int r; + struct udev *udev; + struct udev_enumerate *e = NULL; + struct udev_list_entry *item = NULL, *first = NULL; + + if (!(udev = udev_new())) { + r = -ENOMEM; + goto finish; + } + + if (!(e = udev_enumerate_new(udev))) { + r = -ENOMEM; + goto finish; + } + + if (udev_enumerate_add_match_subsystem(e, "block") < 0) { + r = -EIO; + goto finish; + } + + if (udev_enumerate_add_match_sysname(e, "loop*") < 0) { + r = -EIO; + goto finish; + } + + if (udev_enumerate_scan_devices(e) < 0) { + r = -EIO; + goto finish; + } + + first = udev_enumerate_get_list_entry(e); + + udev_list_entry_foreach(item, first) { + MountPoint *lb; + char *loop; + + loop = cunescape(udev_list_entry_get_name(item)); + if (!loop) { + r = -ENOMEM; + goto finish; + } + + lb = mount_point_alloc(loop); + if (!lb) { + free(loop); + r = -ENOMEM; + goto finish; + } + + LIST_PREPEND(MountPoint, mount_point, *loopback_list_head, lb); + } + + r = 0; + +finish: + if (e) + udev_enumerate_unref(e); + + free(udev); + return r; +} + +static int delete_loopback(const char *device) { + int fd, r; + + if ((fd = open(device, O_RDONLY|O_CLOEXEC)) < 0) { + if (errno == ENOENT) { + log_debug("Loop device %s does not exist.", device); + errno = 0; + return 0; + } + return -errno; + } + + ioctl(fd, LOOP_CLR_FD, 0); + r = errno; + close_nointr(fd); + + if (r == ENXIO) /* not bound, so no error */ + r = 0; + errno = r; + return -errno; +} + +static int mount_points_list_umount(MountPoint **mount_point_list_head) { + MountPoint *mp, *mp_next; + int failed = 0; + + LIST_FOREACH_SAFE(mount_point, mp, mp_next, *mount_point_list_head) { + if (streq(mp->path, "/")) + continue; + + /* Trying to umount. Forcing to umount if busy (only for NFS mounts) */ + if (umount2(mp->path, MNT_FORCE) == 0) + mount_point_remove_and_free(mp, mount_point_list_head); + else { + log_debug("Could not unmount %s: %m", mp->path); + failed++; + } + } + + return failed; +} + +static int mount_points_list_remount_read_only(MountPoint **mount_point_list_head) { + MountPoint *mp, *mp_next; + int failed = 0; + + LIST_FOREACH_SAFE(mount_point, mp, mp_next, *mount_point_list_head) { + if (mp->read_only) + continue; + + /* Trying to remount read-only */ + if (mount(NULL, mp->path, NULL, MS_MGC_VAL|MS_REMOUNT|MS_RDONLY, NULL) == 0) { + mp->read_only = true; + mount_point_remove_and_free(mp, mount_point_list_head); + } else { + log_debug("Could not remount as read-only %s: %m", mp->path); + failed++; + } + } + + return failed; +} + +static int swap_points_list_off(MountPoint **swap_list_head) { + MountPoint *swap, *swap_next; + int failed = 0; + + LIST_FOREACH_SAFE(mount_point, swap, swap_next, *swap_list_head) { + if (swapoff(swap->path) == 0) + mount_point_remove_and_free(swap, swap_list_head); + else { + log_debug("Could not swapoff %s: %m", swap->path); + failed++; + } + } + + return failed; +} + +static int loopback_points_list_detach(MountPoint **loopback_list_head) { + MountPoint *loopback, *loopback_next; + int failed = 0; + + LIST_FOREACH_SAFE(mount_point, loopback, loopback_next, *loopback_list_head) { + if (delete_loopback(loopback->path) == 0) + mount_point_remove_and_free(loopback, loopback_list_head); + else { + log_debug("Could not delete loopback %s: %m", loopback->path); + failed++; + } + } + + return failed; +} + +int umount_all(void) { + int r; + LIST_HEAD(MountPoint, mp_list_head); + + LIST_HEAD_INIT(MountPoint, mp_list_head); + + r = mount_points_list_get(&mp_list_head); + if (r < 0) + goto end; + + r = mount_points_list_umount(&mp_list_head); + if (r <= 0) + goto end; + + r = mount_points_list_remount_read_only(&mp_list_head); + + end: + mount_points_list_free(&mp_list_head); + + return r; +} + +int swapoff_all(void) { + int r; + LIST_HEAD(MountPoint, swap_list_head); + + LIST_HEAD_INIT(MountPoint, swap_list_head); + + r = swap_list_get(&swap_list_head); + if (r < 0) + goto end; + + r = swap_points_list_off(&swap_list_head); + + end: + mount_points_list_free(&swap_list_head); + + return r; +} + +int loopback_detach_all(void) { + int r; + LIST_HEAD(MountPoint, loopback_list_head); + + LIST_HEAD_INIT(MountPoint, loopback_list_head); + + r = loopback_list_get(&loopback_list_head); + if (r < 0) + goto end; + + r = loopback_points_list_detach(&loopback_list_head); + + end: + mount_points_list_free(&loopback_list_head); + + return r; +} diff --git a/src/umount.h b/src/umount.h new file mode 100644 index 0000000000..aeccc00f1e --- /dev/null +++ b/src/umount.h @@ -0,0 +1,31 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef fooumounthfoo +#define fooumounthfoo + +/*** + This file is part of systemd. + + Copyright 2010 ProFUSION embedded systems + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +int umount_all(void); + +int swapoff_all(void); + +int loopback_detach_all(void); + +#endif -- cgit v1.2.3-54-g00ecf From e92787416c691c3f34f47349e5eae3fa68eae856 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 7 Oct 2010 22:38:40 +0200 Subject: user-sessions: add minimal utility to kill user sessions that shall be called before destroying services on shutdown --- .gitignore | 1 + Makefile.am | 13 +++++++++- src/user-sessions.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 src/user-sessions.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 2ba000c0b8..5cb935e9d1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-user-sessions systemd-shutdown systemd-tmpfiles systemd-readahead-collect diff --git a/Makefile.am b/Makefile.am index a79e94e893..7d0ca9a954 100644 --- a/Makefile.am +++ b/Makefile.am @@ -107,7 +107,8 @@ rootlibexec_PROGRAMS = \ systemd-reply-password \ systemd-readahead-collect \ systemd-readahead-replay \ - systemd-tmpfiles + systemd-tmpfiles \ + systemd-user-sessions noinst_PROGRAMS = \ test-engine \ @@ -671,6 +672,16 @@ systemd_tmpfiles_SOURCES = \ systemd_tmpfiles_CFLAGS = \ $(AM_CFLAGS) +systemd_user_sessions_LDADD = \ + libsystemd-basic.la + +systemd_user_sessions_SOURCES = \ + src/user-sessions.c \ + src/cgroup-util.c + +systemd_user_sessions_CFLAGS = \ + $(AM_CFLAGS) + systemd_modules_load_LDADD = \ libsystemd-basic.la diff --git a/src/user-sessions.c b/src/user-sessions.c new file mode 100644 index 0000000000..dc4ee0fb4a --- /dev/null +++ b/src/user-sessions.c @@ -0,0 +1,74 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "log.h" +#include "util.h" +#include "cgroup-util.h" + +int main(int argc, char*argv[]) { + int ret = EXIT_FAILURE; + + if (argc != 2) { + log_error("This program requires one argument."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_parse_environment(); + log_open(); + + if (streq(argv[1], "start")) { + + if (unlink("/var/run/nologin") < 0 || + unlink("/etc/nologin") < 0) { + + if (errno != ENOENT) { + log_error("Failed to remove nologin files: %m"); + goto finish; + } + } + + } else if (streq(argv[1], "stop")) { + int r, q; + + if ((r = write_one_line_file("/var/run/nologin", "System is going down.")) < 0) + log_error("Failed to create /var/run/nologin: %s", strerror(-r)); + + if ((q = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, "/user", true)) < 0) + log_error("Failed to kill sessions: %s", strerror(-q)); + + if (r < 0 || q < 0) + goto finish; + + } else { + log_error("Unknown verb %s.", argv[1]); + goto finish; + } + + ret = EXIT_SUCCESS; + +finish: + return ret; +} -- cgit v1.2.3-54-g00ecf From 3d20ed6d51e38968cd646e2b3b24f36673408024 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 19 Oct 2010 21:19:12 +0200 Subject: fsck: add initial version of fsck and quotacheck wrappers --- .gitignore | 2 + Makefile.am | 34 ++++++++- src/ac-power.c | 98 +++++++++++++++++++++++++ src/ac-power.h | 27 +++++++ src/fsck.c | 218 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/quotacheck.c | 115 +++++++++++++++++++++++++++++ src/umount.c | 1 - 7 files changed, 490 insertions(+), 5 deletions(-) create mode 100644 src/ac-power.c create mode 100644 src/ac-power.h create mode 100644 src/fsck.c create mode 100644 src/quotacheck.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 5cb935e9d1..ad0f5f4e72 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +systemd-fsck +systemd-quotacheck systemd-user-sessions systemd-shutdown systemd-tmpfiles diff --git a/Makefile.am b/Makefile.am index 0311302820..92b1e22159 100644 --- a/Makefile.am +++ b/Makefile.am @@ -108,7 +108,9 @@ rootlibexec_PROGRAMS = \ systemd-readahead-collect \ systemd-readahead-replay \ systemd-tmpfiles \ - systemd-user-sessions + systemd-user-sessions \ + systemd-fsck \ + systemd-quotacheck noinst_PROGRAMS = \ test-engine \ @@ -662,7 +664,7 @@ systemd_modules_load_SOURCES = \ systemd_modules_load_CFLAGS = \ $(AM_CFLAGS) -systemd_tmpfiles_LDADD = \ +systemd_modules_load_LDADD = \ libsystemd-basic.la systemd_tmpfiles_SOURCES = \ @@ -671,7 +673,31 @@ systemd_tmpfiles_SOURCES = \ systemd_tmpfiles_CFLAGS = \ $(AM_CFLAGS) -systemd_user_sessions_LDADD = \ +systemd_tmpfiles_LDADD = \ + libsystemd-basic.la + +systemd_fsck_SOURCES = \ + src/fsck.c \ + src/ac-power.c \ + src/dbus-common.c + +systemd_fsck_CFLAGS = \ + $(AM_CFLAGS) \ + $(UDEV_CFLAGS) \ + $(DBUS_CFLAGS) + +systemd_fsck_LDADD = \ + libsystemd-basic.la \ + $(UDEV_LIBS) \ + $(DBUS_LIBS) + +systemd_quotacheck_SOURCES = \ + src/quotacheck.c + +systemd_quotacheck_CFLAGS = \ + $(AM_CFLAGS) + +systemd_quotacheck_LDADD = \ libsystemd-basic.la systemd_user_sessions_SOURCES = \ @@ -681,7 +707,7 @@ systemd_user_sessions_SOURCES = \ systemd_user_sessions_CFLAGS = \ $(AM_CFLAGS) -systemd_modules_load_LDADD = \ +systemd_user_sessions_LDADD = \ libsystemd-basic.la systemd_vconsole_setup_SOURCES = \ diff --git a/src/ac-power.c b/src/ac-power.c new file mode 100644 index 0000000000..a30216ffb3 --- /dev/null +++ b/src/ac-power.c @@ -0,0 +1,98 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "util.h" +#include "ac-power.h" + +int on_ac_power(void) { + int r; + + struct udev *udev; + struct udev_enumerate *e = NULL; + struct udev_list_entry *item = NULL, *first = NULL; + bool found_offline = false, found_online = false; + + if (!(udev = udev_new())) { + r = -ENOMEM; + goto finish; + } + + if (!(e = udev_enumerate_new(udev))) { + r = -ENOMEM; + goto finish; + } + + if (udev_enumerate_add_match_subsystem(e, "power_supply") < 0) { + r = -EIO; + goto finish; + } + + if (udev_enumerate_scan_devices(e) < 0) { + r = -EIO; + goto finish; + } + + first = udev_enumerate_get_list_entry(e); + udev_list_entry_foreach(item, first) { + struct udev_device *d; + const char *type, *online; + + if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) { + r = -ENOMEM; + goto finish; + } + + if (!(type = udev_device_get_sysattr_value(d, "type"))) + goto next; + + if (!streq(type, "Mains")) + goto next; + + if (!(online = udev_device_get_sysattr_value(d, "online"))) + goto next; + + if (streq(online, "1")) { + found_online = true; + break; + } else if (streq(online, "0")) + found_offline = true; + + next: + udev_device_unref(d); + } + + r = found_online || !found_offline; + +finish: + if (e) + udev_enumerate_unref(e); + + if (udev) + udev_unref(udev); + + return r; +} diff --git a/src/ac-power.h b/src/ac-power.h new file mode 100644 index 0000000000..24f28b5e14 --- /dev/null +++ b/src/ac-power.h @@ -0,0 +1,27 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef fooacpowerhfoo +#define fooacpowerhfoo + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +int on_ac_power(void); + +#endif diff --git a/src/fsck.c b/src/fsck.c new file mode 100644 index 0000000000..0b0cf64036 --- /dev/null +++ b/src/fsck.c @@ -0,0 +1,218 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include + +#include "util.h" +#include "ac-power.h" +#include "dbus-common.h" +#include "special.h" + +static bool arg_skip = false; +static bool arg_force = false; + +static void start_target(const char *target, bool isolate) { + DBusMessage *m = NULL, *reply = NULL; + DBusError error; + const char *mode; + DBusConnection *bus = NULL; + + assert(target); + + dbus_error_init(&error); + + if (bus_connect(DBUS_BUS_SYSTEM, &bus, NULL, &error) < 0) { + log_error("Failed to get D-Bus connection: %s", bus_error_message(&error)); + goto finish; + } + + if (isolate) + mode = "isolate"; + else + mode = "replace"; + + log_debug("Running request %s/start/%s", target, mode); + + if (!(m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartUnit"))) { + log_error("Could not allocate message."); + goto finish; + } + + if (!dbus_message_append_args(m, + DBUS_TYPE_STRING, &target, + DBUS_TYPE_STRING, &mode, + DBUS_TYPE_INVALID)) { + log_error("Could not attach target and flag information to message."); + goto finish; + } + + if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { + log_error("Failed to start unit: %s", bus_error_message(&error)); + goto finish; + } + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + if (bus) { + dbus_connection_flush(bus); + dbus_connection_close(bus); + dbus_connection_unref(bus); + } + + dbus_error_free(&error); +} + +static int parse_proc_cmdline(void) { + char *line, *w, *state; + int r; + size_t l; + + if ((r = read_one_line_file("/proc/cmdline", &line)) < 0) { + log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r)); + return 0; + } + + FOREACH_WORD_QUOTED(w, l, line, state) { + + if (strneq(w, "fsck.mode=auto", l)) + arg_force = arg_skip = false; + else if (strneq(w, "fsck.mode=force", l)) + arg_force = true; + else if (strneq(w, "fsck.mode=skip", l)) + arg_skip = true; + else if (startswith(w, "fsck.mode")) + log_warning("Invalid fsck.mode= parameter. Ignoring."); +#ifdef TARGET_FEDORA + else if (strneq(w, "fastboot", l)) + arg_skip = true; + else if (strneq(w, "forcefsck", l)) + arg_force = true; +#endif + } + + free(line); + return 0; +} + +static void test_files(void) { + if (access("/fastboot", F_OK) >= 0) + arg_skip = true; + + if (access("/forcefsck", F_OK) >= 0) + arg_force = true; +} + +int main(int argc, char *argv[]) { + static const char * cmdline[7]; + int i = 0, r = EXIT_FAILURE, q; + pid_t pid; + siginfo_t status; + + if (argc != 2) { + log_error("This program expects exactly one argument."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_parse_environment(); + log_open(); + + parse_proc_cmdline(); + test_files(); + + if (!arg_force) { + if (arg_skip) + return 0; + + /* FIXME: only execute necessary fsck's if no AC power present */ + if (on_ac_power() == 0) + return 0; + } + + cmdline[i++] = "/sbin/fsck"; + cmdline[i++] = "-a"; + cmdline[i++] = "-T"; + cmdline[i++] = "-C"; + + if (arg_force) + cmdline[i++] = "-f"; + + cmdline[i++] = argv[1]; + cmdline[i++] = NULL; + + if ((pid = fork()) < 0) { + log_error("fork(): %m"); + goto finish; + } else if (pid == 0) { + /* Child */ + execv(cmdline[0], (char**) cmdline); + _exit(8); /* Operational error */ + } + + if ((q = wait_for_terminate(pid, &status)) < 0) { + log_error("waitid(): %s", strerror(-q)); + goto finish; + } + + if (status.si_code == CLD_KILLED || + status.si_code == CLD_DUMPED) { + log_error("fsck terminated by signal %s.", signal_to_string(status.si_status)); + goto finish; + + } else if (status.si_code != CLD_EXITED) { + log_error("fsck failed due to unknown reason."); + goto finish; + } + + if (status.si_status & ~1) { + + if (access("/dev/.systemd/late-fsck", F_OK) >= 0) { + log_error("fsck failed with error code %i.", status.si_status); + goto finish; + } + + if (status.si_status & 2) + /* System should be rebooted. */ + start_target(SPECIAL_REBOOT_TARGET, false); + else + /* Some other problem */ + start_target(SPECIAL_EMERGENCY_TARGET, true); + + } else + r = EXIT_SUCCESS; + + if (status.si_status & 1) + touch("/dev/.systemd/quotacheck"); + +finish: + return r; +} diff --git a/src/quotacheck.c b/src/quotacheck.c new file mode 100644 index 0000000000..a579c5e8b7 --- /dev/null +++ b/src/quotacheck.c @@ -0,0 +1,115 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "util.h" + +static bool arg_skip = false; +static bool arg_force = false; + +static int parse_proc_cmdline(void) { + char *line, *w, *state; + int r; + size_t l; + + if ((r = read_one_line_file("/proc/cmdline", &line)) < 0) { + log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r)); + return 0; + } + + FOREACH_WORD_QUOTED(w, l, line, state) { + + if (strneq(w, "quotacheck.mode=auto", l)) + arg_force = arg_skip = false; + else if (strneq(w, "quotacheck.mode=force", l)) + arg_force = true; + else if (strneq(w, "quotacheck.mode=skip", l)) + arg_skip = true; + else if (startswith(w, "quotacheck.mode")) + log_warning("Invalid quotacheck.mode= parameter. Ignoring."); +#ifdef TARGET_FEDORA + else if (strneq(w, "forcequotacheck", l)) + arg_force = true; +#endif + } + + free(line); + return 0; +} + +static void test_files(void) { +#ifdef TARGET_FEDORA + /* This exists only on Fedora */ + if (access("/forcequoatcheck", F_OK) >= 0) + arg_force = true; +#endif +} + +int main(int argc, char *argv[]) { + static const char * const cmdline[] = { + "/sbin/quotacheck", + "-anug", + NULL + }; + + int r = EXIT_FAILURE; + pid_t pid; + + if (argc > 1) { + log_error("This program takes no arguments."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_parse_environment(); + log_open(); + + parse_proc_cmdline(); + test_files(); + + if (!arg_force) { + + if (arg_skip) + return 0; + + if (access("/dev/.systemd/quotacheck", F_OK) < 0) + return 0; + } + + if ((pid = fork()) < 0) { + log_error("fork(): %m"); + goto finish; + } else if (pid == 0) { + /* Child */ + execv(cmdline[0], (char**) cmdline); + _exit(1); /* Operational error */ + } + + r = wait_for_terminate_and_warn("quotacheck", pid) >= 0 ? EXIT_SUCCESS : EXIT_FAILURE; + +finish: + return r; +} diff --git a/src/umount.c b/src/umount.c index c7f62081d2..ca4dbc2362 100644 --- a/src/umount.c +++ b/src/umount.c @@ -223,7 +223,6 @@ static int loopback_list_get(MountPoint **head) { } first = udev_enumerate_get_list_entry(e); - udev_list_entry_foreach(item, first) { MountPoint *lb; struct udev_device *d; -- cgit v1.2.3-54-g00ecf From ec863ba65a41e58680a3ab15841243088284e808 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 25 Oct 2010 20:35:17 +0200 Subject: ask-password: add basic tty agent --- .gitignore | 3 +- Makefile.am | 10 +- src/.gitignore | 2 +- src/ask-password.c | 113 +++----------- src/conf-parser.c | 25 +++ src/conf-parser.h | 1 + src/reply-password.c | 4 +- src/tty-ask-password-agent.c | 356 +++++++++++++++++++++++++++++++++++++++++++ src/util.c | 164 ++++++++++++++++++++ src/util.h | 2 + 10 files changed, 585 insertions(+), 95 deletions(-) create mode 100644 src/tty-ask-password-agent.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index ad0f5f4e72..2a30fab7d8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-tty-ask-password-agent systemd-fsck systemd-quotacheck systemd-user-sessions @@ -6,7 +7,7 @@ systemd-tmpfiles systemd-readahead-collect systemd-readahead-replay systemd-reply-password -systemd-ask-password-agent +systemd-gnome-ask-password-agent systemd-ask-password systemd-kmsg-syslogd systemd-remount-api-vfs diff --git a/Makefile.am b/Makefile.am index dcaf382fb4..62dcf78e6f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -81,7 +81,8 @@ rootbin_PROGRAMS = \ systemd \ systemctl \ systemd-notify \ - systemd-ask-password + systemd-ask-password \ + systemd-tty-ask-password-agent bin_PROGRAMS = \ systemd-cgls @@ -880,6 +881,13 @@ systemd_gnome_ask_password_agent_LDADD = \ $(DBUSGLIB_LIBS) \ $(GTK_LIBS) +systemd_tty_ask_password_agent_SOURCES = \ + src/tty-ask-password-agent.c \ + src/utmp-wtmp.c + +systemd_tty_ask_password_agent_LDADD = \ + libsystemd-basic.la + pam_systemd_la_SOURCES = \ src/pam-module.c \ src/cgroup-util.c \ diff --git a/src/.gitignore b/src/.gitignore index 389f26daa2..9c01ae1fda 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,3 +1,3 @@ -ask-password-agent.c +gnome-ask-password-agent.c systemd-interfaces.c systemadm.c diff --git a/src/ask-password.c b/src/ask-password.c index 9e4d9e7e68..493bbfef14 100644 --- a/src/ask-password.c +++ b/src/ask-password.c @@ -62,9 +62,9 @@ static int create_socket(char **name) { zero(sa); sa.un.sun_family = AF_UNIX; - snprintf(sa.un.sun_path+1, sizeof(sa.un.sun_path)-1, "/org/freedesktop/systemd1/ask-password/%llu", random_ull()); + snprintf(sa.un.sun_path, sizeof(sa.un.sun_path)-1, "/dev/.systemd/ask-password/sck.%llu", random_ull()); - if (bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) { + if (bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) { r = -errno; log_error("bind() failed: %m"); goto fail; @@ -76,7 +76,7 @@ static int create_socket(char **name) { goto fail; } - if (!(c = strdup(sa.un.sun_path+1))) { + if (!(c = strdup(sa.un.sun_path))) { r = -ENOMEM; log_error("Out of memory"); goto fail; @@ -97,7 +97,7 @@ static int help(void) { "Query the user for a system passphrase, via the TTY or an UI agent.\n\n" " -h --help Show this help\n" " --icon=NAME Icon name\n" - " --timeout=USEC Timeout in usec\n" + " --timeout=SEC Timeout in sec\n" " --no-tty Ask question via agent even on TTY\n", program_invocation_short_name); @@ -176,6 +176,8 @@ static int ask_agent(void) { sigset_t mask; usec_t not_after; + mkdir_p("/dev/.systemd/ask-password", 0755); + if ((fd = mkostemp(temp, O_CLOEXEC|O_CREAT|O_WRONLY)) < 0) { log_error("Failed to create password file: %m"); r = -errno; @@ -211,8 +213,10 @@ static int ask_agent(void) { fprintf(f, "[Ask]\n" + "PID=%lu\n" "Socket=%s\n" "NotAfter=%llu\n", + (unsigned long) getpid(), socket_name, (unsigned long long) not_after); @@ -354,6 +358,11 @@ finish: if (fd >= 0) close_nointr_nofail(fd); + if (socket_name) { + unlink(socket_name); + free(socket_name); + } + if (socket_fd >= 0) close_nointr_nofail(socket_fd); @@ -368,89 +377,6 @@ finish: return r; } -static int ask_tty(void) { - struct termios old_termios, new_termios; - char passphrase[LINE_MAX]; - FILE *ttyf; - - if (!(ttyf = fopen("/dev/tty", "w"))) { - log_error("Failed to open /dev/tty: %m"); - return -errno; - } - - fputs("\x1B[1m", ttyf); - fprintf(ttyf, "%s: ", arg_message); - fputs("\x1B[0m", ttyf); - fflush(ttyf); - - if (tcgetattr(STDIN_FILENO, &old_termios) >= 0) { - - new_termios = old_termios; - - new_termios.c_lflag &= ~(ICANON|ECHO); - new_termios.c_cc[VMIN] = 1; - new_termios.c_cc[VTIME] = 0; - - if (tcsetattr(STDIN_FILENO, TCSADRAIN, &new_termios) >= 0) { - size_t p = 0; - int r = 0; - - for (;;) { - size_t k; - char c; - - k = fread(&c, 1, 1, stdin); - - if (k <= 0) { - r = -EIO; - break; - } - - if (c == '\n') - break; - else if (c == '\b' || c == 127) { - if (p > 0) { - p--; - fputs("\b \b", ttyf); - } - } else { - passphrase[p++] = c; - fputc('*', ttyf); - } - - fflush(ttyf); - } - - fputc('\n', ttyf); - fclose(ttyf); - tcsetattr(STDIN_FILENO, TCSADRAIN, &old_termios); - - if (r < 0) - return -EIO; - - passphrase[p] = 0; - - fputs(passphrase, stdout); - fflush(stdout); - return 0; - } - - } - - fclose(ttyf); - - if (!fgets(passphrase, sizeof(passphrase), stdin)) { - log_error("Failed to read password."); - return -EIO; - } - - truncate_nl(passphrase); - fputs(passphrase, stdout); - fflush(stdout); - - return 0; -} - int main(int argc, char *argv[]) { int r; @@ -460,9 +386,16 @@ int main(int argc, char *argv[]) { if ((r = parse_argv(argc, argv)) <= 0) goto finish; - if (arg_use_tty && isatty(STDIN_FILENO)) - r = ask_tty(); - else + if (arg_use_tty && isatty(STDIN_FILENO)) { + char *password = NULL; + + if ((r = ask_password_tty(arg_message, now(CLOCK_MONOTONIC) + arg_timeout, NULL, &password)) >= 0) { + fputs(password, stdout); + fflush(stdout); + free(password); + } + + } else r = ask_agent(); finish: diff --git a/src/conf-parser.c b/src/conf-parser.c index d18b2a150d..aac64b29a3 100644 --- a/src/conf-parser.c +++ b/src/conf-parser.c @@ -246,6 +246,31 @@ int config_parse_int( return 0; } +int config_parse_uint64( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + uint64_t *u = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if ((r = safe_atou64(rvalue, u)) < 0) { + log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue); + return r; + } + + return 0; +} + int config_parse_unsigned( const char *filename, unsigned line, diff --git a/src/conf-parser.h b/src/conf-parser.h index 9ff65a9bfe..019b7afd13 100644 --- a/src/conf-parser.h +++ b/src/conf-parser.h @@ -46,6 +46,7 @@ int config_parse(const char *filename, FILE *f, const char* const *sections, con /* Generic parsers */ int config_parse_int(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata); int config_parse_unsigned(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata); +int config_parse_uint64(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata); int config_parse_size(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata); int config_parse_bool(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata); int config_parse_string(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata); diff --git a/src/reply-password.c b/src/reply-password.c index 24d73a798e..575a437645 100644 --- a/src/reply-password.c +++ b/src/reply-password.c @@ -49,9 +49,9 @@ static int send_on_socket(int fd, const char *socket_name, const void *packet, s zero(sa); sa.un.sun_family = AF_UNIX; - strncpy(sa.un.sun_path+1, socket_name, sizeof(sa.un.sun_path)-1); + strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path)); - if (sendto(fd, packet, size, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(socket_name)) < 0) { + if (sendto(fd, packet, size, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)) < 0) { log_error("Failed to send: %m"); return -1; } diff --git a/src/tty-ask-password-agent.c b/src/tty-ask-password-agent.c new file mode 100644 index 0000000000..9c4d076b31 --- /dev/null +++ b/src/tty-ask-password-agent.c @@ -0,0 +1,356 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "conf-parser.h" +#include "utmp-wtmp.h" + +static enum { + ACTION_LIST, + ACTION_QUERY, + ACTION_WATCH, + ACTION_WALL +} arg_action = ACTION_QUERY; + +static int parse_password(const char *filename) { + char *socket_name = NULL, *message = NULL, *packet = NULL; + uint64_t not_after = 0; + unsigned pid = 0; + int socket_fd = -1; + + const ConfigItem items[] = { + { "Socket", config_parse_string, &socket_name, "Ask" }, + { "NotAfter", config_parse_uint64, ¬_after, "Ask" }, + { "Message", config_parse_string, &message, "Ask" }, + { "PID", config_parse_unsigned, &pid, "Ask" }, + }; + + FILE *f; + int r; + usec_t n; + + assert(filename); + + if (!(f = fopen(filename, "re"))) { + + if (errno == ENOENT) + return 0; + + log_error("open(%s): %m", filename); + return -errno; + } + + if ((r = config_parse(filename, f, NULL, items, false, NULL)) < 0) { + log_error("Failed to parse password file %s: %s", filename, strerror(-r)); + goto finish; + } + + if (!socket_name || not_after <= 0) { + log_error("Invalid password file %s", filename); + r = -EBADMSG; + goto finish; + } + + n = now(CLOCK_MONOTONIC); + if (n > not_after) { + r = 0; + goto finish; + } + + if (arg_action == ACTION_LIST) + printf("'%s' (PID %u)\n", message, pid); + else if (arg_action == ACTION_WALL) { + char *wall; + + if (asprintf(&wall, + "Password entry required for \'%s\' (PID %u).\r\n" + "Please enter password with the systemd-tty-password-agent tool!", + message, + pid) < 0) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + r = utmp_wall(wall); + free(wall); + } else { + union { + struct sockaddr sa; + struct sockaddr_un un; + } sa; + char *password; + + assert(arg_action == ACTION_QUERY || + arg_action == ACTION_WATCH); + + if (access(socket_name, W_OK) < 0) { + + if (arg_action == ACTION_QUERY) + log_info("Not querying '%s' (PID %u), lacking privileges.", message, pid); + + r = 0; + goto finish; + } + + if ((r = ask_password_tty(message, not_after, filename, &password)) < 0) { + log_error("Failed to query passwords: %s", strerror(-r)); + goto finish; + } + + asprintf(&packet, "+%s", password); + free(password); + + if (!packet) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + if ((socket_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) { + log_error("socket(): %m"); + r = -errno; + goto finish; + } + + zero(sa); + sa.un.sun_family = AF_UNIX; + strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path)); + + if (sendto(socket_fd, packet, strlen(packet), MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)) < 0) { + log_error("Failed to send: %m"); + r = -errno; + goto finish; + } + } + +finish: + fclose(f); + + if (socket_fd >= 0) + close_nointr_nofail(socket_fd); + + free(packet); + free(socket_name); + free(message); + + return r; +} + +static int show_passwords(void) { + DIR *d; + struct dirent *de; + int r = 0; + + if (!(d = opendir("/dev/.systemd/ask-password"))) { + if (errno == ENOENT) + return 0; + + log_error("opendir(): %m"); + return -errno; + } + + while ((de = readdir(d))) { + char *p; + int q; + + if (de->d_type != DT_REG) + continue; + + if (ignore_file(de->d_name)) + continue; + + if (!startswith(de->d_name, "ask.")) + continue; + + if (!(p = strappend("/dev/.systemd/ask-password/", de->d_name))) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + if ((q = parse_password(p)) < 0) + r = q; + + free(p); + } + +finish: + if (d) + closedir(d); + + return r; +} + +static int watch_passwords(void) { + int notify; + struct pollfd pollfd; + int r; + + mkdir_p("/dev/.systemd/ask-password", 0755); + + if ((notify = inotify_init1(IN_CLOEXEC)) < 0) { + r = -errno; + goto finish; + } + + if (inotify_add_watch(notify, "/dev/.systemd/ask-password", IN_CLOSE_WRITE|IN_MOVED_TO) < 0) { + r = -errno; + goto finish; + } + + zero(pollfd); + pollfd.fd = notify; + pollfd.events = POLLIN; + + for (;;) { + if ((r = show_passwords()) < 0) + break; + + if (poll(&pollfd, 1, -1) < 0) { + + if (errno == EINTR) + continue; + + r = -errno; + goto finish; + } + + if (pollfd.revents != 0) + flush_fd(notify); + } + + r = 0; + +finish: + if (notify >= 0) + close_nointr_nofail(notify); + + return r; +} + +static int help(void) { + + printf("%s [OPTIONS...]\n\n" + "Process system password requests.\n\n" + " -h --help Show this help\n" + " --list Show pending password requests\n" + " --query Process pending password requests\n" + " --watch Continously process password requests\n" + " --wall Continously forward password requests to wall\n", + program_invocation_short_name); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_LIST = 0x100, + ARG_QUERY, + ARG_WATCH, + ARG_WALL, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "list", no_argument, NULL, ARG_LIST }, + { "query", no_argument, NULL, ARG_QUERY }, + { "watch", no_argument, NULL, ARG_WATCH }, + { "wall", no_argument, NULL, ARG_WALL }, + { NULL, 0, NULL, 0 } + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_LIST: + arg_action = ACTION_LIST; + break; + + case ARG_QUERY: + arg_action = ACTION_QUERY; + break; + + case ARG_WATCH: + arg_action = ACTION_WATCH; + break; + + case ARG_WALL: + arg_action = ACTION_WALL; + break; + + case '?': + return -EINVAL; + + default: + log_error("Unknown option code %c", c); + return -EINVAL; + } + } + + if (optind != argc) { + help(); + return -EINVAL; + } + + return 1; +} + +int main(int argc, char *argv[]) { + int r; + + log_parse_environment(); + log_open(); + + if ((r = parse_argv(argc, argv)) <= 0) + goto finish; + + if (arg_action == ACTION_WATCH || + arg_action == ACTION_WALL) + r = watch_passwords(); + else + r = show_passwords(); + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/util.c b/src/util.c index cf3cf292a0..98422b236d 100644 --- a/src/util.c +++ b/src/util.c @@ -3350,6 +3350,170 @@ int signal_from_string_try_harder(const char *s) { return signo; } +int ask_password_tty(const char *message, usec_t until, const char *flag_file, char **_passphrase) { + struct termios old_termios, new_termios; + char passphrase[LINE_MAX]; + size_t p = 0; + int r, ttyfd = -1, notify = -1; + struct pollfd pollfd[2]; + bool reset_tty = false; + enum { + POLL_TTY, + POLL_INOTIFY + }; + + assert(message); + assert(_passphrase); + + if (flag_file) { + if ((notify = inotify_init1(IN_CLOEXEC)) < 0) { + r = -errno; + goto finish; + } + + if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0) { + r = -errno; + goto finish; + } + } + + if ((ttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC)) >= 0) { + + if (tcgetattr(ttyfd, &old_termios) < 0) { + r = -errno; + goto finish; + } + + loop_write(ttyfd, "\x1B[1m", 4, false); + loop_write(ttyfd, message, strlen(message), false); + loop_write(ttyfd, ": ", 2, false); + loop_write(ttyfd, "\x1B[0m", 4, false); + + new_termios = old_termios; + new_termios.c_lflag &= ~(ICANON|ECHO); + new_termios.c_cc[VMIN] = 1; + new_termios.c_cc[VTIME] = 0; + + if (tcsetattr(ttyfd, TCSADRAIN, &new_termios) < 0) { + r = -errno; + goto finish; + } + + reset_tty = true; + } + + zero(pollfd); + + pollfd[POLL_TTY].fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO; + pollfd[POLL_TTY].events = POLLIN; + pollfd[POLL_INOTIFY].fd = notify; + pollfd[POLL_INOTIFY].events = POLLIN; + + for (;;) { + char c; + int sleep_for = -1, k; + ssize_t n; + + if (until > 0) { + usec_t y; + + y = now(CLOCK_MONOTONIC); + + if (y > until) { + r = -ETIMEDOUT; + goto finish; + } + + sleep_for = (int) ((until - y) / USEC_PER_MSEC); + } + + if (flag_file) + if (access(flag_file, F_OK) < 0) { + r = -errno; + goto finish; + } + + if ((k = poll(pollfd, notify > 0 ? 2 : 1, sleep_for)) < 0) { + + if (errno == EINTR) + continue; + + r = -errno; + goto finish; + } else if (k == 0) { + r = -ETIMEDOUT; + goto finish; + } + + if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0) + flush_fd(notify); + + if (pollfd[POLL_TTY].revents == 0) + continue; + + if ((n = read(ttyfd >= 0 ? ttyfd : STDIN_FILENO, &c, 1)) < 0) { + + if (errno == EINTR || errno == EAGAIN) + continue; + + r = -errno; + goto finish; + + } else if (n == 0) + break; + + if (c == '\n') + break; + else if (c == 21) { + + while (p > 0) { + p--; + + if (ttyfd >= 0) + loop_write(ttyfd, "\b \b", 3, false); + } + + } else if (c == '\b' || c == 127) { + if (p > 0) { + p--; + + if (ttyfd >= 0) + loop_write(ttyfd, "\b \b", 3, false); + } + } else { + passphrase[p++] = c; + + if (ttyfd >= 0) + loop_write(ttyfd, "*", 1, false); + } + } + + if (ttyfd >= 0) + loop_write(ttyfd, "\n", 1, false); + + passphrase[p] = 0; + + if (!(*_passphrase = strdup(passphrase))) { + r = -ENOMEM; + goto finish; + } + + r = 0; + +finish: + if (notify >= 0) + close_nointr_nofail(notify); + + if (ttyfd >= 0) { + if (reset_tty) + tcsetattr(ttyfd, TCSADRAIN, &old_termios); + + close_nointr_nofail(ttyfd); + } + + return r; +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", diff --git a/src/util.h b/src/util.h index b257ee5603..6a252abbb1 100644 --- a/src/util.h +++ b/src/util.h @@ -364,6 +364,8 @@ bool null_or_empty(struct stat *st); DIR *xopendirat(int dirfd, const char *name); +int ask_password_tty(const char *message, usec_t until, const char *flag_file, char **_passphrase); + #define NULSTR_FOREACH(i, l) \ for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1) -- cgit v1.2.3-54-g00ecf From e23a0ce8badd09aefa961a3a576bfe85f6ebbad7 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 7 Nov 2010 23:02:45 -0500 Subject: cryptsetup: minimal cryptsetup unit generator --- .gitignore | 2 + Makefile.am | 24 ++++- src/cryptsetup-generator.c | 239 +++++++++++++++++++++++++++++++++++++++++++++ src/cryptsetup.c | 53 ++++++++++ src/mount.c | 50 ---------- src/util.c | 51 ++++++++++ src/util.h | 4 +- 7 files changed, 371 insertions(+), 52 deletions(-) create mode 100644 src/cryptsetup-generator.c create mode 100644 src/cryptsetup.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 2a30fab7d8..422378881b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +systemd-cryptsetup +systemd-cryptsetup-generator systemd-tty-ask-password-agent systemd-fsck systemd-quotacheck diff --git a/Makefile.am b/Makefile.am index 456553fa5e..3371c597f4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -57,6 +57,7 @@ AM_CPPFLAGS = \ -DRUNTIME_DIR=\"$(localstatedir)/run\" \ -DRANDOM_SEED=\"$(localstatedir)/lib/random-seed\" \ -DKEXEC_BINARY_PATH=\"/sbin/kexec\" \ + -DSYSTEMD_CRYPTSETUP_PATH=\"$(rootlibexecdir)/systemd-cryptsetup\" \ -I $(top_srcdir)/src if TARGET_GENTOO @@ -112,7 +113,9 @@ rootlibexec_PROGRAMS = \ systemd-tmpfiles \ systemd-user-sessions \ systemd-fsck \ - systemd-quotacheck + systemd-quotacheck \ + systemd-cryptsetup \ + systemd-cryptsetup-generator noinst_PROGRAMS = \ test-engine \ @@ -711,6 +714,25 @@ systemd_quotacheck_CFLAGS = \ systemd_quotacheck_LDADD = \ libsystemd-basic.la +systemd_cryptsetup_SOURCES = \ + src/cryptsetup.c + +systemd_cryptsetup_CFLAGS = \ + $(AM_CFLAGS) + +systemd_cryptsetup_LDADD = \ + libsystemd-basic.la + +systemd_cryptsetup_generator_SOURCES = \ + src/cryptsetup-generator.c \ + src/unit-name.c + +systemd_cryptsetup_generator_CFLAGS = \ + $(AM_CFLAGS) + +systemd_cryptsetup_generator_LDADD = \ + libsystemd-basic.la + systemd_user_sessions_SOURCES = \ src/user-sessions.c \ src/cgroup-util.c diff --git a/src/cryptsetup-generator.c b/src/cryptsetup-generator.c new file mode 100644 index 0000000000..792c1f52d7 --- /dev/null +++ b/src/cryptsetup-generator.c @@ -0,0 +1,239 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "log.h" +#include "util.h" +#include "unit-name.h" + +const char *arg_dest = "/tmp"; + +static bool has_option(const char *haystack, const char *needle) { + const char *f = haystack; + size_t l; + + l = strlen(needle); + + while ((f = strstr(f, needle))) { + + if (f > haystack && f[-1] != ',') { + f++; + continue; + } + + if (f[l] != 0 && f[l] == ',') { + f++; + continue; + } + + return true; + } + + return false; +} + +static int create_disk( + const char *name, + const char *device, + const char *password, + const char *options) { + + char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *from = NULL, *to = NULL; + int r; + FILE *f = NULL; + + assert(name); + assert(device); + + if (!(n = unit_name_build_escape("cryptsetup", name, ".service"))) { + r = -ENOMEM; + log_error("Failed to allocate unit name."); + goto fail; + } + + if (asprintf(&p, "%s/%s", arg_dest, n) < 0) { + r = -ENOMEM; + log_error("Failed to allocate unit file name."); + goto fail; + } + + if (!(u = fstab_node_to_udev_node(device))) { + r = -ENOMEM; + log_error("Failed to allocate device node."); + goto fail; + } + + if (!(d = unit_name_from_path(u, ".device"))) { + r = -ENOMEM; + log_error("Failed to allocate device name."); + goto fail; + } + + if (!(f = fopen(p, "wxe"))) { + r = -errno; + log_error("Failed to create unit file: %m"); + goto fail; + } + + fprintf(f, + "[Unit]\n" + "Description=Cryptography Setup for %%f\n" + "DefaultDependencies=no\n" + "BindTo=%s\n" + "After=systemd-readahead-collect.service systemd-readahead-replay.service %s\n" + "Before=dev-mapper-%%f.device shutdown.target\n", + d, d); + + if (password && (streq(password, "/dev/urandom") || + streq(password, "/dev/random") || + streq(password, "/dev/hw_random"))) + fprintf(f, + "After=systemd-random-seed-load.service\n"); + + fprintf(f, + "\n[Service]\n" + "Type=oneshot\n" + "RemainAfterExit=yes\n" + "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " %s '%s' '%s' '%s' '%s'\n" + "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " remove '%s'\n", + options && has_option(options, "swap") ? "format" : "create", + name, u, strempty(password), strempty(options), + name); + + if (options && has_option(options, "tmp")) + fprintf(f, + "ExecStartPost=/sbin/mke2fs '%s'", + u); + + if (options && has_option(options, "swap")) + fprintf(f, + "ExecStartPost=/sbin/mkswap '%s'", + u); + + fflush(f); + + if (ferror(f)) { + r = -errno; + log_error("Failed to write file: %m"); + goto fail; + } + + if (!options || !has_option(options, "noauto")) { + + if (asprintf(&to, "%s/%s.wants/%s", arg_dest, d, n) < 0) { + r = -ENOMEM; + goto fail; + } + + if (asprintf(&from, "../%s", n) < 0) { + r = -ENOMEM; + goto fail; + } + + mkdir_parents(to, 0755); + + if (symlink(from, to) < 0) { + log_error("Failed to create symlink '%s' to '%s': %m", from, to); + r = -errno; + goto fail; + } + } + + r = 0; + +fail: + free(p); + free(n); + free(d); + + free(from); + free(to); + + if (f) + fclose(f); + + return r; +} + +int main(int argc, char *argv[]) { + FILE *f; + int r = EXIT_SUCCESS; + unsigned n = 0; + + if (argc > 2) { + log_error("This program takes one or no arguments."); + return EXIT_FAILURE; + } + + arg_dest = argv[1]; + + log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_parse_environment(); + log_open(); + + if (!(f = fopen("/etc/crypttab", "re"))) { + + if (errno == ENOENT) + r = EXIT_SUCCESS; + else { + r = EXIT_FAILURE; + log_error("Failed to open /etc/crypttab: %m"); + } + + goto finish; + } + + for (;;) { + char line[LINE_MAX], *l; + char *name = NULL, *device = NULL, *password = NULL, *options = NULL; + int k; + + if (!(fgets(line, sizeof(line), f))) + break; + + n++; + + l = strstrip(line); + if (*l == '#' || *l == 0) + continue; + + if ((k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &password, &options)) < 2 || k > 4) { + log_error("Failed to parse /etc/crypttab:%u, ignoring.", n); + r = EXIT_FAILURE; + goto next; + } + + if (create_disk(name, device, password, options) < 0) + r = EXIT_FAILURE; + + next: + free(name); + free(device); + free(password); + free(options); + } + +finish: + return r; +} diff --git a/src/cryptsetup.c b/src/cryptsetup.c new file mode 100644 index 0000000000..22312477d2 --- /dev/null +++ b/src/cryptsetup.c @@ -0,0 +1,53 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include + +#include "log.h" +#include "util.h" + +int main(int argc, char *argv[]) { + int r = EXIT_SUCCESS; + + if (argc < 3) { + log_error("This program requires at least two arguments."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_parse_environment(); + log_open(); + + if (streq(argv[1], "create")) { + + } else if (streq(argv[1], "format")) { + + + } else if (streq(argv[1], "remove")) { + + } else { + log_error("Unknown verb %s.", argv[1]); + goto finish; + } + +finish: + return r; +} diff --git a/src/mount.c b/src/mount.c index 781c7a8721..5d4944abec 100644 --- a/src/mount.c +++ b/src/mount.c @@ -1297,56 +1297,6 @@ fail: return r; } -static char *fstab_node_to_udev_node(char *p) { - char *dn, *t, *u; - int r; - - /* FIXME: to follow udev's logic 100% we need to leave valid - * UTF8 chars unescaped */ - - if (startswith(p, "LABEL=")) { - - if (!(u = unquote(p+6, "\"\'"))) - return NULL; - - t = xescape(u, "/ "); - free(u); - - if (!t) - return NULL; - - r = asprintf(&dn, "/dev/disk/by-label/%s", t); - free(t); - - if (r < 0) - return NULL; - - return dn; - } - - if (startswith(p, "UUID=")) { - - if (!(u = unquote(p+5, "\"\'"))) - return NULL; - - t = xescape(u, "/ "); - free(u); - - if (!t) - return NULL; - - r = asprintf(&dn, "/dev/disk/by-uuid/%s", ascii_strlower(t)); - free(t); - - if (r < 0) - return NULL; - - return dn; - } - - return strdup(p); -} - static int mount_find_pri(char *options) { char *end, *pri; unsigned long r; diff --git a/src/util.c b/src/util.c index fecdee12e2..6f9399b418 100644 --- a/src/util.c +++ b/src/util.c @@ -3566,6 +3566,57 @@ void dual_timestamp_deserialize(const char *value, dual_timestamp *t) { } } + +char *fstab_node_to_udev_node(const char *p) { + char *dn, *t, *u; + int r; + + /* FIXME: to follow udev's logic 100% we need to leave valid + * UTF8 chars unescaped */ + + if (startswith(p, "LABEL=")) { + + if (!(u = unquote(p+6, "\"\'"))) + return NULL; + + t = xescape(u, "/ "); + free(u); + + if (!t) + return NULL; + + r = asprintf(&dn, "/dev/disk/by-label/%s", t); + free(t); + + if (r < 0) + return NULL; + + return dn; + } + + if (startswith(p, "UUID=")) { + + if (!(u = unquote(p+5, "\"\'"))) + return NULL; + + t = xescape(u, "/ "); + free(u); + + if (!t) + return NULL; + + r = asprintf(&dn, "/dev/disk/by-uuid/%s", ascii_strlower(t)); + free(t); + + if (r < 0) + return NULL; + + return dn; + } + + return strdup(p); +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", diff --git a/src/util.h b/src/util.h index b469009bf7..b7ef51e2ce 100644 --- a/src/util.h +++ b/src/util.h @@ -370,7 +370,9 @@ int ask_password_tty(const char *message, usec_t until, const char *flag_file, c void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t); void dual_timestamp_deserialize(const char *value, dual_timestamp *t); -#define NULSTR_FOREACH(i, l) \ +char *fstab_node_to_udev_node(const char *p); + +#define NULSTR_FOREACH(i, l) \ for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1) #define NULSTR_FOREACH_PAIR(i, j, l) \ -- cgit v1.2.3-54-g00ecf From e9ddabc246ced239cbce436e16792dc4c3d1b52d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 8 Nov 2010 00:31:09 -0500 Subject: manager: parse RD_TIMESTAMP passed from initrd --- .gitignore | 1 + Makefile.am | 12 +++++++++++- src/dbus-manager.c | 3 +++ src/main.c | 30 ++++++++++++++++++++++++++++-- src/manager.c | 36 +++++++++++++++++++++++++----------- src/manager.h | 1 + src/timestamp.c | 39 +++++++++++++++++++++++++++++++++++++++ src/util.c | 19 ++++++++++++++++++- src/util.h | 2 ++ 9 files changed, 128 insertions(+), 15 deletions(-) create mode 100644 src/timestamp.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 422378881b..a99cd17480 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-timestamp systemd-cryptsetup systemd-cryptsetup-generator systemd-tty-ask-password-agent diff --git a/Makefile.am b/Makefile.am index 3371c597f4..52bd13fedb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -115,7 +115,8 @@ rootlibexec_PROGRAMS = \ systemd-fsck \ systemd-quotacheck \ systemd-cryptsetup \ - systemd-cryptsetup-generator + systemd-cryptsetup-generator \ + systemd-timestamp noinst_PROGRAMS = \ test-engine \ @@ -714,6 +715,15 @@ systemd_quotacheck_CFLAGS = \ systemd_quotacheck_LDADD = \ libsystemd-basic.la +systemd_timestamp_SOURCES = \ + src/timestamp.c + +systemd_timestamp_CFLAGS = \ + $(AM_CFLAGS) + +systemd_timestamp_LDADD = \ + libsystemd-basic.la + systemd_cryptsetup_SOURCES = \ src/cryptsetup.c diff --git a/src/dbus-manager.c b/src/dbus-manager.c index c1950319f6..769035f607 100644 --- a/src/dbus-manager.c +++ b/src/dbus-manager.c @@ -150,7 +150,9 @@ #define BUS_MANAGER_INTERFACE_PROPERTIES_GENERAL \ " \n" \ " \n" \ + " \n" \ " \n" \ + " \n" \ " \n" \ " \n" \ " \n" \ @@ -300,6 +302,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, const BusProperty properties[] = { { "org.freedesktop.systemd1.Manager", "Version", bus_property_append_string, "s", PACKAGE_STRING }, { "org.freedesktop.systemd1.Manager", "RunningAs", bus_manager_append_running_as, "s", &m->running_as }, + { "org.freedesktop.systemd1.Manager", "InitRDTimestamp", bus_property_append_uint64, "t", &m->initrd_timestamp.realtime }, { "org.freedesktop.systemd1.Manager", "StartupTimestamp", bus_property_append_uint64, "t", &m->startup_timestamp.realtime }, { "org.freedesktop.systemd1.Manager", "FinishTimestamp", bus_property_append_uint64, "t", &m->finish_timestamp.realtime }, { "org.freedesktop.systemd1.Manager", "LogLevel", bus_manager_append_log_level, "s", NULL }, diff --git a/src/main.c b/src/main.c index f7d76a2941..cd73ee2cff 100644 --- a/src/main.c +++ b/src/main.c @@ -871,12 +871,31 @@ fail: return r; } +static struct dual_timestamp* parse_initrd_timestamp(struct dual_timestamp *t) { + const char *e; + unsigned long long a, b; + + assert(t); + + if (!(e = getenv("RD_TIMESTAMP"))) + return NULL; + + if (sscanf(e, "%llu %llu", &a, &b) != 2) + return NULL; + + t->realtime = (usec_t) a; + t->monotonic = (usec_t) b; + + return t; +} + int main(int argc, char *argv[]) { Manager *m = NULL; int r, retval = EXIT_FAILURE; FDSet *fds = NULL; bool reexecute = false; const char *shutdown_verb = NULL; + dual_timestamp initrd_timestamp = { 0ULL, 0ULL }; if (getpid() != 1 && strstr(program_invocation_short_name, "init")) { /* This is compatbility support for SysV, where @@ -965,9 +984,13 @@ int main(int argc, char *argv[]) { "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", arg_running_as == MANAGER_SYSTEM); - /* Unset some environment variables passed in from the kernel - * that don't really make sense for us. */ if (arg_running_as == MANAGER_SYSTEM) { + /* Parse the data passed to us by the initrd and unset it */ + parse_initrd_timestamp(&initrd_timestamp); + filter_environ("RD_"); + + /* Unset some environment variables passed in from the + * kernel that don't really make sense for us. */ unsetenv("HOME"); unsetenv("TERM"); } @@ -1030,6 +1053,9 @@ int main(int argc, char *argv[]) { m->mount_auto = arg_mount_auto; m->swap_auto = arg_swap_auto; + if (dual_timestamp_is_set(&initrd_timestamp)) + m->initrd_timestamp = initrd_timestamp; + if (arg_console) manager_set_console(m, arg_console); diff --git a/src/manager.c b/src/manager.c index c7de1ea95c..d690a0f16b 100644 --- a/src/manager.c +++ b/src/manager.c @@ -2501,6 +2501,7 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds) { assert(f); assert(fds); + dual_timestamp_serialize(f, "initrd-timestamp", &m->initrd_timestamp); dual_timestamp_serialize(f, "startup-timestamp", &m->startup_timestamp); dual_timestamp_serialize(f, "finish-timestamp", &m->finish_timestamp); @@ -2555,7 +2556,9 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { if (l[0] == 0) break; - if (startswith(l, "startup-timestamp=")) + if (startswith(l, "initrd-timestamp=")) + dual_timestamp_deserialize(l+17, &m->initrd_timestamp); + else if (startswith(l, "startup-timestamp=")) dual_timestamp_deserialize(l+18, &m->startup_timestamp); else if (startswith(l, "finish-timestamp=")) dual_timestamp_deserialize(l+17, &m->finish_timestamp); @@ -2715,7 +2718,7 @@ bool manager_unit_pending_inactive(Manager *m, const char *name) { } void manager_check_finished(Manager *m) { - char userspace[FORMAT_TIMESPAN_MAX], kernel[FORMAT_TIMESPAN_MAX], sum[FORMAT_TIMESPAN_MAX]; + char userspace[FORMAT_TIMESPAN_MAX], initrd[FORMAT_TIMESPAN_MAX], kernel[FORMAT_TIMESPAN_MAX], sum[FORMAT_TIMESPAN_MAX]; assert(m); @@ -2727,15 +2730,26 @@ void manager_check_finished(Manager *m) { dual_timestamp_get(&m->finish_timestamp); - if (m->running_as == MANAGER_SYSTEM) - log_info("Startup finished in %s (kernel) + %s (userspace) = %s.", - format_timespan(kernel, sizeof(kernel), - m->startup_timestamp.monotonic), - format_timespan(userspace, sizeof(userspace), - m->finish_timestamp.monotonic - m->startup_timestamp.monotonic), - format_timespan(sum, sizeof(sum), - m->finish_timestamp.monotonic)); - else + if (m->running_as == MANAGER_SYSTEM) { + if (dual_timestamp_is_set(&m->initrd_timestamp)) { + log_info("Startup finished in %s (kernel) + %s (initrd) + %s (userspace) = %s.", + format_timespan(kernel, sizeof(kernel), + m->initrd_timestamp.monotonic), + format_timespan(initrd, sizeof(initrd), + m->startup_timestamp.monotonic - m->initrd_timestamp.monotonic), + format_timespan(userspace, sizeof(userspace), + m->finish_timestamp.monotonic - m->startup_timestamp.monotonic), + format_timespan(sum, sizeof(sum), + m->finish_timestamp.monotonic)); + } else + log_info("Startup finished in %s (kernel) + %s (userspace) = %s.", + format_timespan(kernel, sizeof(kernel), + m->startup_timestamp.monotonic), + format_timespan(userspace, sizeof(userspace), + m->finish_timestamp.monotonic - m->startup_timestamp.monotonic), + format_timespan(sum, sizeof(sum), + m->finish_timestamp.monotonic)); + } else log_debug("Startup finished in %s.", format_timespan(userspace, sizeof(userspace), m->finish_timestamp.monotonic - m->startup_timestamp.monotonic)); diff --git a/src/manager.h b/src/manager.h index 8a64750584..ab7f26368c 100644 --- a/src/manager.h +++ b/src/manager.h @@ -143,6 +143,7 @@ struct Manager { char **environment; + dual_timestamp initrd_timestamp; dual_timestamp startup_timestamp; dual_timestamp finish_timestamp; diff --git a/src/timestamp.c b/src/timestamp.c new file mode 100644 index 0000000000..ce5142946a --- /dev/null +++ b/src/timestamp.c @@ -0,0 +1,39 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include + +#include "util.h" + +int main(int argc, char *argv[]) { + struct dual_timestamp t; + + /* This is mostly useful for stuff like init ram disk scripts + * which want to take a proper timestamp to do minimal bootup + * profiling. */ + + dual_timestamp_get(&t); + printf("%llu %llu\n", + (unsigned long long) t.realtime, + (unsigned long long) t.monotonic); + + return 0; +} diff --git a/src/util.c b/src/util.c index 6f9399b418..f41861b64e 100644 --- a/src/util.c +++ b/src/util.c @@ -3566,7 +3566,6 @@ void dual_timestamp_deserialize(const char *value, dual_timestamp *t) { } } - char *fstab_node_to_udev_node(const char *p) { char *dn, *t, *u; int r; @@ -3617,6 +3616,24 @@ char *fstab_node_to_udev_node(const char *p) { return strdup(p); } +void filter_environ(const char *prefix) { + int i, j; + assert(prefix); + + if (!environ) + return; + + for (i = 0, j = 0; environ[i]; i++) { + + if (startswith(environ[i], prefix)) + continue; + + environ[j++] = environ[i]; + } + + environ[j] = NULL; +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", diff --git a/src/util.h b/src/util.h index b7ef51e2ce..5567da8544 100644 --- a/src/util.h +++ b/src/util.h @@ -372,6 +372,8 @@ void dual_timestamp_deserialize(const char *value, dual_timestamp *t); char *fstab_node_to_udev_node(const char *p); +void filter_environ(const char *prefix); + #define NULSTR_FOREACH(i, l) \ for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1) -- cgit v1.2.3-54-g00ecf From 06cdd2484c5d0b7792168a7c2d99311e35b0fb8e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 8 Nov 2010 00:43:33 -0500 Subject: ac-power: make ac-power a proper binary that scripts can call --- .gitignore | 1 + Makefile.am | 14 +++++++++++++- src/ac-power.c | 17 +++++++++++++++-- src/ac-power.h | 27 --------------------------- 4 files changed, 29 insertions(+), 30 deletions(-) delete mode 100644 src/ac-power.h (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index a99cd17480..21dd39456e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-ac-power systemd-timestamp systemd-cryptsetup systemd-cryptsetup-generator diff --git a/Makefile.am b/Makefile.am index 52bd13fedb..69faa5581a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -116,7 +116,8 @@ rootlibexec_PROGRAMS = \ systemd-quotacheck \ systemd-cryptsetup \ systemd-cryptsetup-generator \ - systemd-timestamp + systemd-timestamp \ + systemd-ac-power noinst_PROGRAMS = \ test-engine \ @@ -724,6 +725,17 @@ systemd_timestamp_CFLAGS = \ systemd_timestamp_LDADD = \ libsystemd-basic.la +systemd_ac_power_SOURCES = \ + src/ac-power.c + +systemd_ac_power_CFLAGS = \ + $(AM_CFLAGS) \ + $(UDEV_CFLAGS) + +systemd_ac_power_LDADD = \ + libsystemd-basic.la \ + $(UDEV_LIBS) + systemd_cryptsetup_SOURCES = \ src/cryptsetup.c diff --git a/src/ac-power.c b/src/ac-power.c index a30216ffb3..24a68e717e 100644 --- a/src/ac-power.c +++ b/src/ac-power.c @@ -26,9 +26,8 @@ #include #include "util.h" -#include "ac-power.h" -int on_ac_power(void) { +static int on_ac_power(void) { int r; struct udev *udev; @@ -96,3 +95,17 @@ finish: return r; } + +int main(int argc, char *argv[]) { + int r; + + /* This is mostly intended to be used for scripts which want + * to detect whether AC power is plugged in or not. */ + + if ((r = on_ac_power()) < 0) { + log_error("Failed to read AC status: %s", strerror(-r)); + return EXIT_FAILURE; + } + + return r == 0; +} diff --git a/src/ac-power.h b/src/ac-power.h deleted file mode 100644 index 24f28b5e14..0000000000 --- a/src/ac-power.h +++ /dev/null @@ -1,27 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#ifndef fooacpowerhfoo -#define fooacpowerhfoo - -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with systemd; If not, see . -***/ - -int on_ac_power(void); - -#endif -- cgit v1.2.3-54-g00ecf From f90cf44c02ac09469279126e2863a1e71358ee11 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 14 Nov 2010 19:58:33 +0100 Subject: load-fragment: properly do comparison of words --- .gitignore | 1 + Makefile.am | 12 +++++++++++- src/load-fragment.c | 10 +++++----- src/test-strv.c | 41 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 src/test-strv.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 21dd39456e..d5c667ab17 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +test-strv systemd-ac-power systemd-timestamp systemd-cryptsetup diff --git a/Makefile.am b/Makefile.am index 8c92256d7f..e008cc78d8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -137,7 +137,8 @@ noinst_PROGRAMS = \ test-hostname \ test-daemon \ test-cgroup \ - test-env-replace + test-env-replace \ + test-strv if HAVE_PAM pamlib_LTLIBRARIES = \ @@ -619,6 +620,15 @@ test_env_replace_CFLAGS = \ test_env_replace_LDADD = \ libsystemd-basic.la +test_strv_SOURCES = \ + src/test-strv.c + +test_strv_CFLAGS = \ + $(AM_CFLAGS) + +test_strv_LDADD = \ + libsystemd-basic.la + systemd_logger_SOURCES = \ src/logger.c \ src/sd-daemon.c \ diff --git a/src/load-fragment.c b/src/load-fragment.c index 424e6c37bb..9b39d9161a 100644 --- a/src/load-fragment.c +++ b/src/load-fragment.c @@ -433,7 +433,7 @@ static int config_parse_exec( k = 0; FOREACH_WORD_QUOTED(w, l, rvalue, state) { - if (strncmp(w, ";", l) == 0) + if (strncmp(w, ";", MAX(l, 1U)) == 0) break; k++; @@ -444,7 +444,7 @@ static int config_parse_exec( k = 0; FOREACH_WORD_QUOTED(w, l, rvalue, state) { - if (strncmp(w, ";", l) == 0) + if (strncmp(w, ";", MAX(l, 1U)) == 0) break; if (honour_argv0 && w == rvalue) { @@ -1077,11 +1077,11 @@ static int config_parse_mount_flags( assert(data); FOREACH_WORD_QUOTED(w, l, rvalue, state) { - if (strncmp(w, "shared", l) == 0) + if (strncmp(w, "shared", MAX(l, 6U)) == 0) flags |= MS_SHARED; - else if (strncmp(w, "slave", l) == 0) + else if (strncmp(w, "slave", MAX(l, 5U)) == 0) flags |= MS_SLAVE; - else if (strncmp(w, "private", l) == 0) + else if (strncmp(w, "private", MAX(l, 7U)) == 0) flags |= MS_PRIVATE; else { log_error("[%s:%u] Failed to parse mount flags, ignoring: %s", filename, line, rvalue); diff --git a/src/test-strv.c b/src/test-strv.c new file mode 100644 index 0000000000..573436896b --- /dev/null +++ b/src/test-strv.c @@ -0,0 +1,41 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include "util.h" + +int main(int argc, char *argv[]) { + char *w, *state; + size_t l; + const char test[] = "test a b c 'd' e '' '' hhh '' ''"; + + printf("<%s>\n", test); + + FOREACH_WORD_QUOTED(w, l, test, state) { + char *t; + + assert_se(t = strndup(w, l)); + printf("<%s>\n", t); + free(t); + } + + return 0; +} -- cgit v1.2.3-54-g00ecf From 8e1bd70d4ce6d3881c1df6a6482643a2b3a69bb1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 18 Nov 2010 21:52:26 +0100 Subject: sysctl: implement native sysctl tool to support Debian-style /etc/sysctl.d --- .gitignore | 1 + Makefile.am | 19 ++++-- TODO | 2 +- src/sysctl.c | 148 ++++++++++++++++++++++++++++++++++++++++ src/util.c | 3 + units/.gitignore | 1 + units/sysctl.service | 20 ------ units/systemd-sysctl.service.in | 20 ++++++ 8 files changed, 189 insertions(+), 25 deletions(-) create mode 100644 src/sysctl.c delete mode 100644 units/sysctl.service create mode 100644 units/systemd-sysctl.service.in (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index d5c667ab17..1bb184f875 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-sysctl test-strv systemd-ac-power systemd-timestamp diff --git a/Makefile.am b/Makefile.am index a45f333ac6..dd475acf70 100644 --- a/Makefile.am +++ b/Makefile.am @@ -119,7 +119,8 @@ rootlibexec_PROGRAMS = \ systemd-fsck \ systemd-quotacheck \ systemd-timestamp \ - systemd-ac-power + systemd-ac-power \ + systemd-sysctl if HAVE_LIBCRYPTSETUP rootlibexec_PROGRAMS += \ @@ -223,7 +224,6 @@ dist_systemunit_DATA = \ units/var-run.mount \ units/hwclock-load.service \ units/hwclock-save.service \ - units/sysctl.service \ units/remount-rootfs.service \ units/printer.target \ units/bluetooth.target \ @@ -261,6 +261,7 @@ nodist_systemunit_DATA = \ units/systemd-ask-password-wall.service \ units/systemd-ask-password-plymouth.service \ units/systemd-ask-password-console.service \ + units/systemd-sysctl.service \ units/syslog.target \ units/halt.service \ units/poweroff.service \ @@ -306,6 +307,7 @@ EXTRA_DIST = \ units/systemd-ask-password-wall.service.in \ units/systemd-ask-password-plymouth.service.in \ units/systemd-ask-password-console.service.in \ + units/systemd-sysctl.service.in \ units/syslog.target.in \ units/halt.service.in \ units/poweroff.service.in \ @@ -713,6 +715,15 @@ systemd_tmpfiles_CFLAGS = \ systemd_tmpfiles_LDADD = \ libsystemd-basic.la +systemd_sysctl_SOURCES = \ + src/sysctl.c + +systemd_sysctl_CFLAGS = \ + $(AM_CFLAGS) + +systemd_sysctl_LDADD = \ + libsystemd-basic.la + systemd_fsck_SOURCES = \ src/fsck.c \ src/dbus-common.c @@ -1227,7 +1238,7 @@ install-data-hook: systemd-modules-load.service \ systemd-random-seed-load.service \ systemd-tmpfiles-setup.service \ - sysctl.service \ + systemd-sysctl.service \ systemd-ask-password-console.path && \ $(LN_S) ../dev-hugepages.automount dev-hugepages.automount && \ $(LN_S) ../dev-mqueue.automount dev-mqueue.automount && \ @@ -1238,7 +1249,7 @@ install-data-hook: $(LN_S) ../systemd-modules-load.service systemd-modules-load.service && \ $(LN_S) ../systemd-random-seed-load.service systemd-random-seed-load.service && \ $(LN_S) ../systemd-tmpfiles-setup.service systemd-tmpfiles-setup.service && \ - $(LN_S) ../sysctl.service sysctl.service && \ + $(LN_S) ../systemd-sysctl.service systemd-sysctl.service && \ $(LN_S) ../systemd-ask-password-console.path systemd-ask-password-console.path ) ( cd $(DESTDIR)$(systemunitdir)/basic.target.wants && \ rm -f systemd-tmpfiles-clean.timer && \ diff --git a/TODO b/TODO index 17e12de1ba..73d6c36a3a 100644 --- a/TODO +++ b/TODO @@ -75,7 +75,7 @@ * isolate multi-user.target doesn't start a getty@tty1 if we run it from graphical.target -* add to cpu cgroup by default +* read description string from device in crypttsetup External: diff --git a/src/sysctl.c b/src/sysctl.c new file mode 100644 index 0000000000..c57210e28d --- /dev/null +++ b/src/sysctl.c @@ -0,0 +1,148 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "util.h" + +#define PROC_SYS_PREFIX "/proc/sys/" + +static int exit_code = 0; + +static void apply_sysctl(const char *property, const char *value) { + char *p, *n; + int r; + + log_debug("Setting '%s' to '%s'", property, value); + + if (!(p = new(char, sizeof(PROC_SYS_PREFIX) + strlen(property)))) { + log_error("Out of memory"); + exit_code = -ENOMEM; + } + + n = stpcpy(p, PROC_SYS_PREFIX); + strcpy(n, property); + + for (; *n; n++) + if (*n == '.') + *n = '/'; + + if ((r = write_one_line_file(p, value)) < 0) { + log_warning("Failed to write '%s' to '%s': %s", value, p, strerror(-r)); + exit_code = r; + } + + free(p); +} + +static void apply_file(const char *path) { + FILE *f; + + assert(path); + + if (!(f = fopen(path, "re"))) { + log_error("Failed to open file '%s', ignoring: %m", path); + exit_code = -errno; + return; + } + + while (!feof(f)) { + char l[LINE_MAX], *p, *value; + + if (!fgets(l, sizeof(l), f)) { + if (feof(f)) + break; + + log_error("Failed to read file '%s', ignoring: %m", path); + exit_code = -errno; + goto finish; + } + + p = strstrip(l); + + if (!*p) + continue; + + if (strchr(COMMENTS, *p)) + continue; + + if (!(value = strchr(p, '='))) { + log_error("Line is not an assignment in file '%s': %s", path, value); + exit_code = -EINVAL; + continue; + } + + *value = 0; + value++; + + apply_sysctl(strstrip(p), strstrip(value)); + } + +finish: + fclose(f); +} + +static int nftw_cb( + const char *fpath, + const struct stat *sb, + int tflag, + struct FTW *ftwbuf) { + + if (tflag != FTW_F) + return 0; + + if (ignore_file(fpath + ftwbuf->base)) + return 0; + + if (!endswith(fpath, ".conf")) + return 0; + + apply_file(fpath); + return 0; +}; + +int main(int argc, char *argv[]) { + + if (argc > 2) { + log_error("This program expects one or no arguments."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + if (argc > 1) + nftw(argv[1], nftw_cb, 64, FTW_MOUNT|FTW_PHYS); + else { + nftw("/etc/sysctl.conf", nftw_cb, 64, FTW_MOUNT|FTW_PHYS); + nftw("/etc/sysctl.d", nftw_cb, 64, FTW_MOUNT|FTW_PHYS); + } + + return exit_code < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/util.c b/src/util.c index 861f9eaa43..0cd78f6561 100644 --- a/src/util.c +++ b/src/util.c @@ -495,6 +495,9 @@ int write_one_line_file(const char *fn, const char *line) { goto finish; } + if (!endswith(line, "\n")) + fputc('\n', f); + r = 0; finish: fclose(f); diff --git a/units/.gitignore b/units/.gitignore index 419838b677..008446d4bb 100644 --- a/units/.gitignore +++ b/units/.gitignore @@ -1,3 +1,4 @@ +systemd-sysctl.service systemd-ask-password-console.service rescue.service systemd-ask-password-plymouth.service diff --git a/units/sysctl.service b/units/sysctl.service deleted file mode 100644 index ba5626a3a8..0000000000 --- a/units/sysctl.service +++ /dev/null @@ -1,20 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. - -[Unit] -Description=Apply Kernel Variables -DefaultDependencies=no -Conflicts=shutdown.target -After=systemd-readahead-collect.service systemd-readahead-replay.service -Before=basic.target shutdown.target -ConditionPathExists=/etc/sysctl.conf - -[Service] -Type=oneshot -RemainAfterExit=yes -ExecStart=/sbin/sysctl -e -q -p /etc/sysctl.conf -StandardOutput=syslog diff --git a/units/systemd-sysctl.service.in b/units/systemd-sysctl.service.in new file mode 100644 index 0000000000..af3ba88e29 --- /dev/null +++ b/units/systemd-sysctl.service.in @@ -0,0 +1,20 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +[Unit] +Description=Apply Kernel Variables +DefaultDependencies=no +Conflicts=shutdown.target +After=systemd-readahead-collect.service systemd-readahead-replay.service +Before=basic.target shutdown.target +ConditionPathExists=/etc/sysctl.conf +ConditionDirectoryNotEmpty=/etc/sysctl.d + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-sysctl -- cgit v1.2.3-54-g00ecf From 07faed4f99d0c798f92de3032b9c20ca31388494 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 21 Feb 2011 21:48:59 +0100 Subject: virtualization: beef virtualization code --- .gitignore | 1 + Makefile.am | 10 ++++ src/detect-virt.c | 46 ++++++++++++++++++ src/readahead-collect.c | 4 +- src/readahead-replay.c | 4 +- src/util.c | 127 ++++++++++++++++++++++++++++++++++++++---------- src/util.h | 3 +- 7 files changed, 165 insertions(+), 30 deletions(-) create mode 100644 src/detect-virt.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 1bb184f875..adee97f6ce 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-detect-virt systemd-sysctl test-strv systemd-ac-power diff --git a/Makefile.am b/Makefile.am index f508163d5b..7e5d6d8764 100644 --- a/Makefile.am +++ b/Makefile.am @@ -129,6 +129,7 @@ rootlibexec_PROGRAMS = \ systemd-quotacheck \ systemd-timestamp \ systemd-ac-power \ + systemd-detect-virt \ systemd-sysctl if HAVE_LIBCRYPTSETUP @@ -783,6 +784,15 @@ systemd_ac_power_LDADD = \ libsystemd-basic.la \ $(UDEV_LIBS) +systemd_detect_virt_SOURCES = \ + src/detect-virt.c + +systemd_detect_virt_CFLAGS = \ + $(AM_CFLAGS) + +systemd_detect_virt_LDADD = \ + libsystemd-basic.la + systemd_cryptsetup_SOURCES = \ src/cryptsetup.c \ src/ask-password-api.c diff --git a/src/detect-virt.c b/src/detect-virt.c new file mode 100644 index 0000000000..7685d18cc6 --- /dev/null +++ b/src/detect-virt.c @@ -0,0 +1,46 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "util.h" + +int main(int argc, char *argv[]) { + int r; + const char *id; + + /* This is mostly intended to be used for scripts which want + * to detect whether we are being run in a virtualized + * environment or not */ + + if ((r = detect_virtualization(&id)) < 0) { + log_error("Failed to check for virtualization: %s", strerror(-r)); + return EXIT_FAILURE; + } + + if (r > 0) + printf("%s\n", id); + + return r == 0; +} diff --git a/src/readahead-collect.c b/src/readahead-collect.c index ea07b3ff49..330d10776f 100644 --- a/src/readahead-collect.c +++ b/src/readahead-collect.c @@ -652,8 +652,8 @@ int main(int argc, char *argv[]) { return 0; } - if (running_in_vm()) { - log_info("Disabling readahead collector due to execution in virtual machine."); + if (detect_virtualization(NULL) > 0) { + log_info("Disabling readahead collector due to execution in virtualized environment."); return 0; } diff --git a/src/readahead-replay.c b/src/readahead-replay.c index 9447fe0775..cd89654f98 100644 --- a/src/readahead-replay.c +++ b/src/readahead-replay.c @@ -346,8 +346,8 @@ int main(int argc, char*argv[]) { return 0; } - if (running_in_vm()) { - log_info("Disabling readahead replay due to execution in virtual machine."); + if (detect_virtualization(NULL) > 0) { + log_info("Disabling readahead replay due to execution in virtualized environment."); return 0; } diff --git a/src/util.c b/src/util.c index b8e73ef4bf..7e8246bb61 100644 --- a/src/util.c +++ b/src/util.c @@ -3706,7 +3706,8 @@ const char *default_term_for_tty(const char *tty) { return term; } -bool running_in_vm(void) { +/* Returns a short identifier for the various VM implementations */ +int detect_vm(const char **id) { #if defined(__i386__) || defined(__x86_64__) @@ -3718,39 +3719,62 @@ bool running_in_vm(void) { "/sys/class/dmi/id/bios_vendor" }; - uint32_t eax = 0x40000000; + const char dmi_vendor_table[] = + "QEMU\0" "qemu\0" + /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */ + "VMware\0" "vmware\0" + "VMW\0" "vmware\0" + "Microsoft Corporation\0" "microsoft\0" + "innotek GmbH\0" "oracle\0" + "Xen\0" "xen\0" + "\0"; + + const char cpuid_vendor_table[] = + "XenVMMXenVMM\0" "xen\0" + "KVMKVMKVM\0" "kvm\0" + /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */ + "VMwareVMware\0" "vmware\0" + /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */ + "Microsoft Hv\0" "microsoft\0" + "\0"; + + uint32_t eax, ecx; union { uint32_t sig32[3]; char text[13]; } sig; unsigned i; + const char *j, *k; for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) { char *s; - bool b; + int r; + const char *found = NULL; - if (read_one_line_file(dmi_vendors[i], &s) < 0) - continue; + if ((r = read_one_line_file(dmi_vendors[i], &s)) < 0) { + if (r != -ENOENT) + return r; - b = startswith(s, "QEMU") || - /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */ - startswith(s, "VMware") || - startswith(s, "VMW") || - startswith(s, "Microsoft Corporation") || - startswith(s, "innotek GmbH") || - startswith(s, "Xen"); + continue; + } + NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table) + if (startswith(s, j)) + found = k; free(s); - if (b) - return true; + if (found) { + if (id) + *id = found; + + return 1; + } } /* http://lwn.net/Articles/301888/ */ zero(sig); - #if defined (__i386__) #define REG_a "eax" #define REG_b "ebx" @@ -3759,27 +3783,80 @@ bool running_in_vm(void) { #define REG_b "rbx" #endif + /* First detect whether there is a hypervisor */ + eax = 1; __asm__ __volatile__ ( /* ebx/rbx is being used for PIC! */ " push %%"REG_b" \n\t" " cpuid \n\t" - " mov %%ebx, %1 \n\t" " pop %%"REG_b" \n\t" - : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2]) + : "=a" (eax), "=c" (ecx) : "0" (eax) ); - if (streq(sig.text, "XenVMMXenVMM") || - streq(sig.text, "KVMKVMKVM") || - /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */ - streq(sig.text, "VMwareVMware") || - /* http://msdn.microsoft.com/en-us/library/bb969719.aspx */ - streq(sig.text, "Microsoft Hv")) - return true; + if (ecx & 0x80000000U) { + + /* There is a hypervisor, see what it is */ + eax = 0x40000000U; + __asm__ __volatile__ ( + /* ebx/rbx is being used for PIC! */ + " push %%"REG_b" \n\t" + " cpuid \n\t" + " mov %%ebx, %1 \n\t" + " pop %%"REG_b" \n\t" + + : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2]) + : "0" (eax) + ); + + NULSTR_FOREACH_PAIR(j, k, cpuid_vendor_table) + if (streq(sig.text, j)) { + + if (id) + *id = k; + + return 1; + } + + if (id) + *id = "other"; + + return 1; + } #endif - return false; + return 0; +} + +/* Returns a short identifier for the various VM/container implementations */ +int detect_virtualization(const char **id) { + int r; + + /* Unfortunately most of these operations require root access + * in one way or another */ + if (geteuid() != 0) + return -EPERM; + + if ((r = running_in_chroot()) > 0) { + if (id) + *id = "chroot"; + + return r; + } + + /* /proc/vz exists in container and outside of the container, + * /proc/bc only outside of the container. */ + if (access("/proc/vz", F_OK) >= 0 && + access("/proc/bc", F_OK) < 0) { + + if (id) + *id = "openvz"; + + return 1; + } + + return detect_vm(id); } void execute_directory(const char *directory, DIR *d, char *argv[]) { diff --git a/src/util.h b/src/util.h index 80f1a38b57..7f2cc080a7 100644 --- a/src/util.h +++ b/src/util.h @@ -378,7 +378,8 @@ void filter_environ(const char *prefix); bool tty_is_vc(const char *tty); const char *default_term_for_tty(const char *tty); -bool running_in_vm(void); +int detect_vm(const char **id); +int detect_virtualization(const char **id); void execute_directory(const char *directory, DIR *_d, char *argv[]); -- cgit v1.2.3-54-g00ecf From d7ccca2e3f86feb81a48e243d8bad78814659a74 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 4 Mar 2011 21:53:19 +0100 Subject: main: introduce /etc/machine-id This is supposed to play the same roles /var/lib/dbus/machine-id, however fixes a couple of problems: - It is available during early boot since it is stored in /etc - Removes the ID from the D-Bus context and moves it into a system context, thus hopefully lowering hesitation by people to use it. - It is generated at installation time. If the file is empty at boot time it will be mounted over with a randomly generated ID, which is not saved to disk. This is useful to support state-less machines with no transient or writable /etc configuration. --- .gitignore | 1 + Makefile.am | 15 ++++- man/machine-id.xml | 104 +++++++++++++++++++++++++++++++ src/machine-id-main.c | 35 +++++++++++ src/machine-id-setup.c | 166 +++++++++++++++++++++++++++++++++++++++++++++++++ src/machine-id-setup.h | 27 ++++++++ src/main.c | 2 + 7 files changed, 349 insertions(+), 1 deletion(-) create mode 100644 man/machine-id.xml create mode 100644 src/machine-id-main.c create mode 100644 src/machine-id-setup.c create mode 100644 src/machine-id-setup.h (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index adee97f6ce..c5bb5755cc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-machine-id-setup systemd-detect-virt systemd-sysctl test-strv diff --git a/Makefile.am b/Makefile.am index 1841ad5c5a..3d32df2e46 100644 --- a/Makefile.am +++ b/Makefile.am @@ -98,7 +98,8 @@ rootbin_PROGRAMS = \ systemd-notify \ systemd-ask-password \ systemd-tty-ask-password-agent \ - systemd-tmpfiles + systemd-tmpfiles \ + systemd-machine-id-setup bin_PROGRAMS = \ systemd-cgls @@ -434,6 +435,7 @@ libsystemd_core_la_SOURCES = \ src/loopback-setup.c \ src/kmod-setup.c \ src/locale-setup.c \ + src/machine-id-setup.c \ src/specifier.c \ src/unit-name.c \ src/fdset.c \ @@ -523,6 +525,7 @@ MANPAGES = \ man/systemd.conf.5 \ man/tmpfiles.d.5 \ man/hostname.5 \ + man/machine-id.5 \ man/vconsole.conf.5 \ man/locale.conf.5 \ man/os-release.5 \ @@ -737,6 +740,16 @@ systemd_tmpfiles_CFLAGS = \ systemd_tmpfiles_LDADD = \ libsystemd-basic.la +systemd_machine_id_setup_SOURCES = \ + src/machine-id-setup.c \ + src/machine-id-main.c + +systemd_machine_id_setup_CFLAGS = \ + $(AM_CFLAGS) + +systemd_machine_id_setup_LDADD = \ + libsystemd-basic.la + systemd_sysctl_SOURCES = \ src/sysctl.c diff --git a/man/machine-id.xml b/man/machine-id.xml new file mode 100644 index 0000000000..fefeb66fd6 --- /dev/null +++ b/man/machine-id.xml @@ -0,0 +1,104 @@ + + + + + + + + + /etc/machine-id + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + machine-id + 5 + + + + machine-id + local machine ID configuration file + + + + /etc/machine-id + + + + Description + + The /etc/machine-id file + configures the unique machine id of the local system + that is set during installation. It should contain a + single newline-terminated, hexadecimal, lowercase 16 + character machine ID string. + + The machine ID is usually generated from a + random source during system installation and stays + constant for all subsequent boots. Optionally, for + stateless systems it is generated during runtime at + boot. + + The machine ID does not change based on user + configuration, or when hardware is replaced. + + This machine id follows the same format and + logic as the D-Bus machine ID. + + Programs may use this ID to identify the host + with a globally unique ID in the network, that does + not change even if the local network configuration + changes. Due to this and its greater length it is + a more useful replacement than the + gethostid3 + call POSIX specifies. + + + + History + + The simple configuration file format of + /etc/machine-id originates in the + /var/lib/dbus/machine-id file + introduced by D-Bus. In fact this latter file might be a + symlink to the + /etc/machine-id. + + + + See Also + + systemd1, + gethostid3, + hostname5 + + + + diff --git a/src/machine-id-main.c b/src/machine-id-main.c new file mode 100644 index 0000000000..03970a2b06 --- /dev/null +++ b/src/machine-id-main.c @@ -0,0 +1,35 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "machine-id-setup.h" +#include "log.h" + +int main(int argc, char *argv[]) { + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + return machine_id_setup() < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/machine-id-setup.c b/src/machine-id-setup.c new file mode 100644 index 0000000000..940670b339 --- /dev/null +++ b/src/machine-id-setup.c @@ -0,0 +1,166 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "machine-id-setup.h" +#include "macro.h" +#include "util.h" +#include "log.h" + +static int generate(char id[34]) { + int fd; + char buf[16]; + char *p, *q; + ssize_t k; + + assert(id); + + /* First, try reading the D-Bus machine id, unless it is a symlink */ + if ((fd = open("/var/lib/dbus/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW)) >= 0) { + + k = loop_read(fd, id, 33, false); + close_nointr_nofail(fd); + + if (k >= 32) { + id[32] = '\n'; + id[33] = 0; + + log_info("Initializing machine ID from D-Bus machine ID."); + return 0; + } + } + + /* If that didn't work, generate a random machine id */ + if ((fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY)) < 0) { + log_error("Failed to open /dev/urandom: %m"); + return -errno; + } + + k = loop_read(fd, buf, sizeof(buf), false); + close_nointr_nofail(fd); + + if (k != sizeof(buf)) { + log_error("Failed to read /dev/urandom: %s", strerror(k < 0 ? -k : EIO)); + return k < 0 ? (int) k : -EIO; + } + + for (p = buf, q = id; p < buf + sizeof(buf); p++, q += 2) { + q[0] = hexchar(*p >> 4); + q[1] = hexchar(*p & 15); + } + + id[32] = '\n'; + id[33] = 0; + + log_info("Initializing machine ID from random generator."); + + return 0; +} + +int machine_id_setup(void) { + int fd, r; + bool writable; + struct stat st; + char id[34]; /* 32 + \n + \0 */ + mode_t m; + + m = umask(0000); + + if ((fd = open("/etc/machine-id", O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0644)) >= 0) + writable = true; + else { + if ((fd = open("/etc/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY)) < 0) { + umask(m); + log_error("Cannot open /etc/machine-id: %m"); + return -errno; + } + + writable = false; + } + + umask(m); + + if (fstat(fd, &st) < 0) { + log_error("fstat() failed: %m"); + r = -errno; + goto finish; + } + + if (S_ISREG(st.st_mode)) { + if (loop_read(fd, id, 32, false) >= 32) { + r = 0; + goto finish; + } + } + + /* Hmm, so, the id currently stored is not useful, then let's + * generate one */ + + if ((r = generate(id)) < 0) + goto finish; + + if (S_ISREG(st.st_mode) && writable) { + lseek(fd, 0, SEEK_SET); + + if (loop_write(fd, id, 33, false) == 33) { + r = 0; + goto finish; + } + } + + close_nointr_nofail(fd); + fd = -1; + + /* Hmm, we couldn't write it? So let's write it to + * /dev/.systemd/machine-id as a replacement */ + + mkdir_p("/dev/.systemd", 0755); + + if ((r = write_one_line_file("/dev/.systemd/machine-id", id)) < 0) { + log_error("Cannot write /dev/.systemd/machine-id: %s", strerror(-r)); + + unlink("/dev/.systemd/machine-id"); + goto finish; + } + + /* And now, let's mount it over */ + r = mount("/dev/.systemd/machine-id", "/etc/machine-id", "bind", MS_BIND|MS_RDONLY, NULL) < 0 ? -errno : 0; + unlink("/dev/.systemd/machine-id"); + + if (r < 0) + log_error("Failed to mount /etc/machine-id: %s", strerror(-r)); + else + log_info("Installed non-transient /etc/machine-id file."); + +finish: + + if (fd >= 0) + close_nointr_nofail(fd); + + return r; +} diff --git a/src/machine-id-setup.h b/src/machine-id-setup.h new file mode 100644 index 0000000000..4d0a9cf331 --- /dev/null +++ b/src/machine-id-setup.h @@ -0,0 +1,27 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foomachineidsetuphfoo +#define foomachineidsetuphfoo + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +int machine_id_setup(void); + +#endif diff --git a/src/main.c b/src/main.c index ef6e681771..a041a22eda 100644 --- a/src/main.c +++ b/src/main.c @@ -41,6 +41,7 @@ #include "kmod-setup.h" #include "locale-setup.h" #include "selinux-setup.h" +#include "machine-id-setup.h" #include "load-fragment.h" #include "fdset.h" #include "special.h" @@ -1127,6 +1128,7 @@ int main(int argc, char *argv[]) { kmod_setup(); hostname_setup(); + machine_id_setup(); loopback_setup(); mkdir_p("/dev/.systemd/ask-password/", 0755); -- cgit v1.2.3-54-g00ecf From 0992bbd109d60ce10ddd8895259fd9b4c1bbfdb5 Mon Sep 17 00:00:00 2001 From: Andrey Borzenkov Date: Mon, 7 Mar 2011 19:25:15 +0100 Subject: add .vimrc to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index c5bb5755cc..0584d900da 100644 --- a/.gitignore +++ b/.gitignore @@ -80,3 +80,4 @@ ltmain.sh *.tar.gz *.tar.xz libtool +.vimrc -- cgit v1.2.3-54-g00ecf From a8f11321c209830a35edd0357e8def5d4437d854 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 12 Mar 2011 01:03:13 +0100 Subject: systemctl: support remote and privileged systemctl access via SSH and pkexec This adds support for executing systemctl operations remotely or as privileged user while still running systemctl itself unprivileged and locally. This currently requires a D-Bus patch to work properly. https://bugs.freedesktop.org/show_bug.cgi?id=35230 --- .gitignore | 1 + Makefile.am | 10 +- TODO | 2 + man/systemctl.xml | 21 +++ src/bridge.c | 367 ++++++++++++++++++++++++++++++++++++ src/dbus-common.c | 159 ++++++++++++---- src/dbus-common.h | 3 + src/org.freedesktop.systemd1.policy | 11 ++ src/systemctl.c | 68 +++++-- 9 files changed, 589 insertions(+), 53 deletions(-) create mode 100644 src/bridge.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 0584d900da..ffc602a95a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-stdio-bridge systemd-machine-id-setup systemd-detect-virt systemd-sysctl diff --git a/Makefile.am b/Makefile.am index f867624f1a..7120636d9f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -58,6 +58,7 @@ AM_CPPFLAGS = \ -DSYSTEMD_SHUTDOWN_BINARY_PATH=\"$(rootlibexecdir)/systemd-shutdown\" \ -DSYSTEMCTL_BINARY_PATH=\"$(rootbindir)/systemctl\" \ -DSYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH=\"$(rootbindir)/systemd-tty-ask-password-agent\" \ + -DSYSTEMD_STDIO_BRIDGE_BINARY_PATH=\"$(bindir)/systemd-stdio-bridge\" \ -DRUNTIME_DIR=\"$(localstatedir)/run\" \ -DRANDOM_SEED=\"$(localstatedir)/lib/random-seed\" \ -DSYSTEMD_CRYPTSETUP_PATH=\"$(rootlibexecdir)/systemd-cryptsetup\" \ @@ -111,7 +112,8 @@ rootsbin_PROGRAMS = \ systemd-machine-id-setup bin_PROGRAMS = \ - systemd-cgls + systemd-cgls \ + systemd-stdio-bridge if HAVE_GTK bin_PROGRAMS += \ @@ -980,6 +982,12 @@ systemd_cgls_CFLAGS = \ systemd_cgls_LDADD = \ libsystemd-basic.la +systemd_stdio_bridge_SOURCES = \ + src/bridge.c + +systemd_stdio_bridge_LDADD = \ + libsystemd-basic.la + systemadm_SOURCES = \ src/systemadm.vala \ src/systemd-interfaces.vala diff --git a/TODO b/TODO index 64047191e8..a36ca5e0f9 100644 --- a/TODO +++ b/TODO @@ -26,6 +26,8 @@ Features: * optionally create watched directories in .path units +* Support --test based on current system state + * consider services with no [Install] section and stored in /lib enabled by "systemctl is-enabled" * consider services with any kind of link in /etc/systemd/system enabled diff --git a/man/systemctl.xml b/man/systemctl.xml index d37a7d95dc..d6e0a51f2b 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -382,6 +382,27 @@ file that shall be disabled. + + + + + + Execute operation + remotely. Specifiy a hostname, or + username and hostname seperated by @, + to connect to. This will use SSH to + talk to the remote systemd + instance. + + + + + + + Acquire privileges via + PolicyKit before executing the + operation. + The following commands are understood: diff --git a/src/bridge.c b/src/bridge.c new file mode 100644 index 0000000000..5ee058a37f --- /dev/null +++ b/src/bridge.c @@ -0,0 +1,367 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "util.h" +#include "socket-util.h" + +#define BUFFER_SIZE (64*1024) +#define EXTRA_SIZE 16 + +static bool initial_nul = false; +static bool auth_over = false; + +static void format_uid(char *buf, size_t l) { + char text[20 + 1]; /* enough space for a 64bit integer plus NUL */ + unsigned j; + + assert(l > 0); + + snprintf(text, sizeof(text)-1, "%llu", (unsigned long long) geteuid()); + text[sizeof(text)-1] = 0; + + memset(buf, 0, l); + + for (j = 0; text[j] && j*2+2 < l; j++) { + buf[j*2] = hexchar(text[j] >> 4); + buf[j*2+1] = hexchar(text[j] & 0xF); + } + + buf[j*2] = 0; +} + +static size_t patch_in_line(char *line, size_t l, size_t left) { + size_t r; + + if (line[0] == 0 && !initial_nul) { + initial_nul = true; + line += 1; + l -= 1; + r = 1; + } else + r = 0; + + if (l == 5 && strncmp(line, "BEGIN", 5) == 0) { + r += l; + auth_over = true; + + } else if (l == 17 && strncmp(line, "NEGOTIATE_UNIX_FD", 17) == 0) { + memmove(line + 13, line + 17, left); + memcpy(line, "NEGOTIATE_NOP", 13); + r += 13; + + } else if (l >= 14 && strncmp(line, "AUTH EXTERNAL ", 14) == 0) { + char uid[20*2 + 1]; + size_t len; + + format_uid(uid, sizeof(uid)); + len = strlen(uid); + assert(len <= EXTRA_SIZE); + + memmove(line + 14 + len, line + l, left); + memcpy(line + 14, uid, len); + + r += 14 + len; + } else + r += l; + + return r; +} + +static size_t patch_in_buffer(char* in_buffer, size_t *in_buffer_full) { + size_t i, good = 0; + + if (*in_buffer_full <= 0) + return *in_buffer_full; + + /* If authentication is done, we don't touch anything anymore */ + if (auth_over) + return *in_buffer_full; + + if (*in_buffer_full < 2) + return 0; + + for (i = 0; i <= *in_buffer_full - 2; i ++) { + + /* Fully lines can be send on */ + if (in_buffer[i] == '\r' && in_buffer[i+1] == '\n') { + if (i > good) { + size_t old_length, new_length; + + old_length = i - good; + new_length = patch_in_line(in_buffer+good, old_length, *in_buffer_full - i); + *in_buffer_full = *in_buffer_full + new_length - old_length; + + good += new_length + 2; + + } else + good = i+2; + } + + if (auth_over) + break; + } + + return good; +} + +int main(int argc, char *argv[]) { + int r = EXIT_FAILURE, fd = -1, ep = -1; + union sockaddr_union sa; + char in_buffer[BUFFER_SIZE+EXTRA_SIZE], out_buffer[BUFFER_SIZE+EXTRA_SIZE]; + size_t in_buffer_full = 0, out_buffer_full = 0; + struct epoll_event stdin_ev, stdout_ev, fd_ev; + bool stdin_readable = false, stdout_writable = false, fd_readable = false, fd_writable = false; + bool stdin_rhup = false, stdout_whup = false, fd_rhup = false, fd_whup = false; + + if (argc > 1) { + log_error("This program takes no argument."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_parse_environment(); + log_open(); + + if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) { + log_error("Failed to create socket: %s", strerror(errno)); + goto finish; + } + + zero(sa); + sa.un.sun_family = AF_UNIX; + strncpy(sa.un.sun_path, "/var/run/dbus/system_bus_socket", sizeof(sa.un.sun_path)); + + if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) { + log_error("Failed to connect: %m"); + goto finish; + } + + fd_nonblock(STDIN_FILENO, 1); + fd_nonblock(STDOUT_FILENO, 1); + + if ((ep = epoll_create1(EPOLL_CLOEXEC)) < 0) { + log_error("Failed to create epoll: %m"); + goto finish; + } + + zero(stdin_ev); + stdin_ev.events = EPOLLIN|EPOLLET; + stdin_ev.data.fd = STDIN_FILENO; + + zero(stdout_ev); + stdout_ev.events = EPOLLOUT|EPOLLET; + stdout_ev.data.fd = STDOUT_FILENO; + + zero(fd_ev); + fd_ev.events = EPOLLIN|EPOLLOUT|EPOLLET; + fd_ev.data.fd = fd; + + if (epoll_ctl(ep, EPOLL_CTL_ADD, STDIN_FILENO, &stdin_ev) < 0 || + epoll_ctl(ep, EPOLL_CTL_ADD, STDOUT_FILENO, &stdout_ev) < 0 || + epoll_ctl(ep, EPOLL_CTL_ADD, fd, &fd_ev) < 0) { + log_error("Failed to regiser fds in epoll: %m"); + goto finish; + } + + do { + struct epoll_event ev[16]; + ssize_t k; + int i, nfds; + + if ((nfds = epoll_wait(ep, ev, ELEMENTSOF(ev), -1)) < 0) { + + if (errno == EINTR || errno == EAGAIN) + continue; + + log_error("epoll_wait(): %m"); + goto finish; + } + + assert(nfds >= 1); + + for (i = 0; i < nfds; i++) { + if (ev[i].data.fd == STDIN_FILENO) { + + if (!stdin_rhup && (ev[i].events & (EPOLLHUP|EPOLLIN))) + stdin_readable = true; + + } else if (ev[i].data.fd == STDOUT_FILENO) { + + if (ev[i].events & EPOLLHUP) { + stdout_writable = false; + stdout_whup = true; + } + + if (!stdout_whup && (ev[i].events & EPOLLOUT)) + stdout_writable = true; + + } else if (ev[i].data.fd == fd) { + + if (ev[i].events & EPOLLHUP) { + fd_writable = false; + fd_whup = true; + } + + if (!fd_rhup && (ev[i].events & (EPOLLHUP|EPOLLIN))) + fd_readable = true; + + if (!fd_whup && (ev[i].events & EPOLLOUT)) + fd_writable = true; + } + } + + while ((stdin_readable && in_buffer_full <= 0) || + (fd_writable && patch_in_buffer(in_buffer, &in_buffer_full) > 0) || + (fd_readable && out_buffer_full <= 0) || + (stdout_writable && out_buffer_full > 0)) { + + size_t in_buffer_good = 0; + + if (stdin_readable && in_buffer_full < BUFFER_SIZE) { + + if ((k = read(STDIN_FILENO, in_buffer + in_buffer_full, BUFFER_SIZE - in_buffer_full)) < 0) { + + if (errno == EAGAIN) + stdin_readable = false; + else if (errno == EPIPE || errno == ECONNRESET) + k = 0; + else { + log_error("read(): %m"); + goto finish; + } + } else + in_buffer_full += (size_t) k; + + if (k == 0) { + stdin_rhup = true; + stdin_readable = false; + shutdown(STDIN_FILENO, SHUT_RD); + close_nointr_nofail(STDIN_FILENO); + } + } + + in_buffer_good = patch_in_buffer(in_buffer, &in_buffer_full); + + if (fd_writable && in_buffer_good > 0) { + + if ((k = write(fd, in_buffer, in_buffer_good)) < 0) { + + if (errno == EAGAIN) + fd_writable = false; + else if (errno == EPIPE || errno == ECONNRESET) { + fd_whup = true; + fd_writable = false; + shutdown(fd, SHUT_WR); + } else { + log_error("write(): %m"); + goto finish; + } + + } else { + assert(in_buffer_full >= (size_t) k); + memmove(in_buffer, in_buffer + k, in_buffer_full - k); + in_buffer_full -= k; + } + } + + if (fd_readable && out_buffer_full < BUFFER_SIZE) { + + if ((k = read(fd, out_buffer + out_buffer_full, BUFFER_SIZE - out_buffer_full)) < 0) { + + if (errno == EAGAIN) + fd_readable = false; + else if (errno == EPIPE || errno == ECONNRESET) + k = 0; + else { + log_error("read(): %m"); + goto finish; + } + } else + out_buffer_full += (size_t) k; + + if (k == 0) { + fd_rhup = true; + fd_readable = false; + shutdown(fd, SHUT_RD); + } + } + + if (stdout_writable && out_buffer_full > 0) { + + if ((k = write(STDOUT_FILENO, out_buffer, out_buffer_full)) < 0) { + + if (errno == EAGAIN) + stdout_writable = false; + else if (errno == EPIPE || errno == ECONNRESET) { + stdout_whup = true; + stdout_writable = false; + shutdown(STDOUT_FILENO, SHUT_WR); + close_nointr(STDOUT_FILENO); + } else { + log_error("write(): %m"); + goto finish; + } + + } else { + assert(out_buffer_full >= (size_t) k); + memmove(out_buffer, out_buffer + k, out_buffer_full - k); + out_buffer_full -= k; + } + } + } + + if (stdin_rhup && in_buffer_full <= 0 && !fd_whup) { + fd_whup = true; + fd_writable = false; + shutdown(fd, SHUT_WR); + } + + if (fd_rhup && out_buffer_full <= 0 && !stdout_whup) { + stdout_whup = true; + stdout_writable = false; + shutdown(STDOUT_FILENO, SHUT_WR); + close_nointr(STDOUT_FILENO); + } + + } while (!stdout_whup || !fd_whup); + + r = EXIT_SUCCESS; + +finish: + if (fd >= 0) + close_nointr_nofail(fd); + + if (ep >= 0) + close_nointr_nofail(ep); + + return r; +} diff --git a/src/dbus-common.c b/src/dbus-common.c index 809ea0f67a..25b718ec02 100644 --- a/src/dbus-common.c +++ b/src/dbus-common.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include #include "log.h" @@ -55,59 +57,63 @@ int bus_check_peercred(DBusConnection *c) { return 1; } -int bus_connect(DBusBusType t, DBusConnection **_bus, bool *private, DBusError *error) { - DBusConnection *bus; +#define TIMEOUT_USEC (60*USEC_PER_SEC) - assert(_bus); +static int sync_auth(DBusConnection *bus, DBusError *error) { + usec_t begin, tstamp; -#define TIMEOUT_USEC (60*USEC_PER_SEC) + assert(bus); - /* If we are root, then let's not go via the bus */ - if (geteuid() == 0 && t == DBUS_BUS_SYSTEM) { - usec_t begin, tstamp; + /* This complexity should probably move into D-Bus itself: + * + * https://bugs.freedesktop.org/show_bug.cgi?id=35189 */ - if (!(bus = dbus_connection_open_private("unix:abstract=/org/freedesktop/systemd1/private", error))) - return -EIO; + begin = tstamp = now(CLOCK_MONOTONIC); + for (;;) { - if (bus_check_peercred(bus) < 0) { - dbus_connection_close(bus); - dbus_connection_unref(bus); + if (tstamp > begin + TIMEOUT_USEC) + break; - dbus_set_error_const(error, DBUS_ERROR_ACCESS_DENIED, "Failed to verify owner of bus."); - return -EACCES; - } + if (dbus_connection_get_is_authenticated(bus)) + break; - /* This complexity should probably move into D-Bus itself: - * - * https://bugs.freedesktop.org/show_bug.cgi?id=35189 */ - begin = tstamp = now(CLOCK_MONOTONIC); - for (;;) { + if (!dbus_connection_read_write_dispatch(bus, ((begin + TIMEOUT_USEC - tstamp) + USEC_PER_MSEC - 1) / USEC_PER_MSEC)) + break; - if (tstamp > begin + TIMEOUT_USEC) - break; + tstamp = now(CLOCK_MONOTONIC); + } - if (dbus_connection_get_is_authenticated(bus)) - break; + if (!dbus_connection_get_is_connected(bus)) { + dbus_set_error_const(error, DBUS_ERROR_NO_SERVER, "Connection terminated during authentication."); + return -ECONNREFUSED; + } - if (!dbus_connection_read_write_dispatch(bus, ((begin + TIMEOUT_USEC - tstamp) + USEC_PER_MSEC - 1) / USEC_PER_MSEC)) - break; + if (!dbus_connection_get_is_authenticated(bus)) { + dbus_set_error_const(error, DBUS_ERROR_TIMEOUT, "Failed to authenticate in time."); + return -EACCES; + } - tstamp = now(CLOCK_MONOTONIC); - } + return 0; +} - if (!dbus_connection_get_is_connected(bus)) { - dbus_connection_close(bus); - dbus_connection_unref(bus); +int bus_connect(DBusBusType t, DBusConnection **_bus, bool *private, DBusError *error) { + DBusConnection *bus; + int r; - dbus_set_error_const(error, DBUS_ERROR_NO_SERVER, "Connection terminated during authentication."); - return -ECONNREFUSED; - } + assert(_bus); - if (!dbus_connection_get_is_authenticated(bus)) { + /* If we are root, then let's not go via the bus */ + if (geteuid() == 0 && t == DBUS_BUS_SYSTEM) { + if (!(bus = dbus_connection_open_private("unix:abstract=/org/freedesktop/systemd1/private", error))) + return -EIO; + + dbus_connection_set_exit_on_disconnect(bus, FALSE); + + if (bus_check_peercred(bus) < 0) { dbus_connection_close(bus); dbus_connection_unref(bus); - dbus_set_error_const(error, DBUS_ERROR_TIMEOUT, "Failed to authenticate in time."); + dbus_set_error_const(error, DBUS_ERROR_ACCESS_DENIED, "Failed to verify owner of bus."); return -EACCES; } @@ -118,12 +124,93 @@ int bus_connect(DBusBusType t, DBusConnection **_bus, bool *private, DBusError * if (!(bus = dbus_bus_get_private(t, error))) return -EIO; + dbus_connection_set_exit_on_disconnect(bus, FALSE); + if (private) *private = false; } + if ((r = sync_auth(bus, error)) < 0) { + dbus_connection_close(bus); + dbus_connection_unref(bus); + return r; + } + + *_bus = bus; + return 0; +} + +int bus_connect_system_ssh(const char *user, const char *host, DBusConnection **_bus, DBusError *error) { + DBusConnection *bus; + char *p = NULL; + int r; + + assert(_bus); + assert(user || host); + + if (user && host) + asprintf(&p, "exec:path=ssh,argv1=-xT,argv2=%s@%s,argv3=systemd-stdio-bridge", user, host); + else if (user) + asprintf(&p, "exec:path=ssh,argv1=-xT,argv2=%s@localhost,argv3=systemd-stdio-bridge", user); + else if (host) + asprintf(&p, "exec:path=ssh,argv1=-xT,argv2=%s,argv3=systemd-stdio-bridge", host); + + if (!p) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL); + return -ENOMEM; + } + + bus = dbus_connection_open_private(p, error); + free(p); + + if (!bus) + return -EIO; + + dbus_connection_set_exit_on_disconnect(bus, FALSE); + + if ((r = sync_auth(bus, error)) < 0) { + dbus_connection_close(bus); + dbus_connection_unref(bus); + return r; + } + + if (!dbus_bus_register(bus, error)) { + dbus_connection_close(bus); + dbus_connection_unref(bus); + return r; + } + + *_bus = bus; + return 0; +} + +int bus_connect_system_polkit(DBusConnection **_bus, DBusError *error) { + DBusConnection *bus; + int r; + + assert(_bus); + + /* Don't bother with PolicyKit if we are root */ + if (geteuid() == 0) + return bus_connect(DBUS_BUS_SYSTEM, _bus, NULL, error); + + if (!(bus = dbus_connection_open_private("exec:path=pkexec,argv1=" SYSTEMD_STDIO_BRIDGE_BINARY_PATH, error))) + return -EIO; + dbus_connection_set_exit_on_disconnect(bus, FALSE); + if ((r = sync_auth(bus, error)) < 0) { + dbus_connection_close(bus); + dbus_connection_unref(bus); + return r; + } + + if (!dbus_bus_register(bus, error)) { + dbus_connection_close(bus); + dbus_connection_unref(bus); + return r; + } + *_bus = bus; return 0; } diff --git a/src/dbus-common.h b/src/dbus-common.h index 9a66b78744..76333cd4f6 100644 --- a/src/dbus-common.h +++ b/src/dbus-common.h @@ -28,6 +28,9 @@ int bus_check_peercred(DBusConnection *c); int bus_connect(DBusBusType t, DBusConnection **_bus, bool *private_bus, DBusError *error); +int bus_connect_system_ssh(const char *user, const char *host, DBusConnection **_bus, DBusError *error); +int bus_connect_system_polkit(DBusConnection **_bus, DBusError *error); + const char *bus_error_message(const DBusError *error); #endif diff --git a/src/org.freedesktop.systemd1.policy b/src/org.freedesktop.systemd1.policy index bb07b827fc..a9958c2e33 100644 --- a/src/org.freedesktop.systemd1.policy +++ b/src/org.freedesktop.systemd1.policy @@ -27,4 +27,15 @@ /lib/systemd/systemd-reply-password + + Privileged system and service manager access + Authentication is required to access the system and service manager. + + no + no + auth_admin_keep + + /usr/bin/systemd-stdio-bridge + + diff --git a/src/systemctl.c b/src/systemctl.c index 5b205fec56..b8af654e09 100644 --- a/src/systemctl.c +++ b/src/systemctl.c @@ -108,6 +108,12 @@ static enum dot { DOT_ORDER, DOT_REQUIRE } arg_dot = DOT_ALL; +static enum transport { + TRANSPORT_NORMAL, + TRANSPORT_SSH, + TRANSPORT_POLKIT +} arg_transport = TRANSPORT_NORMAL; +static const char *arg_host = NULL; static bool private_bus = false; @@ -2061,12 +2067,14 @@ static void print_status_info(UnitStatusInfo *i) { printf("\t CGroup: %s\n", i->default_control_group); - if ((c = columns()) > 18) - c -= 18; - else - c = 0; + if (arg_transport != TRANSPORT_SSH) { + if ((c = columns()) > 18) + c -= 18; + else + c = 0; - show_cgroup_by_path(i->default_control_group, "\t\t ", c); + show_cgroup_by_path(i->default_control_group, "\t\t ", c); + } } if (i->need_daemon_reload) @@ -4290,22 +4298,25 @@ static int systemctl_help(void) { " pending\n" " --ignore-dependencies\n" " When queueing a new job, ignore all its dependencies\n" + " --kill-mode=MODE How to send signal\n" + " --kill-who=WHO Who to send signal to\n" + " -s --signal=SIGNAL Which signal to send\n" + " -H --host=[user@]host\n" + " Show information for remote host\n" + " -P --privileged Acquire privileges before execution\n" " -q --quiet Suppress output\n" " --no-block Do not wait until operation finished\n" - " --no-pager Do not pipe output into a pager.\n" - " --system Connect to system manager\n" - " --user Connect to user service manager\n" - " --order When generating graph for dot, show only order\n" - " --require When generating graph for dot, show only requirement\n" " --no-wall Don't send wall message before halt/power-off/reboot\n" - " --global Enable/disable unit files globally\n" " --no-reload When enabling/disabling unit files, don't reload daemon\n" " configuration\n" + " --no-pager Do not pipe output into a pager.\n" " --no-ask-password\n" " Do not ask for system passwords\n" - " --kill-mode=MODE How to send signal\n" - " --kill-who=WHO Who to send signal to\n" - " -s --signal=SIGNAL Which signal to send\n" + " --order When generating graph for dot, show only order\n" + " --require When generating graph for dot, show only requirement\n" + " --system Connect to system manager\n" + " --user Connect to user service manager\n" + " --global Enable/disable unit files globally\n" " -f --force When enabling unit files, override existing symlinks\n" " When shutting down, execute action immediately\n" " --defaults When disabling unit files, remove default symlinks only\n\n" @@ -4472,6 +4483,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "kill-who", required_argument, NULL, ARG_KILL_WHO }, { "signal", required_argument, NULL, 's' }, { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, + { "host", required_argument, NULL, 'H' }, + { "privileged",no_argument, NULL, 'P' }, { NULL, 0, NULL, 0 } }; @@ -4483,7 +4496,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { /* Only when running as systemctl we ask for passwords */ arg_ask_password = true; - while ((c = getopt_long(argc, argv, "ht:p:aqfs:", options, NULL)) >= 0) { + while ((c = getopt_long(argc, argv, "ht:p:aqfs:H:P", options, NULL)) >= 0) { switch (c) { @@ -4605,6 +4618,15 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_ask_password = false; break; + case 'P': + arg_transport = TRANSPORT_POLKIT; + break; + + case 'H': + arg_transport = TRANSPORT_SSH; + arg_host = optarg; + break; + case '?': return -EINVAL; @@ -4614,6 +4636,11 @@ static int systemctl_parse_argv(int argc, char *argv[]) { } } + if (arg_transport != TRANSPORT_NORMAL && arg_user) { + log_error("Cannot access user instance remotely."); + return -EINVAL; + } + return 1; } @@ -5622,7 +5649,16 @@ int main(int argc, char*argv[]) { goto finish; } - bus_connect(arg_user ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &bus, &private_bus, &error); + if (arg_transport == TRANSPORT_NORMAL) + bus_connect(arg_user ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &bus, &private_bus, &error); + else if (arg_transport == TRANSPORT_POLKIT) { + bus_connect_system_polkit(&bus, &error); + private_bus = false; + } else if (arg_transport == TRANSPORT_SSH) { + bus_connect_system_ssh(NULL, arg_host, &bus, &error); + private_bus = false; + } else + assert_not_reached("Uh, invalid transport..."); switch (arg_action) { -- cgit v1.2.3-54-g00ecf From 88213476187cafc86bea2276199891873000588d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 14 Mar 2011 02:40:36 +0100 Subject: nspawn: add simple chroot(1) like tool to execute commands in a namespace container --- .gitignore | 1 + Makefile.am | 12 ++ src/nspawn.c | 444 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 457 insertions(+) create mode 100644 src/nspawn.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index ffc602a95a..d679f791e6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-nspawn systemd-stdio-bridge systemd-machine-id-setup systemd-detect-virt diff --git a/Makefile.am b/Makefile.am index 872bcc255c..7d6cfd12d8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -115,6 +115,9 @@ bin_PROGRAMS = \ systemd-cgls \ systemd-stdio-bridge +sbin_PROGRAMS = \ + systemd-nspawn + if HAVE_GTK bin_PROGRAMS += \ systemadm \ @@ -983,6 +986,15 @@ systemd_cgls_CFLAGS = \ systemd_cgls_LDADD = \ libsystemd-basic.la +systemd_nspawn_SOURCES = \ + src/nspawn.c + +systemd_nspawn_CFLAGS = \ + $(AM_CFLAGS) + +systemd_nspawn_LDADD = \ + libsystemd-basic.la + systemd_stdio_bridge_SOURCES = \ src/bridge.c diff --git a/src/nspawn.c b/src/nspawn.c new file mode 100644 index 0000000000..4e4d40ea6f --- /dev/null +++ b/src/nspawn.c @@ -0,0 +1,444 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "util.h" + +static char *arg_directory = NULL; + +static int help(void) { + + printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n" + "Spawn a minimal namespace container for debugging, testing and building.\n\n" + " -h --help Show this help\n" + " -D --directory=NAME Root directory for the container\n", + program_invocation_short_name); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "directory", required_argument, NULL, 'D' }, + { NULL, 0, NULL, 0 } + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "+hD:", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case 'D': + free(arg_directory); + if (!(arg_directory = strdup(optarg))) { + log_error("Failed to duplicate root directory."); + return -ENOMEM; + } + + break; + + case '?': + return -EINVAL; + + default: + log_error("Unknown option code %c", c); + return -EINVAL; + } + } + + return 1; +} + +static int mount_all(const char *dest) { + + typedef struct MountPoint { + const char *what; + const char *where; + const char *type; + const char *options; + unsigned long flags; + } MountPoint; + + static const MountPoint mount_table[] = { + { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV }, + { "/proc/sys", "/proc/sys", "bind", NULL, MS_BIND }, /* Bind mount first */ + { "/proc/sys", "/proc/sys", "bind", NULL, MS_BIND|MS_RDONLY|MS_REMOUNT }, /* Then, make it r/o */ + { "sysfs", "/sys", "sysfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY }, + { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID }, + { "/dev/pts", "/dev/pts", "bind", NULL, MS_BIND }, + { "tmpfs", "/dev/.run", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV }, + }; + + unsigned k; + int r = 0; + + for (k = 0; k < ELEMENTSOF(mount_table); k++) { + char *where; + int t; + + if (asprintf(&where, "%s/%s", dest, mount_table[k].where) < 0) { + log_error("Out of memory"); + + if (r == 0) + r = -ENOMEM; + + break; + } + + if ((t = path_is_mount_point(where)) < 0) { + log_error("Failed to detect whether %s is a mount point: %s", where, strerror(-t)); + free(where); + + if (r == 0) + r = t; + + continue; + } + + mkdir_p(where, 0755); + + if (mount(mount_table[k].what, + where, + mount_table[k].type, + mount_table[k].flags, + mount_table[k].options) < 0) { + + log_error("mount(%s) failed: %m", where); + + if (r == 0) + r = -errno; + } + + free(where); + } + + return r; +} + +static int copy_devnodes(const char *dest) { + + static const char devnodes[] = + "null\0" + "zero\0" + "full\0" + "random\0" + "urandom\0" + "tty\0" + "ptmx\0" + "kmsg\0" + "rtc0\0"; + + const char *d; + int r = 0, k; + char *tty = NULL; + dev_t tty_devnum; + + NULSTR_FOREACH(d, devnodes) { + char *from = NULL, *to = NULL; + struct stat st; + + asprintf(&from, "/dev/%s", d); + asprintf(&to, "%s/dev/%s", dest, d); + + if (!from || !to) { + log_error("Failed to allocate devnode path"); + + free(from); + free(to); + + if (r == 0) + r = -ENOMEM; + + break; + } + + if (stat(from, &st) < 0) { + + if (errno != ENOENT) { + log_error("Failed to stat %s: %m", from); + + if (r == 0) + r = -errno; + } + + } else { + if (mknod(to, st.st_mode, st.st_rdev) < 0) { + log_error("mknod(%s) failed: %m", dest); + + if (r == 0) + r = -errno; + } + } + + free(from); + free(to); + } + + if ((k = get_ctty(&tty, &tty_devnum)) < 0) { + log_error("Failed to determine controlling tty: %s", strerror(-k)); + + if (r == 0) + r = k; + } else { + char *from = NULL, *to = NULL; + + asprintf(&from, "/dev/%s", tty); + asprintf(&to, "%s/dev/console", dest); + + if (!from || !to) { + log_error("Out of memory"); + + if (r == 0) + r = k; + } else { + /* We need to bind mount our own tty on + * /dev/console, since ptys cannot be used + * unless on a devpts file system. But to bind + * mount it we first have to create a device + * node where we can bind mount it on. This is + * kinda ugly since the TTY will very likely + * be owned by a user/group that does not + * exist in the container. */ + + if (mknod(to, S_IFCHR|0600, tty_devnum) < 0) { + log_error("mknod for /dev/console failed: %m"); + + if (r == 0) + r = -errno; + } + + if (mount(from, to, "bind", MS_BIND, NULL) < 0) { + log_error("bind mount for /dev/console failed: %m"); + + if (r == 0) + r = -errno; + } + } + + free(from); + free(to); + } + + free(tty); + + return r; +} + +static int drop_capabilities(void) { + static const unsigned long retain[] = { + CAP_CHOWN, + CAP_DAC_OVERRIDE, + CAP_DAC_READ_SEARCH, + CAP_FOWNER, + CAP_FSETID, + CAP_IPC_OWNER, + CAP_KILL, + CAP_LEASE, + CAP_LINUX_IMMUTABLE, + CAP_NET_BIND_SERVICE, + CAP_NET_BROADCAST, + CAP_NET_RAW, + CAP_SETGID, + CAP_SETFCAP, + CAP_SETPCAP, + CAP_SETUID, + CAP_SYS_ADMIN, + CAP_SYS_CHROOT, + CAP_SYS_NICE, + CAP_SYS_PTRACE, + CAP_SYS_TTY_CONFIG + }; + + unsigned long l; + + for (l = 0; l <= MAX(63LU, (unsigned long) CAP_LAST_CAP); l ++) { + unsigned i; + + for (i = 0; i < ELEMENTSOF(retain); i++) + if (retain[i] == l) + break; + + if (i < ELEMENTSOF(retain)) + continue; + + if (prctl(PR_CAPBSET_DROP, l) < 0) { + + /* If this capability is not known, EINVAL + * will be returned, let's ignore this. */ + if (errno == EINVAL) + continue; + + log_error("PR_CAPBSET_DROP failed: %m"); + return -errno; + } + } + + return 0; +} + +static int is_os_tree(const char *path) { + int r; + char *p; + /* We use /bin/sh as flag file if something is an OS */ + + if (asprintf(&p, "%s/bin/sh", path) < 0) + return -ENOMEM; + + r = access(p, F_OK); + free(p); + + return r < 0 ? 0 : 1; +} + + +int main(int argc, char *argv[]) { + pid_t pid = 0; + int r = EXIT_FAILURE; + + log_parse_environment(); + log_open(); + + if ((r = parse_argv(argc, argv)) <= 0) + goto finish; + + if (arg_directory) { + char *p; + + p = path_make_absolute_cwd(arg_directory); + free(arg_directory); + arg_directory = p; + } else + arg_directory = get_current_dir_name(); + + if (!arg_directory) { + log_error("Failed to determine path"); + goto finish; + } + + path_kill_slashes(arg_directory); + + if (geteuid() != 0) { + log_error("Need to be root."); + goto finish; + } + + if (path_equal(arg_directory, "/")) { + log_error("Spawning constainer on root directory not supported."); + goto finish; + } + + if (is_os_tree(arg_directory) <= 0) { + log_error("Directory %s doesn't look like an OS root directory. Refusing.", arg_directory); + goto finish; + } + + log_info("Spawning namespace container on %s.", arg_directory); + + if ((pid = syscall(__NR_clone, SIGCHLD|CLONE_NEWIPC|CLONE_NEWNS|CLONE_NEWPID|CLONE_NEWUTS|CLONE_NEWNET, NULL)) < 0) { + log_error("clone() failed: %m"); + goto finish; + } + + if (pid == 0) { + const char *hn; + + /* child */ + + if (mount_all(arg_directory) < 0) + goto child_fail; + + if (copy_devnodes(arg_directory) < 0) + goto child_fail; + + if (chdir(arg_directory) < 0) { + log_error("chdir(%s) failed: %m", arg_directory); + goto child_fail; + } + if (mount(arg_directory, "/", "bind", MS_BIND|MS_MOVE, NULL) < 0) { + log_error("mount(MS_MOVE) failed: %m"); + goto child_fail; + } + + if (chroot(".") < 0) { + log_error("chroot() failed: %m"); + goto child_fail; + } + + if (chdir("/") < 0) { + log_error("chdir() failed: %m"); + goto child_fail; + } + + if (drop_capabilities() < 0) + goto child_fail; + + if ((hn = file_name_from_path(arg_directory))) + sethostname(hn, strlen(hn)); + + if (argc > optind) + execvp(argv[optind], argv + optind); + else + execl("/bin/bash", "/bin/bash", NULL); + + log_error("execv() failed: %m"); + + child_fail: + _exit(EXIT_FAILURE); + } + + r = wait_for_terminate_and_warn("container", pid); + + if (r < 0) + r = EXIT_FAILURE; + +finish: + free(arg_directory); + + if (pid > 0) + kill(pid, SIGTERM); + + return r; +} -- cgit v1.2.3-54-g00ecf From 2a796654b9a1f84962e5dafbcf171dcc22742c99 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 15 Mar 2011 00:44:13 +0100 Subject: getty: move automatic serial getty logic into generator --- .gitignore | 1 + Makefile.am | 15 +++++- TODO | 2 - src/cryptsetup-generator.c | 3 +- src/getty-generator.c | 127 +++++++++++++++++++++++++++++++++++++++++++++ src/special.h | 2 - src/target.c | 67 ------------------------ 7 files changed, 144 insertions(+), 73 deletions(-) create mode 100644 src/getty-generator.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index d679f791e6..7213a8ba99 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-getty-generator systemd-nspawn systemd-stdio-bridge systemd-machine-id-setup diff --git a/Makefile.am b/Makefile.am index 6104c809d3..52a8c475e2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -143,11 +143,14 @@ rootlibexec_PROGRAMS = \ systemd-detect-virt \ systemd-sysctl +systemgenerator_PROGRAMS = \ + systemd-getty-generator + if HAVE_LIBCRYPTSETUP rootlibexec_PROGRAMS += \ systemd-cryptsetup -systemgenerator_PROGRAMS = \ +systemgenerator_PROGRAMS += \ systemd-cryptsetup-generator endif @@ -856,6 +859,16 @@ systemd_cryptsetup_generator_CFLAGS = \ systemd_cryptsetup_generator_LDADD = \ libsystemd-basic.la +systemd_getty_generator_SOURCES = \ + src/getty-generator.c \ + src/unit-name.c + +systemd_getty_generator_CFLAGS = \ + $(AM_CFLAGS) + +systemd_getty_generator_LDADD = \ + libsystemd-basic.la + systemd_user_sessions_SOURCES = \ src/user-sessions.c \ src/cgroup-util.c diff --git a/TODO b/TODO index 522ef5929d..d377bc3142 100644 --- a/TODO +++ b/TODO @@ -24,8 +24,6 @@ F15: * capability_bounding_set_drop not used. -* remove getty magic, move it into a generator - * recreate private socket on SIGUSR2 Features: diff --git a/src/cryptsetup-generator.c b/src/cryptsetup-generator.c index 00120f67a0..00f48009ed 100644 --- a/src/cryptsetup-generator.c +++ b/src/cryptsetup-generator.c @@ -222,7 +222,8 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - arg_dest = argv[1]; + if (argc > 1) + arg_dest = argv[1]; log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); log_parse_environment(); diff --git a/src/getty-generator.c b/src/getty-generator.c new file mode 100644 index 0000000000..f49f9eae99 --- /dev/null +++ b/src/getty-generator.c @@ -0,0 +1,127 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "log.h" +#include "util.h" +#include "unit-name.h" + +const char *arg_dest = "/tmp"; + +static int add_symlink(const char *fservice, const char *tservice) { + char *from = NULL, *to = NULL; + int r; + + asprintf(&from, SYSTEM_DATA_UNIT_PATH "/%s", fservice); + asprintf(&to, "%s/getty.target.wants/%s", arg_dest, tservice); + + if (!from || !to) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + mkdir_parents(to, 0755); + + if ((r = symlink(from, to)) < 0) { + log_error("Failed to create symlink from %s to %s: %m", from, to); + r = -errno; + } + +finish: + + free(from); + free(to); + + return r; +} + +int main(int argc, char *argv[]) { + int r = EXIT_SUCCESS; + char *active; + + if (argc > 2) { + log_error("This program takes one or no arguments."); + return EXIT_FAILURE; + } + + if (argc > 1) + arg_dest = argv[1]; + + log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_parse_environment(); + log_open(); + + if (detect_container(NULL) > 0) { + log_debug("Automatic adding console shell."); + + if (add_symlink("console-shell.service", "console-shell.service") < 0) + r = EXIT_FAILURE; + + /* Don't add any further magic if we are in a container */ + goto finish; + } + + if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) { + const char *tty; + + truncate_nl(active); + if ((tty = strrchr(active, ' '))) + tty ++; + else + tty = active; + + /* Automatically add in a serial getty on the kernel + * console */ + if (!tty_is_vc(tty)) { + char *n; + + /* We assume that gettys on virtual terminals are + * started via manual configuration and do this magic + * only for non-VC terminals. */ + + log_debug("Automatically adding serial getty for /dev/%s.", tty); + + if (!(n = unit_name_replace_instance("serial-getty@.service", tty)) || + add_symlink("serial-getty@.service", n) < 0) + r = EXIT_FAILURE; + + free(n); + } + + free(active); + } + + /* Automatically add in a serial getty on the first + * virtualizer console */ + if (access("/sys/class/tty/hvc0", F_OK) == 0) { + log_debug("Automatic adding serial getty for hvc0."); + + if (add_symlink("serial-getty@.service", "serial-getty@hvc0.service") < 0) + r = EXIT_FAILURE; + } + +finish: + return r; +} diff --git a/src/special.h b/src/special.h index df1e1fa417..2f2d9e7b24 100644 --- a/src/special.h +++ b/src/special.h @@ -63,8 +63,6 @@ #define SPECIAL_KEXEC_TARGET "kexec.target" #define SPECIAL_DBUS_SERVICE "dbus.service" #define SPECIAL_DBUS_SOCKET "dbus.socket" -#define SPECIAL_GETTY_TARGET "getty.target" -#define SPECIAL_SERIAL_GETTY_SERVICE "serial-getty@.service" #define SPECIAL_REMOUNT_ROOTFS_SERVICE "remount-rootfs.service" #ifndef SPECIAL_SYSLOG_SERVICE diff --git a/src/target.c b/src/target.c index b8d4a01b64..54c34daa0d 100644 --- a/src/target.c +++ b/src/target.c @@ -83,70 +83,6 @@ static int target_add_default_dependencies(Target *t) { return unit_add_dependency_by_name(UNIT(t), UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true); } -static int target_add_getty_dependencies(Target *t) { - char *n, *active; - int r; - - assert(t); - - if (!unit_has_name(UNIT(t), SPECIAL_GETTY_TARGET)) - return 0; - - if (detect_container(NULL) > 0) - return 1; - - if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) { - const char *tty; - - truncate_nl(active); - if ((tty = strrchr(active, ' '))) - tty ++; - else - tty = active; - - /* Automatically add in a serial getty on the kernel - * console */ - if (!tty_is_vc(tty)) { - - /* We assume that gettys on virtual terminals are - * started via manual configuration and do this magic - * only for non-VC terminals. */ - - log_debug("Automatically adding serial getty for /dev/%s", tty); - if (!(n = unit_name_replace_instance(SPECIAL_SERIAL_GETTY_SERVICE, tty))) { - free(active); - return -ENOMEM; - } - - r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_WANTS, n, NULL, true); - free(n); - - if (r < 0) { - free(active); - return r; - } - } - - free(active); - } - - /* Automatically add in a serial getty on the first - * virtualizer console */ - if (access("/sys/class/tty/hvc0", F_OK) == 0) { - log_debug("Automatic adding serial getty for hvc0"); - if (!(n = unit_name_replace_instance(SPECIAL_SERIAL_GETTY_SERVICE, "hvc0"))) - return -ENOMEM; - - r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_WANTS, n, NULL, true); - free(n); - - if (r < 0) - return r; - } - - return 0; -} - static int target_load(Unit *u) { Target *t = TARGET(u); int r; @@ -161,9 +97,6 @@ static int target_load(Unit *u) { if (u->meta.default_dependencies) if ((r = target_add_default_dependencies(t)) < 0) return r; - - if ((r = target_add_getty_dependencies(t)) < 0) - return r; } return 0; -- cgit v1.2.3-54-g00ecf From 151b190e79e64824552e01849352ca8f6ac7dedb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Apr 2011 03:36:42 +0200 Subject: binfmt: add binfmt tool to set up binfmt_misc at boot --- .gitignore | 1 + Makefile.am | 22 ++++- man/binfmt.d.xml | 103 ++++++++++++++++++++++ src/binfmt.c | 187 ++++++++++++++++++++++++++++++++++++++++ src/util.c | 11 ++- units/.gitignore | 1 + units/systemd-binfmt.service.in | 19 ++++ 7 files changed, 340 insertions(+), 4 deletions(-) create mode 100644 man/binfmt.d.xml create mode 100644 src/binfmt.c create mode 100644 units/systemd-binfmt.service.in (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 7213a8ba99..be44cb60bb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-binfmt systemd-getty-generator systemd-nspawn systemd-stdio-bridge diff --git a/Makefile.am b/Makefile.am index 7bf32aa832..4fd3e6257c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -144,7 +144,8 @@ rootlibexec_PROGRAMS = \ systemd-timestamp \ systemd-ac-power \ systemd-detect-virt \ - systemd-sysctl + systemd-sysctl \ + systemd-binfmt systemgenerator_PROGRAMS = \ systemd-getty-generator @@ -300,6 +301,7 @@ nodist_systemunit_DATA = \ units/systemd-ask-password-wall.service \ units/systemd-ask-password-console.service \ units/systemd-sysctl.service \ + units/systemd-binfmt.service \ units/halt.service \ units/poweroff.service \ units/reboot.service \ @@ -343,6 +345,7 @@ EXTRA_DIST = \ units/systemd-ask-password-wall.service.in \ units/systemd-ask-password-console.service.in \ units/systemd-sysctl.service.in \ + units/systemd-binfmt.service.in \ units/halt.service.in \ units/poweroff.service.in \ units/reboot.service.in \ @@ -569,7 +572,8 @@ MANPAGES = \ man/vconsole.conf.5 \ man/locale.conf.5 \ man/os-release.5 \ - man/modules-load.d.5 + man/modules-load.d.5 \ + man/binfmt.d.5 MANPAGES_ALIAS = \ man/reboot.8 \ @@ -799,6 +803,15 @@ systemd_sysctl_CFLAGS = \ systemd_sysctl_LDADD = \ libsystemd-basic.la +systemd_binfmt_SOURCES = \ + src/binfmt.c + +systemd_binfmt_CFLAGS = \ + $(AM_CFLAGS) + +systemd_binfmt_LDADD = \ + libsystemd-basic.la + systemd_fsck_SOURCES = \ src/fsck.c \ src/dbus-common.c @@ -1240,9 +1253,10 @@ CLEANFILES += \ install-data-hook: $(MKDIR_P) -m 0755 \ - $(DESTDIR)$(sysconfdir)/modules-load.d \ $(DESTDIR)$(tmpfilesdir) \ + $(DESTDIR)$(sysconfdir)/modules-load.d \ $(DESTDIR)$(sysconfdir)/sysctl.d \ + $(DESTDIR)$(sysconfdir)/binfmt.d \ $(DESTDIR)$(systemshutdowndir) \ $(DESTDIR)$(systemgeneratordir) \ $(DESTDIR)$(usergeneratordir) @@ -1364,6 +1378,7 @@ install-data-hook: systemd-random-seed-load.service \ systemd-tmpfiles-setup.service \ systemd-sysctl.service \ + systemd-binfmt.service \ systemd-ask-password-console.path \ systemd-kmsg-syslogd.service \ cryptsetup.target && \ @@ -1377,6 +1392,7 @@ install-data-hook: $(LN_S) ../systemd-random-seed-load.service systemd-random-seed-load.service && \ $(LN_S) ../systemd-tmpfiles-setup.service systemd-tmpfiles-setup.service && \ $(LN_S) ../systemd-sysctl.service systemd-sysctl.service && \ + $(LN_S) ../systemd-binfmt.service systemd-binfmt.service && \ $(LN_S) ../systemd-ask-password-console.path systemd-ask-password-console.path && \ $(LN_S) ../systemd-kmsg-syslogd.service && \ $(LN_S) ../cryptsetup.target cryptsetup.target ) diff --git a/man/binfmt.d.xml b/man/binfmt.d.xml new file mode 100644 index 0000000000..d2a0d7932c --- /dev/null +++ b/man/binfmt.d.xml @@ -0,0 +1,103 @@ + + + + + + + + binfmt.d + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + binfmt.d + 5 + + + + binfmt.d + Configure additional binary formats at boot + + + + /etc/binfmt.d/*.conf + + + + Description + + systemd uses + /etc/binfmt.d/ to configure + additional binary formats to register during boot in + the kernel. Each configuration file is named in the + style of + /etc/binfmt.d/<program>.conf. + + + + + + Configuration Format + + Each file contains a list of binfmt_misc kernel + binary format rules. Consult binfmt_misc.txt + for more information on registration of additional + binary formats and how to write rules. + + Empty lines and lines beginning with ; and # are + ignored. Note that this means you may not use ; and # + as delimiter in binary format rules. + + Configuration files are loaded in alphabetical + order. To ensure that a specific rule takes precedence + over another place it in a file with an alphabetically + later name. + + + + + Example + + /etc/binfmt.d/wine.conf example: + + # Start WINE on Windows executables +:DOSWin:M::MZ::/usr/bin/wine: + + + + + See Also + + systemd1, + wine8 + + + + diff --git a/src/binfmt.c b/src/binfmt.c new file mode 100644 index 0000000000..6ebd212ee1 --- /dev/null +++ b/src/binfmt.c @@ -0,0 +1,187 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "util.h" + +static int delete_rule(const char *rule) { + char *x, *fn, *e; + int r; + + assert(rule[0]); + + if (!(x = strdup(rule))) + return -ENOMEM; + + e = strchrnul(x+1, x[0]); + *e = 0; + + asprintf(&fn, "/proc/sys/fs/binfmt_misc/%s", x+1); + free(x); + + if (!fn) + return -ENOMEM; + + r = write_one_line_file(fn, "-1"); + free(fn); + + return r; +} + +static int apply_rule(const char *rule) { + int r; + + delete_rule(rule); + + if ((r = write_one_line_file("/proc/sys/fs/binfmt_misc/register", rule)) < 0) { + log_error("Failed to add binary format: %s", strerror(-r)); + return r; + } + + return 0; +} + +static int apply_file(const char *path, bool ignore_enoent) { + FILE *f; + int r = 0; + + assert(path); + + if (!(f = fopen(path, "re"))) { + if (ignore_enoent && errno == ENOENT) + return 0; + + log_error("Failed to open file '%s', ignoring: %m", path); + return -errno; + } + + while (!feof(f)) { + char l[LINE_MAX], *p; + int k; + + if (!fgets(l, sizeof(l), f)) { + if (feof(f)) + break; + + log_error("Failed to read file '%s', ignoring: %m", path); + r = -errno; + goto finish; + } + + p = strstrip(l); + + if (!*p) + continue; + + if (strchr(COMMENTS, *p)) + continue; + + if ((k = apply_rule(p)) < 0 && r == 0) + r = k; + } + +finish: + fclose(f); + + return r; +} + +static int scandir_filter(const struct dirent *d) { + assert(d); + + if (ignore_file(d->d_name)) + return 0; + + if (d->d_type != DT_REG && + d->d_type != DT_LNK && + d->d_type != DT_UNKNOWN) + return 0; + + return endswith(d->d_name, ".conf"); +} + +static int apply_tree(const char *path) { + struct dirent **de = NULL; + int n, i, r = 0; + + if ((n = scandir(path, &de, scandir_filter, alphasort)) < 0) { + + if (errno == ENOENT) + return 0; + + log_error("Failed to enumerate %s files: %m", path); + return -errno; + } + + for (i = 0; i < n; i++) { + char *fn; + int k; + + k = asprintf(&fn, "%s/%s", path, de[i]->d_name); + free(de[i]); + + if (k < 0) { + log_error("Failed to allocate file name."); + + if (r == 0) + r = -ENOMEM; + continue; + } + + if ((k = apply_file(fn, true)) < 0 && r == 0) + r = k; + } + + free(de); + + return r; +} + +int main(int argc, char *argv[]) { + int r = 0; + + if (argc > 2) { + log_error("This program expects one or no arguments."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + if (argc > 1) + r = apply_file(argv[1], false); + else { + /* Flush out all rules */ + write_one_line_file("/proc/sys/fs/binfmt_misc/status", "-1"); + + r = apply_tree("/etc/binfmt.d"); + } + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/util.c b/src/util.c index a1686ed389..5daafdf7c2 100644 --- a/src/util.c +++ b/src/util.c @@ -513,7 +513,16 @@ int write_one_line_file(const char *fn, const char *line) { if (!endswith(line, "\n")) fputc('\n', f); - r = 0; + fflush(f); + + if (ferror(f)) { + if (errno != 0) + r = -errno; + else + r = -EIO; + } else + r = 0; + finish: fclose(f); return r; diff --git a/units/.gitignore b/units/.gitignore index 41ab51b2a5..cbf9f255f0 100644 --- a/units/.gitignore +++ b/units/.gitignore @@ -34,3 +34,4 @@ remote-fs.target systemd-update-utmp-runlevel.service systemd-update-utmp-shutdown.service test-env-replace +systemd-binfmt.service diff --git a/units/systemd-binfmt.service.in b/units/systemd-binfmt.service.in new file mode 100644 index 0000000000..0bf6df2011 --- /dev/null +++ b/units/systemd-binfmt.service.in @@ -0,0 +1,19 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +[Unit] +Description=Set Up Additional Binary Formats +DefaultDependencies=no +Conflicts=shutdown.target +After=systemd-readahead-collect.service systemd-readahead-replay.service proc-sys-fs-binfmt_misc.automount +Before=sysinit.target shutdown.target +ConditionDirectoryNotEmpty=/etc/binfmt.d + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-binfmt -- cgit v1.2.3-54-g00ecf From 7640a5de1b3ffe6547200ad204d14e4f067caf4f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 16 Apr 2011 02:02:54 +0200 Subject: hostnamed: introduce systemd-hostnamed http://www.freedesktop.org/wiki/Software/systemd/hostnamed --- .gitignore | 1 + Makefile.am | 35 +- man/hostname.xml | 13 +- man/machine-id.xml | 6 +- man/machine-info.xml | 147 ++++++++ man/os-release.xml | 9 +- src/hostnamed.c | 689 ++++++++++++++++++++++++++++++++++ src/org.freedesktop.hostname1.conf | 27 ++ src/org.freedesktop.hostname1.policy | 49 +++ src/org.freedesktop.hostname1.service | 12 + src/strv.c | 26 ++ src/strv.h | 1 + src/util.c | 81 ++++ src/util.h | 2 + units/.gitignore | 1 + units/systemd-hostnamed.service.in | 16 + 16 files changed, 1099 insertions(+), 16 deletions(-) create mode 100644 man/machine-info.xml create mode 100644 src/hostnamed.c create mode 100644 src/org.freedesktop.hostname1.conf create mode 100644 src/org.freedesktop.hostname1.policy create mode 100644 src/org.freedesktop.hostname1.service create mode 100644 units/systemd-hostnamed.service.in (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index be44cb60bb..5669d740ba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-hostnamed systemd-binfmt systemd-getty-generator systemd-nspawn diff --git a/Makefile.am b/Makefile.am index db28572dc2..26412ab304 100644 --- a/Makefile.am +++ b/Makefile.am @@ -144,7 +144,8 @@ rootlibexec_PROGRAMS = \ systemd-ac-power \ systemd-detect-virt \ systemd-sysctl \ - systemd-binfmt + systemd-binfmt \ + systemd-hostnamed systemgenerator_PROGRAMS = \ systemd-getty-generator @@ -177,10 +178,12 @@ dist_pkgsysconf_DATA = \ src/system.conf dist_dbuspolicy_DATA = \ - src/org.freedesktop.systemd1.conf + src/org.freedesktop.systemd1.conf \ + src/org.freedesktop.hostname1.conf dist_dbussystemservice_DATA = \ - src/org.freedesktop.systemd1.service + src/org.freedesktop.systemd1.service \ + src/org.freedesktop.hostname1.service dist_udevrules_DATA = \ src/99-systemd.rules @@ -282,6 +285,7 @@ nodist_systemunit_DATA = \ units/systemd-initctl.service \ units/systemd-logger.service \ units/systemd-shutdownd.service \ + units/systemd-hostnamed.service \ units/systemd-kmsg-syslogd.service \ units/systemd-modules-load.service \ units/systemd-vconsole-setup.service \ @@ -326,6 +330,7 @@ EXTRA_DIST = \ units/systemd-initctl.service.in \ units/systemd-logger.service.in \ units/systemd-shutdownd.service.in \ + units/systemd-hostnamed.service.in \ units/systemd-kmsg-syslogd.service.in \ units/systemd-modules-load.service.in \ units/systemd-vconsole-setup.service.in \ @@ -406,9 +411,12 @@ dist_doc_DATA = \ pkgconfigdata_DATA = \ systemd.pc -polkitpolicy_DATA = \ +nodist_polkitpolicy_DATA = \ src/org.freedesktop.systemd1.policy +dist_polkitpolicy_DATA = \ + src/org.freedesktop.hostname1.policy + noinst_LTLIBRARIES = \ libsystemd-basic.la \ libsystemd-core.la \ @@ -571,6 +579,7 @@ MANPAGES = \ man/vconsole.conf.5 \ man/locale.conf.5 \ man/os-release.5 \ + man/machine-info.5 \ man/modules-load.d.5 \ man/binfmt.d.5 \ man/sysctl.d.5 \ @@ -754,6 +763,19 @@ systemd_shutdownd_LDADD = \ libsystemd-basic.la \ libsystemd-daemon.la +systemd_hostnamed_SOURCES = \ + src/hostnamed.c \ + src/dbus-common.c + +systemd_hostnamed_CFLAGS = \ + $(AM_CFLAGS) \ + $(DBUS_CFLAGS) + +systemd_hostnamed_LDADD = \ + libsystemd-basic.la \ + libsystemd-daemon.la \ + $(DBUS_LIBS) + systemd_shutdown_SOURCES = \ src/mount-setup.c \ src/umount.c \ @@ -1346,9 +1368,10 @@ install-data-hook: $(LN_S) graphical.target runlevel5.target && \ $(LN_S) reboot.target runlevel6.target ) ( cd $(DESTDIR)$(systemunitdir) && \ - rm -f default.target ctrl-alt-del.target && \ + rm -f default.target ctrl-alt-del.target dbus-org.freedesktop.hostname1.service && \ $(LN_S) graphical.target default.target && \ - $(LN_S) reboot.target ctrl-alt-del.target ) + $(LN_S) reboot.target ctrl-alt-del.target && \ + $(LN_S) systemd-hostnamed.service dbus-org.freedesktop.hostname1.service ) ( cd $(DESTDIR)$(systemunitdir)/multi-user.target.wants && \ rm -f getty.target systemd-user-sessions.service systemd-ask-password-wall.path && \ $(LN_S) ../getty.target getty.target && \ diff --git a/man/hostname.xml b/man/hostname.xml index f226ef05ad..b8b05c8d35 100644 --- a/man/hostname.xml +++ b/man/hostname.xml @@ -44,7 +44,7 @@ hostname - local host name configuration file + Local host name configuration file @@ -62,10 +62,10 @@ newline-terminated host name string. The host name may be a free-form string up to 64 characters in length, however it is recommended that it consists - only of 7bit ASCII characters and no spaces or dots, - and limit itself to the format allowed for DNS domain + only of 7bit ASCII lower-case characters and no spaces or dots, + and limits itself to the format allowed for DNS domain name labels, even though this is not a - requirement. + strict requirement. Depending on the operating system other configuration files might be checked for configuration @@ -85,7 +85,10 @@ systemd1, sethostname2, - hostname1 + hostname1, + hostname5, + machine-id5, + machine-info5 diff --git a/man/machine-id.xml b/man/machine-id.xml index fefeb66fd6..98c09436ba 100644 --- a/man/machine-id.xml +++ b/man/machine-id.xml @@ -76,7 +76,7 @@ with a globally unique ID in the network, that does not change even if the local network configuration changes. Due to this and its greater length it is - a more useful replacement than the + a more useful replacement for the gethostid3 call POSIX specifies. @@ -97,7 +97,9 @@ systemd1, gethostid3, - hostname5 + hostname5, + machine-info5, + os-release5 diff --git a/man/machine-info.xml b/man/machine-info.xml new file mode 100644 index 0000000000..c6d3e92f9a --- /dev/null +++ b/man/machine-info.xml @@ -0,0 +1,147 @@ + + + + + + + + + machine-info + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + machine-info + 5 + + + + machine-info + Local machine information file + + + + /etc/machine-info + + + + Description + + The /etc/machine-info file + contains machine meta data. + + The basic file format of + machine-info is a + newline-separated list of environment-like + shell-compatible variable assignments. It is possible + to source the configuration from shell scripts, + however, beyond mere variable assignments no shell + features are supported, allowing applications to read + the file without implementing a shell compatible + execution engine. + + /etc/machine-info contains + meta data about the machine that is set by the user or + administrator. + + Depending on the operating system other + configuration files might be checked for machine + information as well, however only as fallback. + + + + Options + + The following machine meta data parameters may + be set using + /etc/machine-info: + + + + + PRETTY_HOSTNAME= + + A pretty + human-readable UTF8 machine identifier + string. This should contain a name + like Lennart's + Laptop which is useful to + present to the user and does not + suffer by the syntax limitations of + internet domain names. If possible the + internet host name as configured in + /etc/hostname + should be kept similar to this + one. Example: if this value is + Lennart's Computer + an Internet host name of + lennarts-computer + might be a good choice. If this + parameter is not set an application + should fall back to the Internet host + name for presentation + purposes. + + + + ICON_NAME= + + An icon identifying + this machine according to the XDG + Icon Naming Specification. If + this parameter is not set an + application should fall back to + computer or a + similar icon name. + + + + + + + + Example + + PRETTY_NAME="Lennart's Computer" +ICON_NAME=computer-laptop + + + + See Also + + systemd1, + os-release5, + hostname5, + machine-id5 + + + + diff --git a/man/os-release.xml b/man/os-release.xml index 759c04d320..1e4c067c1b 100644 --- a/man/os-release.xml +++ b/man/os-release.xml @@ -182,10 +182,10 @@ Example NAME=Fedora -VERSION=15 (Rawhide) +VERSION="15 (Rawhide)" ID=fedora VERSION_ID=15 -PRETTY_NAME=Fedora 15 (Rawhide) +PRETTY_NAME="Fedora 15 (Rawhide)" ANSI_COLOR=0;34 @@ -193,7 +193,10 @@ ANSI_COLOR=0;34 See Also systemd1, - lsb_release1 + lsb_release1, + hostname5, + machine-id5, + machine-info5 diff --git a/src/hostnamed.c b/src/hostnamed.c new file mode 100644 index 0000000000..4ba1d4a570 --- /dev/null +++ b/src/hostnamed.c @@ -0,0 +1,689 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include + +#include +#include +#include + +#include "util.h" +#include "strv.h" +#include "dbus-common.h" + +#define INTROSPECTION \ + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ + "\n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + BUS_PROPERTIES_INTERFACE \ + BUS_INTROSPECTABLE_INTERFACE \ + BUS_PEER_INTERFACE \ + "\n" + +#define INTERFACES_LIST \ + BUS_GENERIC_INTERFACES_LIST \ + "org.freedesktop.hostname1\0" + +enum { + PROP_HOSTNAME, + PROP_STATIC_HOSTNAME, + PROP_PRETTY_HOSTNAME, + PROP_ICON_NAME, + _PROP_MAX +}; + +static char *data[_PROP_MAX] = { + NULL, + NULL, + NULL, + NULL +}; + +static void free_data(void) { + int p; + + for (p = 0; p < _PROP_MAX; p++) { + free(data[p]); + data[p] = NULL; + } +} + +static int read_data(void) { + int r; + + free_data(); + + data[PROP_HOSTNAME] = gethostname_malloc(); + if (!data[PROP_HOSTNAME]) + return -ENOMEM; + + r = read_one_line_file("/etc/hostname", &data[PROP_STATIC_HOSTNAME]); + if (r < 0 && r != -ENOENT) + return r; + + r = parse_env_file("/etc/machine-info", NEWLINE, + "PRETTY_HOSTNAME", &data[PROP_PRETTY_HOSTNAME], + "ICON_NAME", &data[PROP_ICON_NAME], + NULL); + if (r < 0 && r != -ENOENT) + return r; + + return 0; +} + +static const char* fallback_icon_name(void) { + +#if defined(__i386__) || defined(__x86_64__) + int r; + char *type; + unsigned t; +#endif + + if (detect_virtualization(NULL) > 0) + return "computer-vm"; + +#if defined(__i386__) || defined(__x86_64__) + r = read_one_line_file("/sys/class/dmi/id/chassis_type", &type); + if (r < 0) + return NULL; + + r = safe_atou(type, &t); + free(type); + + if (r < 0) + return NULL; + + /* We only list the really obvious cases here. The DMI data is + unreliable enough, so let's not do any additional guesswork + on top of that. */ + + switch (t) { + + case 0x3: + case 0x4: + case 0x6: + case 0x7: + return "computer-desktop"; + + case 0x9: + case 0xA: + case 0xE: + return "computer-laptop"; + + case 0x11: + return "computer-server"; + } + +#endif + return NULL; +} + +static int write_data_hostname(void) { + const char *hn; + + if (isempty(data[PROP_HOSTNAME])) + hn = "localhost"; + else + hn = data[PROP_HOSTNAME]; + + if (sethostname(hn, strlen(hn)) < 0) + return -errno; + + return 0; +} + +static int write_data_static_hostname(void) { + + if (isempty(data[PROP_STATIC_HOSTNAME])) { + + if (unlink("/etc/hostname") < 0) + return errno == ENOENT ? 0 : -errno; + + return 0; + } + + return write_one_line_file("/etc/hostname", data[PROP_STATIC_HOSTNAME]); +} + +static int write_data_other(void) { + static const char * const name[_PROP_MAX] = { + [PROP_PRETTY_HOSTNAME] = "PRETTY_HOSTNAME", + [PROP_ICON_NAME] = "ICON_NAME" + }; + + char **l = NULL; + int r, p; + + r = load_env_file("/etc/machine-info", &l); + if (r < 0 && r != -ENOENT) + return r; + + for (p = 2; p < _PROP_MAX; p++) { + char *t, **u; + + assert(name[p]); + + if (isempty(data[p])) { + l = strv_env_unset(l, name[p]); + continue; + } + + if (asprintf(&t, "%s=%s", name[p], strempty(data[p])) < 0) { + strv_free(l); + return -ENOMEM; + } + + u = strv_env_set(l, t); + free(t); + strv_free(l); + + if (!u) + return -ENOMEM; + l = u; + } + + if (strv_isempty(l)) { + + if (unlink("/etc/machine-info") < 0) + return errno == ENOENT ? 0 : -errno; + + return 0; + } + + r = write_env_file("/etc/machine-info", l); + strv_free(l); + + return r; +} + +/* This mimics dbus_bus_get_unix_user() */ +static pid_t get_unix_process_id( + DBusConnection *connection, + const char *name, + DBusError *error) { + + DBusMessage *m = NULL, *reply = NULL; + uint32_t pid = 0; + + m = dbus_message_new_method_call( + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "GetConnectionUnixProcessID"); + if (!m) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL); + goto finish; + } + + if (!dbus_message_append_args( + m, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID)) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL); + goto finish; + } + + reply = dbus_connection_send_with_reply_and_block(connection, m, -1, error); + if (!reply) + goto finish; + + if (dbus_set_error_from_message(error, reply)) + goto finish; + + if (!dbus_message_get_args( + reply, error, + DBUS_TYPE_UINT32, &pid, + DBUS_TYPE_INVALID)) + goto finish; + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + return (pid_t) pid; +} + +static int verify_polkit( + DBusConnection *c, + DBusMessage *request, + const char *action, + bool interactive, + DBusError *error) { + + DBusMessage *m = NULL, *reply = NULL; + const char *unix_process = "unix-process", *pid = "pid", *starttime = "start-time", *cancel_id = ""; + const char *sender; + uint32_t flags = interactive ? 1 : 0; + pid_t pid_raw; + uint32_t pid_u32; + unsigned long long starttime_raw; + uint64_t starttime_u64; + DBusMessageIter iter_msg, iter_struct, iter_array, iter_dict, iter_variant; + int r; + dbus_bool_t authorized = FALSE; + + assert(c); + assert(request); + + sender = dbus_message_get_sender(request); + if (!sender) + return -EINVAL; + + pid_raw = get_unix_process_id(c, sender, error); + if (pid_raw == 0) + return -EINVAL; + + r = get_starttime_of_pid(pid_raw, &starttime_raw); + if (r < 0) + return r; + + m = dbus_message_new_method_call( + "org.freedesktop.PolicyKit1", + "/org/freedesktop/PolicyKit1/Authority", + "org.freedesktop.PolicyKit1.Authority", + "CheckAuthorization"); + if (!m) + return -ENOMEM; + + dbus_message_iter_init_append(m, &iter_msg); + + pid_u32 = (uint32_t) pid_raw; + starttime_u64 = (uint64_t) starttime_raw; + + if (!dbus_message_iter_open_container(&iter_msg, DBUS_TYPE_STRUCT, NULL, &iter_struct) || + !dbus_message_iter_append_basic(&iter_struct, DBUS_TYPE_STRING, &unix_process) || + !dbus_message_iter_open_container(&iter_struct, DBUS_TYPE_ARRAY, "{sv}", &iter_array) || + !dbus_message_iter_open_container(&iter_array, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict) || + !dbus_message_iter_append_basic(&iter_dict, DBUS_TYPE_STRING, &pid) || + !dbus_message_iter_open_container(&iter_dict, DBUS_TYPE_VARIANT, "u", &iter_variant) || + !dbus_message_iter_append_basic(&iter_variant, DBUS_TYPE_UINT32, &pid_u32) || + !dbus_message_iter_close_container(&iter_dict, &iter_variant) || + !dbus_message_iter_close_container(&iter_array, &iter_dict) || + !dbus_message_iter_open_container(&iter_array, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict) || + !dbus_message_iter_append_basic(&iter_dict, DBUS_TYPE_STRING, &starttime) || + !dbus_message_iter_open_container(&iter_dict, DBUS_TYPE_VARIANT, "t", &iter_variant) || + !dbus_message_iter_append_basic(&iter_variant, DBUS_TYPE_UINT64, &starttime_u64) || + !dbus_message_iter_close_container(&iter_dict, &iter_variant) || + !dbus_message_iter_close_container(&iter_array, &iter_dict) || + !dbus_message_iter_close_container(&iter_struct, &iter_array) || + !dbus_message_iter_close_container(&iter_msg, &iter_struct) || + !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_STRING, &action) || + !dbus_message_iter_open_container(&iter_msg, DBUS_TYPE_ARRAY, "{ss}", &iter_array) || + !dbus_message_iter_close_container(&iter_msg, &iter_array) || + !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_UINT32, &flags) || + !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_STRING, &cancel_id)) { + r = -ENOMEM; + goto finish; + } + + reply = dbus_connection_send_with_reply_and_block(c, m, -1, error); + if (!reply) { + r = -EIO; + goto finish; + } + + if (dbus_set_error_from_message(error, reply)) { + r = -EIO; + goto finish; + } + + if (!dbus_message_iter_init(reply, &iter_msg) || + dbus_message_iter_get_arg_type(&iter_msg) != DBUS_TYPE_STRUCT) { + r = -EIO; + goto finish; + } + + dbus_message_iter_recurse(&iter_msg, &iter_struct); + + if (dbus_message_iter_get_arg_type(&iter_struct) != DBUS_TYPE_BOOLEAN) { + r = -EIO; + goto finish; + } + + dbus_message_iter_get_basic(&iter_struct, &authorized); + + r = authorized ? 0 : -EPERM; + +finish: + + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + return r; +} + +static int bus_hostname_append_icon_name(DBusMessageIter *i, const char *property, void *userdata) { + const char *name; + + assert(i); + assert(property); + + if (isempty(data[PROP_ICON_NAME])) + name = fallback_icon_name(); + else + name = data[PROP_ICON_NAME]; + + return bus_property_append_string(i, property, (void*) name); +} + +static DBusHandlerResult hostname_message_handler( + DBusConnection *connection, + DBusMessage *message, + void *userdata) { + + const BusProperty properties[] = { + { "org.freedesktop.hostname1", "Hostname", bus_property_append_string, "s", data[PROP_HOSTNAME]}, + { "org.freedesktop.hostname1", "StaticHostname", bus_property_append_string, "s", data[PROP_STATIC_HOSTNAME]}, + { "org.freedesktop.hostname1", "PrettyHostname", bus_property_append_string, "s", data[PROP_PRETTY_HOSTNAME]}, + { "org.freedesktop.hostname1", "IconName", bus_hostname_append_icon_name, "s", data[PROP_ICON_NAME]}, + { NULL, NULL, NULL, NULL, NULL } + }; + + DBusMessage *reply = NULL, *changed = NULL; + DBusError error; + int r; + + assert(connection); + assert(message); + + dbus_error_init(&error); + + if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetHostname")) { + const char *name; + dbus_bool_t interactive; + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + if (isempty(name)) + name = data[PROP_STATIC_HOSTNAME]; + + if (isempty(name)) + name = "localhost"; + + if (!hostname_is_valid(name)) + return bus_send_error_reply(connection, message, NULL, -EINVAL); + + if (!streq_ptr(name, data[PROP_HOSTNAME])) { + char *h; + + r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-hostname", interactive, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + h = strdup(name); + if (!h) + goto oom; + + free(data[PROP_HOSTNAME]); + data[PROP_HOSTNAME] = h; + + r = write_data_hostname(); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + log_info("Changed host name to '%s'", data[PROP_HOSTNAME]); + + changed = bus_properties_changed_new( + "/org/freedesktop/hostname1", + "org.freedesktop.hostname1", + "Hostname\0"); + if (!changed) + goto oom; + } + + } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetStaticHostname")) { + const char *name; + dbus_bool_t interactive; + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + if (isempty(name)) + name = NULL; + + if (!streq_ptr(name, data[PROP_STATIC_HOSTNAME])) { + + r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-static-hostname", interactive, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + if (isempty(name)) { + free(data[PROP_STATIC_HOSTNAME]); + data[PROP_STATIC_HOSTNAME] = NULL; + } else { + char *h; + + if (!hostname_is_valid(name)) + return bus_send_error_reply(connection, message, NULL, -EINVAL); + + h = strdup(name); + if (!h) + goto oom; + + free(data[PROP_STATIC_HOSTNAME]); + data[PROP_STATIC_HOSTNAME] = h; + } + + r = write_data_static_hostname(); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + log_info("Changed static host name to '%s'", data[PROP_HOSTNAME]); + + changed = bus_properties_changed_new( + "/org/freedesktop/hostname1", + "org.freedesktop.hostname1", + "StaticHostname\0"); + if (!changed) + goto oom; + } + + } else if (dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetPrettyHostname") || + dbus_message_is_method_call(message, "org.freedesktop.hostname1", "SetIconName")) { + + const char *name; + dbus_bool_t interactive; + int k; + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + if (isempty(name)) + name = NULL; + + k = streq(dbus_message_get_member(message), "SetPrettyHostname") ? PROP_PRETTY_HOSTNAME : PROP_ICON_NAME; + + if (!streq_ptr(name, data[k])) { + + r = verify_polkit(connection, message, "org.freedesktop.hostname1.set-machine-info", interactive, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + if (isempty(name)) { + free(data[k]); + data[k] = NULL; + } else { + char *h; + + h = strdup(name); + if (!h) + goto oom; + + free(data[k]); + data[k] = h; + } + + r = write_data_other(); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + log_info("Changed %s to '%s'", k == PROP_PRETTY_HOSTNAME ? "pretty host name" : "icon name", data[k]); + + changed = bus_properties_changed_new( + "/org/freedesktop/hostname1", + "org.freedesktop.hostname1", + k == PROP_PRETTY_HOSTNAME ? "PrettyHostname\0" : "IconName\0"); + if (!changed) + goto oom; + } + + } else + return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties); + + if (!(reply = dbus_message_new_method_return(message))) + goto oom; + + if (!dbus_connection_send(connection, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + reply = NULL; + + if (changed) { + + if (!dbus_connection_send(connection, changed, NULL)) + goto oom; + + dbus_message_unref(changed); + } + + return DBUS_HANDLER_RESULT_HANDLED; + +oom: + if (reply) + dbus_message_unref(reply); + + if (changed) + dbus_message_unref(changed); + + dbus_error_free(&error); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; +} + +int main(int argc, char *argv[]) { + const DBusObjectPathVTable hostname_vtable = { + .message_function = hostname_message_handler + }; + + DBusConnection *bus = NULL; + DBusError error; + int r; + + dbus_error_init(&error); + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + if (argc != 1) { + log_error("This program takes no arguments."); + r = -EINVAL; + goto finish; + } + + umask(0022); + + r = read_data(); + if (r < 0) { + log_error("Failed to read hostname data: %s", strerror(-r)); + goto finish; + } + + bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); + if (!bus) { + log_error("Failed to get system D-Bus connection: %s", error.message); + r = -ECONNREFUSED; + goto finish; + } + + if (!dbus_connection_register_object_path(bus, "/org/freedesktop/hostname1", &hostname_vtable, NULL)) { + log_error("Not enough memory"); + r = -ENOMEM; + goto finish; + } + + if (dbus_bus_request_name(bus, "org.freedesktop.hostname1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) < 0) { + log_error("Failed to register name on bus: %s", error.message); + goto finish; + } + + while (dbus_connection_read_write_dispatch(bus, -1)) + ; + + r = 0; + +finish: + free_data(); + + if (bus) { + dbus_connection_flush(bus); + dbus_connection_close(bus); + dbus_connection_unref(bus); + } + + dbus_error_free(&error); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/org.freedesktop.hostname1.conf b/src/org.freedesktop.hostname1.conf new file mode 100644 index 0000000000..eb241c022f --- /dev/null +++ b/src/org.freedesktop.hostname1.conf @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/org.freedesktop.hostname1.policy b/src/org.freedesktop.hostname1.policy new file mode 100644 index 0000000000..148874fc89 --- /dev/null +++ b/src/org.freedesktop.hostname1.policy @@ -0,0 +1,49 @@ + + + + + + + + The systemd Project + http://www.freedesktop.org/wiki/Software/systemd + + + Set host name + Authentication is required to set the local host name. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + + Set static host name + Authentication is required to set the statically configured local host name. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + + Set machine information + Authentication is required to set local machine information. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + diff --git a/src/org.freedesktop.hostname1.service b/src/org.freedesktop.hostname1.service new file mode 100644 index 0000000000..42e4adb2c9 --- /dev/null +++ b/src/org.freedesktop.hostname1.service @@ -0,0 +1,12 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +[D-BUS Service] +Name=org.freedesktop.hostname1 +Exec=/bin/false +User=root +SystemdService=dbus-org.freedesktop.hostname1.service diff --git a/src/strv.c b/src/strv.c index 1c15ee802c..71b77c9bbf 100644 --- a/src/strv.c +++ b/src/strv.c @@ -525,6 +525,32 @@ char **strv_env_delete(char **x, unsigned n_lists, ...) { return r; } +char **strv_env_unset(char **l, const char *p) { + + char **f, **t; + + if (!l) + return NULL; + + assert(p); + + /* Drops every occurrence of the env var setting p in the + * string list. edits in-place. */ + + for (f = t = l; *f; f++) { + + if (env_match(*f, p)) { + free(*f); + continue; + } + + *(t++) = *f; + } + + *t = NULL; + return l; +} + char **strv_env_set(char **x, const char *p) { char **k, **r; diff --git a/src/strv.h b/src/strv.h index 064576ce1e..46436a52d9 100644 --- a/src/strv.h +++ b/src/strv.h @@ -59,6 +59,7 @@ char **strv_env_merge(unsigned n_lists, ...); char **strv_env_delete(char **x, unsigned n_lists, ...); char **strv_env_set(char **x, const char *p); +char **strv_env_unset(char **l, const char *p); char *strv_env_get_with_length(char **l, const char *name, size_t k); char *strv_env_get(char **x, const char *n); diff --git a/src/util.c b/src/util.c index bec6e0c8e8..c2173c7222 100644 --- a/src/util.c +++ b/src/util.c @@ -496,6 +496,64 @@ int get_parent_of_pid(pid_t pid, pid_t *_ppid) { return 0; } +int get_starttime_of_pid(pid_t pid, unsigned long long *st) { + int r; + FILE *f; + char fn[PATH_MAX], line[LINE_MAX], *p; + + assert(pid > 0); + assert(st); + + assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%lu/stat", (unsigned long) pid) < (int) (sizeof(fn)-1)); + char_array_0(fn); + + if (!(f = fopen(fn, "r"))) + return -errno; + + if (!(fgets(line, sizeof(line), f))) { + r = -errno; + fclose(f); + return r; + } + + fclose(f); + + /* Let's skip the pid and comm fields. The latter is enclosed + * in () but does not escape any () in its value, so let's + * skip over it manually */ + + if (!(p = strrchr(line, ')'))) + return -EIO; + + p++; + + if (sscanf(p, " " + "%*c " /* state */ + "%*d " /* ppid */ + "%*d " /* pgrp */ + "%*d " /* session */ + "%*d " /* tty_nr */ + "%*d " /* tpgid */ + "%*u " /* flags */ + "%*u " /* minflt */ + "%*u " /* cminflt */ + "%*u " /* majflt */ + "%*u " /* cmajflt */ + "%*u " /* utime */ + "%*u " /* stime */ + "%*d " /* cutime */ + "%*d " /* cstime */ + "%*d " /* priority */ + "%*d " /* nice */ + "%*d " /* num_threads */ + "%*d " /* itrealvalue */ + "%llu " /* starttime */, + st) != 1) + return -EIO; + + return 0; +} + int write_one_line_file(const char *fn, const char *line) { FILE *f; int r; @@ -776,6 +834,29 @@ finish: return r; } +int write_env_file(const char *fname, char **l) { + + char **i; + FILE *f; + int r; + + f = fopen(fname, "we"); + if (!f) + return -errno; + + STRV_FOREACH(i, l) { + fputs(*i, f); + fputc('\n', f); + } + + fflush(f); + + r = ferror(f) ? -errno : 0; + fclose(f); + + return r; +} + char *truncate_nl(char *s) { assert(s); diff --git a/src/util.h b/src/util.h index 946179a90d..fcaeac4ab9 100644 --- a/src/util.h +++ b/src/util.h @@ -196,6 +196,7 @@ char *split_quoted(const char *c, size_t *l, char **state); char **split_path_and_make_absolute(const char *p); pid_t get_parent_of_pid(pid_t pid, pid_t *ppid); +int get_starttime_of_pid(pid_t pid, unsigned long long *st); int write_one_line_file(const char *fn, const char *line); int read_one_line_file(const char *fn, char **line); @@ -203,6 +204,7 @@ int read_full_file(const char *fn, char **contents); int parse_env_file(const char *fname, const char *separator, ...) _sentinel_; int load_env_file(const char *fname, char ***l); +int write_env_file(const char *fname, char **l); char *strappend(const char *s, const char *suffix); char *strnappend(const char *s, const char *suffix, size_t length); diff --git a/units/.gitignore b/units/.gitignore index cbf9f255f0..fe23b12266 100644 --- a/units/.gitignore +++ b/units/.gitignore @@ -1,3 +1,4 @@ +systemd-hostnamed.service console-shell.service systemd-sysctl.service systemd-ask-password-console.service diff --git a/units/systemd-hostnamed.service.in b/units/systemd-hostnamed.service.in new file mode 100644 index 0000000000..32a3ab5f9a --- /dev/null +++ b/units/systemd-hostnamed.service.in @@ -0,0 +1,16 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# See systemd.special(7) for details + +[Unit] +Description=Hostname Service + +[Service] +ExecStart=@rootlibexecdir@/systemd-hostnamed +Type=dbus +BusName=org.freedesktop.hostname1 -- cgit v1.2.3-54-g00ecf From 202630822f52e06dce8404633407329c38099278 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 23 May 2011 23:55:06 +0200 Subject: logind: first version that compiles fine --- .gitignore | 1 + Makefile.am | 24 +- src/dbus-loop.c | 263 +++++++++++++++ src/dbus-loop.h | 30 ++ src/logind-device.c | 85 +++++ src/logind-seat.c | 245 ++++++++++++++ src/logind-session.c | 472 +++++++++++++++++++++++++++ src/logind-user.c | 420 ++++++++++++++++++++++++ src/logind.c | 886 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/logind.h | 235 ++++++++++++++ 10 files changed, 2660 insertions(+), 1 deletion(-) create mode 100644 src/dbus-loop.c create mode 100644 src/dbus-loop.h create mode 100644 src/logind-device.c create mode 100644 src/logind-seat.c create mode 100644 src/logind-session.c create mode 100644 src/logind-user.c create mode 100644 src/logind.c create mode 100644 src/logind.h (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 5669d740ba..c2820b03e2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-logind systemd-hostnamed systemd-binfmt systemd-getty-generator diff --git a/Makefile.am b/Makefile.am index 7ca0ae858f..79ccbbe6e6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -158,7 +158,8 @@ rootlibexec_PROGRAMS = \ systemd-ac-power \ systemd-detect-virt \ systemd-sysctl \ - systemd-hostnamed + systemd-hostnamed \ + systemd-logind if ENABLE_BINFMT rootlibexec_PROGRAMS += \ @@ -806,6 +807,27 @@ systemd_hostnamed_LDADD = \ libsystemd-daemon.la \ $(DBUS_LIBS) +systemd_logind_SOURCES = \ + src/logind.c \ + src/logind-device.c \ + src/logind-seat.c \ + src/logind-session.c \ + src/logind-user.c \ + src/dbus-common.c \ + src/dbus-loop.c \ + src/cgroup-util.c + +systemd_logind_CFLAGS = \ + $(AM_CFLAGS) \ + $(DBUS_CFLAGS) \ + $(UDEV_CFLAGS) + +systemd_logind_LDADD = \ + libsystemd-basic.la \ + libsystemd-daemon.la \ + $(DBUS_LIBS) \ + $(UDEV_LIBS) + systemd_shutdown_SOURCES = \ src/mount-setup.c \ src/umount.c \ diff --git a/src/dbus-loop.c b/src/dbus-loop.c new file mode 100644 index 0000000000..8eb1d171a7 --- /dev/null +++ b/src/dbus-loop.c @@ -0,0 +1,263 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "dbus-loop.h" +#include "dbus-common.h" +#include "util.h" + +/* Minimal implementation of the dbus loop which integrates all dbus + * events into a single epoll fd which we can triviall integrate with + * other loops. Note that this is not used in the main systemd daemon + * since we run a more elaborate mainloop there. */ + +typedef struct EpollData { + int fd; + void *object; + bool is_timeout:1; + bool fd_is_dupped:1; +} EpollData; + +static dbus_bool_t add_watch(DBusWatch *watch, void *data) { + EpollData *e; + struct epoll_event ev; + + assert(watch); + + e = new0(EpollData, 1); + if (!e) + return FALSE; + + e->fd = dbus_watch_get_unix_fd(watch); + e->object = watch; + e->is_timeout = false; + + zero(ev); + ev.events = bus_flags_to_events(watch); + ev.data.ptr = e; + + if (epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_ADD, e->fd, &ev) < 0) { + + if (errno != EEXIST) { + free(e); + return FALSE; + } + + /* Hmm, bloody D-Bus creates multiple watches on the + * same fd. epoll() does not like that. As a dirty + * hack we simply dup() the fd and hence get a second + * one we can safely add to the epoll(). */ + + e->fd = dup(e->fd); + if (e->fd < 0) { + free(e); + return FALSE; + } + + if (epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_ADD, e->fd, &ev) < 0) { + close_nointr_nofail(e->fd); + free(e); + return FALSE; + } + + e->fd_is_dupped = true; + } + + dbus_watch_set_data(watch, e, NULL); + + return TRUE; +} + +static void remove_watch(DBusWatch *watch, void *data) { + EpollData *e; + + assert(watch); + + e = dbus_watch_get_data(watch); + if (!e) + return; + + assert_se(epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_DEL, e->fd, NULL) >= 0); + + if (e->fd_is_dupped) + close_nointr_nofail(e->fd); + + free(e); +} + +static void toggle_watch(DBusWatch *watch, void *data) { + EpollData *e; + struct epoll_event ev; + + assert(watch); + + e = dbus_watch_get_data(watch); + if (!e) + return; + + zero(ev); + ev.events = bus_flags_to_events(watch); + ev.data.ptr = e; + + assert_se(epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_MOD, e->fd, &ev) == 0); +} + +static int timeout_arm(EpollData *e) { + struct itimerspec its; + + assert(e); + assert(e->is_timeout); + + zero(its); + + if (dbus_timeout_get_enabled(e->object)) { + timespec_store(&its.it_value, dbus_timeout_get_interval(e->object) * USEC_PER_MSEC); + its.it_interval = its.it_value; + } + + if (timerfd_settime(e->fd, 0, &its, NULL) < 0) + return -errno; + + return 0; +} + +static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) { + EpollData *e; + struct epoll_event ev; + + assert(timeout); + + e = new0(EpollData, 1); + if (!e) + return FALSE; + + e->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC); + if (e->fd < 0) + goto fail; + + e->object = timeout; + e->is_timeout = true; + + if (timeout_arm(e) < 0) + goto fail; + + zero(ev); + ev.events = EPOLLIN; + ev.data.ptr = e; + + if (epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_ADD, e->fd, &ev) < 0) + goto fail; + + dbus_timeout_set_data(timeout, e, NULL); + + return TRUE; + +fail: + if (e->fd >= 0) + close_nointr_nofail(e->fd); + + free(e); + return FALSE; +} + +static void remove_timeout(DBusTimeout *timeout, void *data) { + EpollData *e; + + assert(timeout); + + e = dbus_timeout_get_data(timeout); + if (!e) + return; + + assert_se(epoll_ctl(PTR_TO_INT(data), EPOLL_CTL_DEL, e->fd, NULL) >= 0); + close_nointr_nofail(e->fd); + free(e); +} + +static void toggle_timeout(DBusTimeout *timeout, void *data) { + EpollData *e; + int r; + + assert(timeout); + + e = dbus_timeout_get_data(timeout); + if (!e) + return; + + r = timeout_arm(e); + if (r < 0) + log_error("Failed to rearm timer: %s", strerror(-r)); +} + +int bus_loop_open(DBusConnection *c) { + int fd; + + assert(c); + + fd = epoll_create1(EPOLL_CLOEXEC); + if (fd < 0) + return -errno; + + if (!dbus_connection_set_watch_functions(c, add_watch, remove_watch, toggle_watch, INT_TO_PTR(fd), NULL) || + !dbus_connection_set_timeout_functions(c, add_timeout, remove_timeout, toggle_timeout, INT_TO_PTR(fd), NULL)) { + close_nointr_nofail(fd); + return -ENOMEM; + } + + return fd; +} + +int bus_loop_dispatch(int fd) { + int n; + struct epoll_event event; + EpollData *d; + + assert(fd >= 0); + + zero(event); + + n = epoll_wait(fd, &event, 1, 0); + if (n < 0) + return errno == EAGAIN || errno == EINTR ? 0 : -errno; + + assert_se(d = event.data.ptr); + + if (d->is_timeout) { + DBusTimeout *t = d->object; + + if (dbus_timeout_get_enabled(t)) + dbus_timeout_handle(t); + } else { + DBusWatch *w = d->object; + + if (dbus_watch_get_enabled(w)) + dbus_watch_handle(w, bus_events_to_flags(event.events)); + } + + return 0; +} diff --git a/src/dbus-loop.h b/src/dbus-loop.h new file mode 100644 index 0000000000..0bbdfe5925 --- /dev/null +++ b/src/dbus-loop.h @@ -0,0 +1,30 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foodbusloophfoo +#define foodbusloophfoo + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include + +int bus_loop_open(DBusConnection *c); +int bus_loop_dispatch(int fd); + +#endif diff --git a/src/logind-device.c b/src/logind-device.c new file mode 100644 index 0000000000..9084b5fb4e --- /dev/null +++ b/src/logind-device.c @@ -0,0 +1,85 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "logind.h" +#include "util.h" + +Device* device_new(Manager *m, const char *sysfs) { + Device *d; + + assert(m); + assert(sysfs); + + d = new0(Device, 1); + if (!d) + return NULL; + + d->sysfs = strdup(sysfs); + if (!d->sysfs) { + free(d); + return NULL; + } + + if (hashmap_put(m->devices, d->sysfs, d) < 0) { + free(d->sysfs); + free(d); + return NULL; + } + + d->manager = m; + dual_timestamp_get(&d->timestamp); + + return d; +} + +void device_free(Device *d) { + assert(d); + + device_detach(d); + + hashmap_remove(d->manager->devices, d->sysfs); + + free(d->sysfs); + free(d); +} + +void device_detach(Device *d) { + assert(d); + + if (d->seat) + LIST_REMOVE(Device, devices, d->seat->devices, d); + + d->seat = NULL; +} + +void device_attach(Device *d, Seat *s) { + assert(d); + assert(s); + + if (d->seat) + device_detach(d); + + LIST_PREPEND(Device, devices, d->seat->devices, d); + d->seat = s; +} diff --git a/src/logind-seat.c b/src/logind-seat.c new file mode 100644 index 0000000000..dcf1c7107b --- /dev/null +++ b/src/logind-seat.c @@ -0,0 +1,245 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include "logind.h" +#include "util.h" + +Seat *seat_new(Manager *m, const char *id) { + Seat *s; + + assert(m); + assert(id); + + s = new0(Seat, 1); + if (!s) + return NULL; + + s->state_file = strappend("/run/systemd/seat/", id); + if (!s->state_file) { + free(s); + return NULL; + } + + s->id = file_name_from_path(s->state_file); + + if (hashmap_put(m->seats, s->id, s) < 0) { + free(s->id); + free(s); + return NULL; + } + + s->manager = m; + + return s; +} + +void seat_free(Seat *s) { + assert(s); + + while (s->sessions) + session_free(s->sessions); + + assert(!s->active); + + while (s->devices) + device_free(s->devices); + + hashmap_remove(s->manager->seats, s->id); + + free(s->state_file); + free(s); +} + +int seat_save(Seat *s) { + FILE *f; + int r; + + assert(s); + + r = safe_mkdir("/run/systemd/seat", 0755, 0, 0); + if (r < 0) + return r; + + f = fopen(s->state_file, "we"); + if (!f) + return -errno; + + fprintf(f, + "IS_VTCONSOLE=%i\n", + s->manager->vtconsole == s); + + if (s->active) { + assert(s->active->user); + + fprintf(f, + "ACTIVE=%s\n" + "ACTIVE_UID=%lu\n", + s->active->id, + (unsigned long) s->active->user->uid); + } + + if (s->sessions) { + Session *i; + fputs("OTHER_UIDS=", f); + + LIST_FOREACH(sessions_by_seat, i, s->sessions) { + assert(i->user); + + if (i == s->active) + continue; + + fprintf(f, + "%s%lu", + i == s->sessions ? "" : " ", + (unsigned long) i->user->uid); + } + } + + fflush(f); + if (ferror(f)) { + r = -errno; + unlink(s->state_file); + } + + fclose(f); + return r; +} + +int seat_load(Seat *s) { + assert(s); + + return 0; +} + +static int vt_allocate(int vtnr) { + int fd, r; + char *p; + + assert(vtnr >= 1); + + if (asprintf(&p, "/dev/tty%i", vtnr) < 0) + return -ENOMEM; + + fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC); + free(p); + + r = fd < 0 ? -errno : 0; + + if (fd >= 0) + close_nointr_nofail(fd); + + return r; +} + +int seat_preallocate_vts(Seat *s) { + int i, r = 0; + + assert(s); + assert(s->manager); + + if (s->manager->n_autovts <= 0) + return 0; + + if (s->manager->vtconsole != s) + return 0; + + for (i = 1; i < s->manager->n_autovts; i++) { + int q; + + q = vt_allocate(i); + if (r >= 0 && q < 0) + r = q; + } + + return r; +} + +int seat_apply_acls(Seat *s) { + assert(s); + + + return 0; +} + +static int vt_is_busy(int vtnr) { + struct vt_stat vt_stat; + int r = 0, fd; + + assert(vtnr >= 1); + + fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + return -errno; + + if (ioctl(fd, VT_GETSTATE, &vt_stat) < 0) + r = -errno; + else + r = !!(vt_stat.v_state & (1 << vtnr)); + + close_nointr_nofail(fd); + + return r; +} + +void seat_active_vt_changed(Seat *s, int vtnr) { + Session *i; + + assert(s); + assert(vtnr >= 1); + assert(s->manager->vtconsole == s); + + s->active = NULL; + + LIST_FOREACH(sessions_by_seat, i, s->sessions) + if (i->vtnr == vtnr) { + s->active = i; + break; + } + + seat_apply_acls(s); + + if (vt_is_busy(vtnr) == 0) + manager_spawn_autovt(s->manager, vtnr); +} + +int seat_stop(Seat *s) { + Session *session; + int r = 0; + + assert(s); + + LIST_FOREACH(sessions_by_seat, session, s->sessions) { + int k; + + k = session_stop(session); + if (k < 0) + r = k; + } + + return r; +} diff --git a/src/logind-session.c b/src/logind-session.c new file mode 100644 index 0000000000..9af99d054a --- /dev/null +++ b/src/logind-session.c @@ -0,0 +1,472 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "logind.h" +#include "strv.h" +#include "util.h" +#include "cgroup-util.h" + +Session* session_new(Manager *m, User *u, const char *id) { + Session *s; + + assert(m); + assert(id); + + s = new(Session, 1); + if (!s) + return NULL; + + s->state_file = strappend("/run/systemd/session/", id); + if (!s->state_file) { + free(s); + return NULL; + } + + s->id = file_name_from_path(s->state_file); + + if (hashmap_put(m->sessions, s->id, s) < 0) { + free(s->id); + free(s); + return NULL; + } + + s->manager = m; + s->pipe_fd = -1; + s->user = u; + + dual_timestamp_get(&s->timestamp); + + return s; +} + +void session_free(Session *s) { + assert(s); + + if (s->user) { + LIST_REMOVE(Session, sessions_by_user, s->user->sessions, s); + + if (s->user->display == s) + s->user->display = NULL; + } + + if (s->seat) + LIST_REMOVE(Session, sessions_by_seat, s->seat->sessions, s); + + free(s->cgroup_path); + strv_free(s->controllers); + + free(s->tty); + free(s->display); + free(s->remote_host); + + hashmap_remove(s->manager->sessions, s->id); + + free(s->state_file); + free(s); +} + +int session_save(Session *s) { + FILE *f; + int r = 0; + + assert(s); + + r = safe_mkdir("/run/systemd/session", 0755, 0, 0); + if (r < 0) + return r; + + f = fopen(s->state_file, "we"); + if (!f) + return -errno; + + assert(s->user); + + fprintf(f, + "# This is private data. Do not parse.\n" + "UID=%lu\n" + "USER=%s\n" + "ACTIVE=%i\n" + "REMOTE=%i\n" + "KILL_PROCESSES=%i\n", + (unsigned long) s->user->uid, + s->user->name, + session_is_active(s), + s->remote, + s->kill_processes); + + if (s->cgroup_path) + fprintf(f, + "CGROUP=%s\n", + s->cgroup_path); + + if (s->seat) + fprintf(f, + "SEAT=%s\n", + s->seat->id); + + if (s->tty) + fprintf(f, + "TTY=%s\n", + s->tty); + + if (s->display) + fprintf(f, + "DISPLAY=%s\n", + s->display); + + if (s->remote_host) + fprintf(f, + "REMOTE_HOST=%s\n", + s->remote_host); + + if (s->seat && s->seat->manager->vtconsole == s->seat) + fprintf(f, + "VTNR=%i\n", + s->vtnr); + + if (s->leader > 0) + fprintf(f, + "LEADER=%lu\n", + (unsigned long) s->leader); + + if (s->audit_id > 0) + fprintf(f, + "AUDIT=%llu\n", + (unsigned long long) s->audit_id); + + fflush(f); + if (ferror(f)) { + r = -errno; + unlink(s->state_file); + } + + fclose(f); + return r; +} + +int session_load(Session *s) { + assert(s); + + return 0; +} + +int session_activate(Session *s) { + int r; + + assert(s); + + if (s->vtnr < 0) + return -ENOTSUP; + + if (!s->seat) + return -ENOTSUP; + + if (s->seat->active == s) + return 0; + + assert(s->manager->vtconsole == s->seat); + + r = chvt(s->vtnr); + if (r < 0) + return r; + + s->seat->active = s; + + return seat_apply_acls(s->seat); +} + +bool x11_display_is_local(const char *display) { + assert(display); + + return + display[0] == ':' && + display[1] >= '0' && + display[1] <= '9'; +} + +static int session_link_x11_socket(Session *s) { + char *t, *f, *c; + size_t k; + + assert(s); + assert(s->user); + assert(s->user->runtime_path); + + if (s->user->display) + return 0; + + if (!s->display || !x11_display_is_local(s->display)) + return 0; + + k = strspn(s->display+1, "0123456789"); + f = new(char, sizeof("/tmp/.X11-unix/X") + k); + if (!f) { + log_error("Out of memory"); + return -ENOMEM; + } + + c = stpcpy(f, "/tmp/.X11-unix/X"); + memcpy(c, s->display+1, k); + c[k] = 0; + + if (access(f, F_OK) < 0) { + log_warning("Session %s has display %s with nonexisting socket %s.", s->id, s->display, f); + free(f); + return -ENOENT; + } + + t = strappend(s->user->runtime_path, "/display"); + if (!t) { + log_error("Out of memory"); + free(f); + return -ENOMEM; + } + + if (link(f, t) < 0) { + if (errno == EEXIST) { + unlink(t); + + if (link(f, t) >= 0) + goto done; + } + + if (symlink(f, t) < 0) { + + if (errno == EEXIST) { + unlink(t); + + if (symlink(f, t) >= 0) + goto done; + } + + log_error("Failed to link %s to %s: %m", f, t); + free(f); + free(t); + return -errno; + } + } + +done: + log_info("Linked %s to %s.", f, t); + free(f); + free(t); + + s->user->display = s; + + return 0; +} + +static int session_create_cgroup(Session *s) { + char **k; + char *p; + int r; + + assert(s); + assert(s->user); + assert(s->user->cgroup_path); + + if (!s->cgroup_path) { + if (asprintf(&p, "%s/%s", s->user->cgroup_path, s->id) < 0) { + log_error("Out of memory"); + return -ENOMEM; + } + } else + p = s->cgroup_path; + + if (s->leader > 0) + r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, p, s->leader); + else + r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p); + + if (r < 0) { + free(p); + s->cgroup_path = NULL; + log_error("Failed to create "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r)); + return r; + } + + s->cgroup_path = p; + + STRV_FOREACH(k, s->manager->controllers) { + if (s->leader > 0) + r = cg_create_and_attach(*k, p, s->leader); + else + r = cg_create(*k, p); + + if (r < 0) + log_warning("Failed to create cgroup %s:%s: %s", *k, p, strerror(-r)); + } + + return 0; +} + +int session_start(Session *s) { + int r; + + assert(s); + assert(s->user); + + /* Create user first */ + r = user_start(s->user); + if (r < 0) + return r; + + /* Create cgroup */ + r = session_create_cgroup(s); + if (r < 0) + return r; + + /* Create X11 symlink */ + session_link_x11_socket(s); + return 0; +} + +static bool session_shall_kill(Session *s) { + assert(s); + + return s->kill_processes; +} + +static int session_kill_cgroup(Session *s) { + int r; + char **k; + + assert(s); + + if (!s->cgroup_path) + return 0; + + cg_trim(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false); + + if (session_shall_kill(s)) { + + r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true); + if (r < 0) + log_error("Failed to kill session cgroup: %s", strerror(-r)); + + } else { + r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true); + if (r < 0) + log_error("Failed to check session cgroup: %s", strerror(-r)); + else if (r > 0) { + r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path); + if (r < 0) + log_error("Failed to delete session cgroup: %s", strerror(-r)); + } else + r = -EBUSY; + } + + STRV_FOREACH(k, s->user->manager->controllers) + cg_trim(*k, s->cgroup_path, true); + + free(s->cgroup_path); + s->cgroup_path = NULL; + + return r; +} + +static int session_unlink_x11_socket(Session *s) { + char *t; + int r; + + assert(s); + assert(s->user); + + if (s->user->display != s) + return 0; + + s->user->display = NULL; + + t = strappend(s->user->runtime_path, "/display"); + if (!t) { + log_error("Out of memory"); + return -ENOMEM; + } + + r = unlink(t); + free(t); + + return r < 0 ? -errno : 0; +} + +int session_stop(Session *s) { + int r = 0, k; + + assert(s); + + /* Kill cgroup */ + k = session_kill_cgroup(s); + if (k < 0) + r = k; + + /* Remove X11 symlink */ + session_unlink_x11_socket(s); + + return r; +} + +bool session_is_active(Session *s) { + assert(s); + + if (!s->seat) + return true; + + return s->seat->active == s; +} + +int session_check_gc(Session *s) { + int r; + + assert(s); + + if (s->pipe_fd >= 0) { + + r = pipe_eof(s->pipe_fd); + if (r < 0) + return r; + + if (r <= 0) + return 1; + } + + if (s->cgroup_path) { + + r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false); + if (r < 0) + return r; + + if (r <= 0) + return 1; + } + + return 0; +} + +static const char* const session_type_table[_SESSION_TYPE_MAX] = { + [SESSION_TERMINAL] = "terminal", + [SESSION_X11] = "x11" +}; + +DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType); diff --git a/src/logind-user.c b/src/logind-user.c new file mode 100644 index 0000000000..1292eed3e2 --- /dev/null +++ b/src/logind-user.c @@ -0,0 +1,420 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "logind.h" +#include "util.h" +#include "cgroup-util.h" +#include "hashmap.h" +#include "strv.h" + +User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) { + User *u; + + assert(m); + assert(name); + + u = new(User, 1); + if (!u) + return NULL; + + u->name = strdup(name); + if (!u->name) { + free(u); + return NULL; + } + + if (asprintf(&u->state_file, "/run/systemd/user/%lu", (unsigned long) uid) < 0) { + free(u->name); + free(u); + return NULL; + } + + if (hashmap_put(m->users, ULONG_TO_PTR((unsigned long) uid), u) < 0) { + free(u->state_file); + free(u->name); + free(u); + return NULL; + } + + u->manager = m; + u->uid = uid; + u->gid = gid; + + return u; +} + +void user_free(User *u) { + assert(u); + + while (u->sessions) + session_free(u->sessions); + + free(u->cgroup_path); + + free(u->service); + free(u->runtime_path); + + hashmap_remove(u->manager->users, ULONG_TO_PTR((unsigned long) u->uid)); + + free(u->name); + free(u->state_file); + free(u); +} + +int user_save(User *u) { + FILE *f; + int r; + + assert(u); + assert(u->state_file); + + r = safe_mkdir("/run/systemd/user", 0755, 0, 0); + if (r < 0) + return r; + + f = fopen(u->state_file, "we"); + if (!f) + return -errno; + + fprintf(f, + "NAME=%s\n" + "STATE=%s\n", + u->name, + user_state_to_string(user_get_state(u))); + + if (u->cgroup_path) + fprintf(f, + "CGROUP=%s\n", + u->cgroup_path); + + if (u->runtime_path) + fprintf(f, + "RUNTIME=%s\n", + u->runtime_path); + + if (u->service) + fprintf(f, + "SERVICE=%s\n", + u->service); + + if (u->display) + fprintf(f, + "DISPLAY=%s\n", + u->display->id); + + fflush(f); + if (ferror(f)) { + r = -errno; + unlink(u->state_file); + } + + fclose(f); + return r; +} + +int user_load(User *u) { + int r; + char *display = NULL; + Session *s; + + assert(u); + + r = parse_env_file(u->state_file, "r", + "CGROUP", &u->cgroup_path, + "RUNTIME", &u->runtime_path, + "SERVICE", &u->service, + "DISPLAY", &display, + NULL); + if (r < 0) { + free(display); + + if (r == -ENOENT) + return 0; + + log_error("Failed to read %s: %s", u->state_file, strerror(-r)); + return r; + } + + s = hashmap_get(u->manager->sessions, display); + free(display); + + if (s && s->display && x11_display_is_local(s->display)) + u->display = s; + + return r; +} + +static int user_mkdir_runtime_path(User *u) { + char *p; + int r; + + assert(u); + + r = safe_mkdir("/run/user", 0755, 0, 0); + if (r < 0) { + log_error("Failed to create /run/user: %s", strerror(-r)); + return r; + } + + if (!u->runtime_path) { + p = strappend("/run/user/", u->name); + + if (!p) { + log_error("Out of memory"); + return -ENOMEM; + } + } else + p = u->runtime_path; + + r = safe_mkdir(p, 0700, u->uid, u->gid); + if (r < 0) { + log_error("Failed to create runtime directory %s: %s", p, strerror(-r)); + free(p); + u->runtime_path = NULL; + return r; + } + + u->runtime_path = p; + return 0; +} + +static int user_create_cgroup(User *u) { + char **k; + char *p; + int r; + + assert(u); + + if (!u->cgroup_path) { + if (asprintf(&p, "%s/%s", u->manager->cgroup_path, u->name) < 0) { + log_error("Out of memory"); + return -ENOMEM; + } + } else + p = u->cgroup_path; + + r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p); + if (r < 0) { + free(p); + u->cgroup_path = NULL; + log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r)); + return r; + } + + u->cgroup_path = p; + + STRV_FOREACH(k, u->manager->controllers) { + r = cg_create(*k, p); + if (r < 0) + log_warning("Failed to create cgroup %s:%s: %s", *k, p, strerror(-r)); + } + + return 0; +} + +static int user_start_service(User *u) { + assert(u); + + return 0; +} + +int user_start(User *u) { + int r; + + assert(u); + + /* Make XDG_RUNTIME_DIR */ + r = user_mkdir_runtime_path(u); + if (r < 0) + return r; + + /* Create cgroup */ + r = user_create_cgroup(u); + if (r < 0) + return r; + + /* Spawn user systemd */ + r = user_start_service(u); + if (r < 0) + return r; + + dual_timestamp_get(&u->timestamp); + + return 0; +} + +static int user_stop_service(User *u) { + assert(u); + + if (!u->service) + return 0; + + return 0; +} + +static int user_shall_kill(User *u) { + assert(u); + + return u->manager->kill_user_processes; +} + +static int user_kill_cgroup(User *u) { + int r; + char **k; + + assert(u); + + if (!u->cgroup_path) + return 0; + + cg_trim(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false); + + if (user_shall_kill(u)) { + + r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true); + if (r < 0) + log_error("Failed to kill user cgroup: %s", strerror(-r)); + } else { + + r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true); + if (r < 0) + log_error("Failed to check user cgroup: %s", strerror(-r)); + else if (r > 0) { + r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path); + if (r < 0) + log_error("Failed to delete user cgroup: %s", strerror(-r)); + } else + r = -EBUSY; + } + + STRV_FOREACH(k, u->manager->controllers) + cg_trim(*k, u->cgroup_path, true); + + free(u->cgroup_path); + u->cgroup_path = NULL; + + return r; +} + +static int user_remove_runtime_path(User *u) { + int r; + + assert(u); + + if (!u->runtime_path) + return 0; + + r = rm_rf(u->runtime_path, false, true); + if (r < 0) + log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r)); + + free(u->runtime_path); + u->runtime_path = NULL; + + return r; +} + +int user_stop(User *u) { + Session *s; + int r = 0, k; + assert(u); + + LIST_FOREACH(sessions_by_user, s, u->sessions) { + k = session_stop(s); + if (k < 0) + r = k; + } + + /* Kill systemd */ + k = user_stop_service(u); + if (k < 0) + r = k; + + /* Kill cgroup */ + k = user_kill_cgroup(u); + if (k < 0) + r = k; + + /* Kill XDG_RUNTIME_DIR */ + k = user_remove_runtime_path(u); + if (k < 0) + r = k; + + return r; +} + +int user_check_gc(User *u) { + int r; + char *p; + + assert(u); + + if (u->sessions) + return 1; + + if (asprintf(&p, "/var/lib/systemd/linger/%s", u->name) < 0) + return -ENOMEM; + + r = access(p, F_OK) >= 0; + free(p); + + if (r > 0) + return 1; + + if (u->cgroup_path) { + r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false); + if (r < 0) + return r; + + if (r <= 0) + return 1; + } + + return 0; +} + +UserState user_get_state(User *u) { + Session *i; + + assert(u); + + if (!u->sessions) + return USER_LINGERING; + + LIST_FOREACH(sessions_by_user, i, u->sessions) + if (session_is_active(i)) + return USER_ACTIVE; + + return USER_ONLINE; +} + +static const char* const user_state_table[_USER_STATE_MAX] = { + [USER_OFFLINE] = "offline", + [USER_LINGERING] = "lingering", + [USER_ONLINE] = "online", + [USER_ACTIVE] = "active" +}; + +DEFINE_STRING_TABLE_LOOKUP(user_state, UserState); diff --git a/src/logind.c b/src/logind.c new file mode 100644 index 0000000000..0c26aaddbf --- /dev/null +++ b/src/logind.c @@ -0,0 +1,886 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "logind.h" +#include "dbus-common.h" +#include "dbus-loop.h" + +enum { + FD_UDEV, + FD_CONSOLE, + FD_BUS +}; + +Manager *manager_new(void) { + Manager *m; + + m = new0(Manager, 1); + if (!m) + return NULL; + + m->console_active_fd = -1; + m->bus_fd = -1; + m->udev_fd = -1; + m->epoll_fd = -1; + m->n_autovts = 6; + + m->devices = hashmap_new(string_hash_func, string_compare_func); + m->seats = hashmap_new(string_hash_func, string_compare_func); + m->sessions = hashmap_new(string_hash_func, string_compare_func); + m->users = hashmap_new(trivial_hash_func, trivial_compare_func); + + if (!m->devices || !m->seats || !m->sessions || !m->users) { + manager_free(m); + return NULL; + } + + m->udev = udev_new(); + if (!m->udev) { + manager_free(m); + return NULL; + } + + if (cg_get_user_path(&m->cgroup_path) < 0) { + manager_free(m); + return NULL; + } + + return m; +} + +void manager_free(Manager *m) { + Session *session; + User *u; + Device *d; + Seat *s; + + assert(m); + + while ((session = hashmap_first(m->sessions))) + session_free(session); + + while ((u = hashmap_first(m->users))) + user_free(u); + + while ((d = hashmap_first(m->devices))) + device_free(d); + + while ((s = hashmap_first(m->seats))) + seat_free(s); + + hashmap_free(m->sessions); + hashmap_free(m->users); + hashmap_free(m->devices); + hashmap_free(m->seats); + + if (m->console_active_fd >= 0) + close_nointr_nofail(m->console_active_fd); + + if (m->udev_monitor) + udev_monitor_unref(m->udev_monitor); + + if (m->udev) + udev_unref(m->udev); + + if (m->bus) { + dbus_connection_flush(m->bus); + dbus_connection_close(m->bus); + dbus_connection_unref(m->bus); + } + + if (m->bus_fd >= 0) + close_nointr_nofail(m->bus_fd); + + if (m->epoll_fd >= 0) + close_nointr_nofail(m->epoll_fd); + + free(m->cgroup_path); + free(m); +} + +int manager_add_device(Manager *m, const char *sysfs, Device **_device) { + Device *d; + + assert(m); + assert(sysfs); + + d = hashmap_get(m->devices, sysfs); + if (d) { + if (_device) + *_device = d; + + return 0; + } + + d = device_new(m, sysfs); + if (!d) + return -ENOMEM; + + if (_device) + *_device = d; + + return 0; +} + +int manager_add_seat(Manager *m, const char *id, Seat **_seat) { + Seat *s; + + assert(m); + assert(id); + + s = hashmap_get(m->seats, id); + if (s) { + if (_seat) + *_seat = s; + + return 0; + } + + s = seat_new(m, id); + if (!s) + return -ENOMEM; + + if (_seat) + *_seat = s; + + return 0; +} + +int manager_add_session(Manager *m, User *u, const char *id, Session **_session) { + Session *s; + + assert(m); + assert(id); + + s = hashmap_get(m->sessions, id); + if (s) { + if (_session) + *_session = s; + + return 0; + } + + s = session_new(m, u, id); + if (!s) + return -ENOMEM; + + if (_session) + *_session = s; + + return 0; +} + +int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user) { + User *u; + int r; + + assert(m); + assert(name); + + u = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid)); + if (u) { + if (_user) + *_user = u; + + return 0; + } + + u = user_new(m, uid, gid, name); + if (!u) + return -ENOMEM; + + r = user_start(u); + if (r < 0) { + user_stop(u); + user_free(u); + } else { + if (_user) + *_user = u; + } + + return r; +} + +int manager_add_user_by_name(Manager *m, const char *name, User **_user) { + struct passwd *p; + + assert(m); + assert(name); + + errno = 0; + p = getpwnam(name); + if (!p) + return errno ? -errno : -ENOENT; + + return manager_add_user(m, p->pw_uid, p->pw_gid, name, _user); +} + +int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) { + struct passwd *p; + + assert(m); + + errno = 0; + p = getpwuid(uid); + if (!p) + return errno ? -errno : -ENOENT; + + return manager_add_user(m, uid, p->pw_gid, p->pw_name, _user); +} + +int manager_process_device(Manager *m, struct udev_device *d) { + const char *sn; + Seat *seat; + Device *device; + int r; + + assert(m); + + sn = udev_device_get_property_value(d, "SEAT"); + if (!sn) + sn = "seat0"; + + r = manager_add_device(m, udev_device_get_syspath(d), &device); + if (r < 0) + return r; + + r = manager_add_seat(m, sn, &seat); + if (r < 0) + return r; + + device_attach(device, seat); + return 0; +} + +int manager_enumerate_devices(Manager *m) { + struct udev_list_entry *item = NULL, *first = NULL; + struct udev_enumerate *e; + int r = -ENOMEM; + + assert(m); + + /* Loads devices from udev and creates seats for them as + * necessary */ + + e = udev_enumerate_new(m->udev); + if (!e) + return -ENOMEM; + + if (udev_enumerate_add_match_subsystem(e, "graphics") < 0) + goto finish; + + if (udev_enumerate_add_match_tag(e, "seat") < 0) + goto finish; + + if (udev_enumerate_scan_devices(e) < 0) + goto finish; + + first = udev_enumerate_get_list_entry(e); + udev_list_entry_foreach(item, first) { + struct udev_device *d; + int k; + + d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item)); + if (!d) + goto finish; + + k = manager_process_device(m, d); + udev_device_unref(d); + + if (k < 0) + r = k; + } + +finish: + if (e) + udev_enumerate_unref(e); + + return r; +} + +int manager_enumerate_seats(Manager *m) { + DIR *d; + struct dirent *de; + int r = 0; + + assert(m); + + /* This loads data about seats stored on disk, but does not + * actually create any seats. Removes data of seats that no + * longer exist. */ + + d = opendir("/run/systemd/seats"); + if (!d) { + if (errno == ENOENT) + return 0; + + log_error("Failed to open /run/systemd/seats: %m"); + return -errno; + } + + while ((de = readdir(d))) { + Seat *s; + int k; + + if (!dirent_is_file(de)) + continue; + + s = hashmap_get(m->seats, de->d_name); + if (!s) { + unlinkat(dirfd(d), de->d_name, 0); + continue; + } + + k = seat_load(s); + if (k < 0) + r = k; + } + + closedir(d); + + return r; +} + +static int manager_enumerate_users_from_cgroup(Manager *m) { + int r = 0; + char *name; + DIR *d; + + r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_path, &d); + if (r < 0) { + if (r == -ENOENT) + return 0; + + log_error("Failed to open %s: %s", m->cgroup_path, strerror(-r)); + return r; + } + + while ((r = cg_read_subgroup(d, &name)) > 0) { + User *user; + + r = manager_add_user_by_name(m, name, &user); + free(name); + + if (r < 0) + break; + + if (user->cgroup_path) + continue; + + if (asprintf(&user->cgroup_path, "%s/%s", m->cgroup_path, name) < 0) { + r = -ENOMEM; + break; + } + } + + if (d) + closedir(d); + + return r; +} + +int manager_enumerate_users(Manager *m) { + DIR *d; + struct dirent *de; + int r; + + assert(m); + + r = manager_enumerate_users_from_cgroup(m); + + d = opendir("/run/systemd/users"); + if (!d) { + if (errno == ENOENT) + return 0; + + log_error("Failed to open /run/systemd/users: %m"); + return -errno; + } + + while ((de = readdir(d))) { + unsigned long ul; + User *u; + int k; + + if (!dirent_is_file(de)) + continue; + + k = safe_atolu(de->d_name, &ul); + if (k < 0) { + log_error("Failed to parse file name %s: %s", de->d_name, strerror(-k)); + continue; + } + + u = hashmap_get(m->users, ULONG_TO_PTR(ul)); + if (!u) { + unlinkat(dirfd(d), de->d_name, 0); + continue; + } + + k = user_load(u); + if (k < 0) + r = k; + } + + closedir(d); + + return r; +} + +static int manager_enumerate_sessions_from_cgroup(Manager *m) { + User *u; + Iterator i; + int r = 0; + + HASHMAP_FOREACH(u, m->users, i) { + DIR *d; + char *name; + int k; + + k = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, &d); + if (k < 0) { + if (k == -ENOENT) + continue; + + log_error("Failed to open %s: %s", u->cgroup_path, strerror(-k)); + r = k; + continue; + } + + while ((k = cg_read_subgroup(d, &name)) > 0) { + Session *session; + + k = manager_add_session(m, u, name, &session); + free(name); + + if (k < 0) + break; + + if (session->cgroup_path) + continue; + + if (asprintf(&session->cgroup_path, "%s/%s", u->cgroup_path, name) < 0) { + k = -ENOMEM; + break; + } + } + + closedir(d); + + if (k < 0) + r = k; + } + + return r; +} + +int manager_enumerate_sessions(Manager *m) { + DIR *d; + struct dirent *de; + int r = 0; + + assert(m); + + r = manager_enumerate_sessions_from_cgroup(m); + + d = opendir("/run/systemd/sessions"); + if (!d) { + if (errno == ENOENT) + return 0; + + log_error("Failed to open /run/systemd/sessions: %m"); + return -errno; + } + + while ((de = readdir(d))) { + struct Session *s; + int k; + + if (!dirent_is_file(de)) + continue; + + s = hashmap_get(m->sessions, de->d_name); + if (!s) { + unlinkat(dirfd(d), de->d_name, 0); + continue; + } + + k = session_load(s); + if (k < 0) + r = k; + } + + closedir(d); + + return r; +} + +int manager_start_linger_users(Manager *m) { + DIR *d; + struct dirent *de; + int r = 0; + + d = opendir("/var/lib/systemd/linger"); + if (!d) { + if (errno == ENOENT) + return 0; + + log_error("Failed to open /var/lib/systemd/linger/: %m"); + return -errno; + } + + while ((de = readdir(d))) { + int k; + + if (!dirent_is_file(de)) + continue; + + k = manager_add_user_by_name(m, de->d_name, NULL); + if (k < 0) { + log_notice("Couldn't add lingering user %s: %s", de->d_name, strerror(-k)); + r = k; + } + } + + closedir(d); + + return r; +} + +int manager_dispatch_udev(Manager *m) { + struct udev_device *d; + int r; + + assert(m); + + d = udev_monitor_receive_device(m->udev_monitor); + if (!d) + return -ENOMEM; + + r = manager_process_device(m, d); + udev_device_unref(d); + + return r; +} + +int manager_dispatch_console(Manager *m) { + char t[64]; + ssize_t k; + int r, vtnr; + + assert(m); + + lseek(m->console_active_fd, SEEK_SET, 0); + + k = read(m->console_active_fd, t, sizeof(t)-1); + if (k <= 0) { + log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF"); + return k < 0 ? -errno : -EIO; + } + + t[k] = 0; + if (!startswith(t, "tty")) { + log_error("Hm, /sys/class/tty/tty0/active is badly formatted."); + return -EIO; + } + + r = safe_atoi(t+3, &vtnr); + if (r < 0) { + log_error("Failed to parse VT number %s", t+3); + return r; + } + + if (vtnr <= 0) { + log_error("VT number invalid: %s", t+3); + return -EIO; + } + + if (m->vtconsole) + seat_active_vt_changed(m->vtconsole, vtnr); + + return 0; +} + +int manager_spawn_autovt(Manager *m, int vtnr) { + assert(m); + + return 0; +} + +static DBusHandlerResult login_message_handler( + DBusConnection *connection, + DBusMessage *message, + void *userdata) { + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult login_message_filter( + DBusConnection *connection, + DBusMessage *message, + void *userdata) { + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static int manager_connect_bus(Manager *m) { + const DBusObjectPathVTable login_vtable = { + .message_function = login_message_handler + }; + + DBusError error; + int r; + struct epoll_event ev; + + assert(m); + assert(!m->bus); + assert(m->bus_fd < 0); + + dbus_error_init(&error); + + m->bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); + if (!m->bus) { + log_error("Failed to get system D-Bus connection: %s", error.message); + r = -ECONNREFUSED; + goto fail; + } + + if (!dbus_connection_register_object_path(m->bus, "/org/freedesktop/login1", &login_vtable, NULL)) { + log_error("Not enough memory"); + r = -ENOMEM; + goto fail; + } + + if (!dbus_connection_add_filter(m->bus, login_message_filter, m, NULL)) { + log_error("Not enough memory"); + r = -ENOMEM; + goto fail; + } + + dbus_bus_add_match(m->bus, + "type='signal'," + "interface='org.freedesktop.systemd1.Agent'," + "member='Released'," + "path='/org/freedesktop/systemd1/agent'", + &error); + + if (dbus_error_is_set(&error)) { + log_error("Failed to register match: %s", error.message); + r = -EIO; + goto fail; + } + + if (dbus_bus_request_name(m->bus, "org.freedesktop.login1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) < 0) { + log_error("Failed to register name on bus: %s", error.message); + r = -EEXIST; + goto fail; + } + + m->bus_fd = bus_loop_open(m->bus); + if (m->bus_fd < 0) { + r = m->bus_fd; + goto fail; + } + + zero(ev); + ev.events = EPOLLIN; + ev.data.u32 = FD_BUS; + + if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->bus_fd, &ev) < 0) + goto fail; + + return 0; + +fail: + dbus_error_free(&error); + + return r; +} + +static int manager_connect_console(Manager *m) { + struct epoll_event ev; + + assert(m); + assert(m->console_active_fd < 0); + + m->console_active_fd = open("/sys/class/tty/tty0/active", O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (m->console_active_fd < 0) { + log_error("Failed to open /sys/class/tty/tty0/active: %m"); + return -errno; + } + + zero(ev); + ev.events = EPOLLIN; + ev.data.u32 = FD_CONSOLE; + + if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->console_active_fd, &ev) < 0) + return -errno; + + return 0; +} + +static int manager_connect_udev(Manager *m) { + struct epoll_event ev; + + assert(m); + assert(!m->udev_monitor); + + m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev"); + if (!m->udev_monitor) + return -ENOMEM; + + udev_monitor_filter_add_match_tag(m->udev_monitor, "seat"); + + if (udev_monitor_filter_add_match_subsystem_devtype(m->udev_monitor, "graphics", NULL) < 0) + return -ENOMEM; + + if (udev_monitor_enable_receiving(m->udev_monitor) < 0) + return -ENOMEM; + + m->udev_fd = udev_monitor_get_fd(m->udev_monitor); + + zero(ev); + ev.events = EPOLLIN; + ev.data.u32 = FD_UDEV; + + if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->udev_fd, &ev) < 0) + return -errno; + + return 0; +} + +int manager_startup(Manager *m) { + int r; + + assert(m); + assert(m->epoll_fd <= 0); + + m->epoll_fd = epoll_create1(EPOLL_CLOEXEC); + if (m->epoll_fd < 0) + return -errno; + + /* Connect to udev */ + r = manager_connect_udev(m); + if (r < 0) + return r; + + /* Connect to console */ + r = manager_connect_console(m); + if (r < 0) + return r; + + /* Connect to the bus */ + r = manager_connect_bus(m); + if (r < 0) + return r; + + /* Deserialize state */ + manager_enumerate_devices(m); + manager_enumerate_seats(m); + manager_enumerate_users(m); + manager_enumerate_sessions(m); + + /* Spawn lingering users */ + manager_start_linger_users(m); + + return 0; +} + +int manager_run(Manager *m) { + assert(m); + + for (;;) { + struct epoll_event event; + int n; + + if (dbus_connection_dispatch(m->bus) != DBUS_DISPATCH_COMPLETE) + continue; + + n = epoll_wait(m->epoll_fd, &event, 1, -1); + if (n < 0) { + log_error("epoll() failed: %m"); + return -errno; + } + + switch (event.data.u32) { + + case FD_UDEV: + manager_dispatch_udev(m); + break; + + case FD_CONSOLE: + manager_dispatch_console(m); + break; + + case FD_BUS: + bus_loop_dispatch(m->bus_fd); + break; + } + } + + return 0; +} + +int main(int argc, char *argv[]) { + Manager *m = NULL; + int r = 0; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + if (argc != 1) { + log_error("This program takes no arguments."); + r = -EINVAL; + goto finish; + } + + umask(0022); + + m = manager_new(); + if (!m) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + r = manager_startup(m); + if (r < 0) { + log_error("Failed to fully start up daemon: %s", strerror(-r)); + goto finish; + } + + r = manager_run(m); + +finish: + if (m) + manager_free(m); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/logind.h b/src/logind.h new file mode 100644 index 0000000000..5e69a71b02 --- /dev/null +++ b/src/logind.h @@ -0,0 +1,235 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foologindhfoo +#define foologindhfoo + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "util.h" +#include "list.h" +#include "hashmap.h" +#include "cgroup-util.h" + +/* TODO: + * + * recreate VTs when disallocated + * udev rules + * spawn user systemd + * non-local X11 server + * udev-acl + * reboot/shutdown halt management + * PAM rewrite + */ + +typedef struct Manager Manager; +typedef struct Device Device; +typedef struct Seat Seat; +typedef struct Session Session; +typedef struct User User; + +struct Device { + Manager *manager; + + char *sysfs; + Seat *seat; + + dual_timestamp timestamp; + + LIST_FIELDS(struct Device, devices); +}; + +struct Seat { + Manager *manager; + char *id; + + char *state_file; + + LIST_HEAD(Device, devices); + + Session *active; + LIST_HEAD(Session, sessions); +}; + +typedef enum SessionType { + SESSION_TERMINAL, + SESSION_X11, + _SESSION_TYPE_MAX, + _SESSION_TYPE_INVALID = -1 +} SessionType; + +struct Session { + Manager *manager; + + char *id; + SessionType type; + + char *state_file; + + User *user; + + dual_timestamp timestamp; + + char *tty; + char *display; + + bool remote; + char *remote_host; + + int vtnr; + Seat *seat; + + pid_t leader; + uint64_t audit_id; + + int pipe_fd; + + char *cgroup_path; + char **controllers, **reset_controllers; + + bool kill_processes; + + LIST_FIELDS(Session, sessions_by_user); + LIST_FIELDS(Session, sessions_by_seat); +}; + +typedef enum UserState { + USER_OFFLINE, + USER_LINGERING, + USER_ONLINE, + USER_ACTIVE, + _USER_STATE_MAX, + _USER_STATE_INVALID = -1 +} UserState; + +struct User { + Manager *manager; + + uid_t uid; + gid_t gid; + char *name; + + char *state_file; + char *runtime_path; + char *service; + char *cgroup_path; + + Session *display; + + dual_timestamp timestamp; + + LIST_HEAD(Session, sessions); +}; + +struct Manager { + DBusConnection *bus; + + Hashmap *devices; + Hashmap *seats; + Hashmap *sessions; + Hashmap *users; + + struct udev *udev; + struct udev_monitor *udev_monitor; + + int udev_fd; + int console_active_fd; + int bus_fd; + int epoll_fd; + + int n_autovts; + + Seat *vtconsole; + + char *cgroup_path; + char **controllers, **reset_controllers; + + char **kill_only_users, **kill_exlude_users; + + bool kill_user_processes; +}; + +Device* device_new(Manager *m, const char *sysfs); +void device_free(Device *d); +void device_attach(Device *d, Seat *s); +void device_detach(Device *d); + +Seat *seat_new(Manager *m, const char *id); +void seat_free(Seat *s); +int seat_preallocate_vts(Seat *s); +void seat_active_vt_changed(Seat *s, int vtnr); +int seat_apply_acls(Seat *s); +int seat_stop(Seat *s); +int seat_save(Seat *s); +int seat_load(Seat *s); + +Session *session_new(Manager *m, User *u, const char *id); +void session_free(Session *s); +int session_activate(Session *s); +bool session_is_active(Session *s); +int session_check_gc(Session *s); +int session_start(Session *s); +int session_stop(Session *s); +int session_save(Session *s); +int session_load(Session *s); + +User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name); +void user_free(User *u); +int user_start(User *u); +int user_stop(User *u); +int user_check_gc(User *u); +UserState user_get_state(User *u); +int user_save(User *u); +int user_load(User *u); + +Manager *manager_new(void); +void manager_free(Manager *m); +int manager_add_device(Manager *m, const char *sysfs, Device **_device); +int manager_add_seat(Manager *m, const char *id, Seat **_seat); +int manager_add_session(Manager *m, User *u, const char *id, Session **_session); +int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user); +int manager_add_user_by_name(Manager *m, const char *name, User **_user); +int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user); +int manager_process_device(Manager *m, struct udev_device *d); +int manager_dispatch_udev(Manager *m); +int manager_dispatch_console(Manager *m); +int manager_enumerate_devices(Manager *m); +int manager_enumerate_seats(Manager *m); +int manager_enumerate_sessions(Manager *m); +int manager_enumerate_users(Manager *m); +int manager_start_one_linger_user(Manager *m, const char *user); +int manager_start_linger_users(Manager *m); +int manager_startup(Manager *m); +int manager_run(Manager *m); +int manager_spawn_autovt(Manager *m, int vtnr); + +const char* session_type_to_string(SessionType t); +SessionType session_type_from_string(const char *s); + +const char* user_state_to_string(UserState s); +UserState user_state_from_string(const char *s); + +bool x11_display_is_local(const char *display); + +#endif -- cgit v1.2.3-54-g00ecf From 5eda94dda25bccda928c4b33c790dbe748573a22 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 24 May 2011 04:20:35 +0200 Subject: logind: implement ACL management --- .gitignore | 1 + Makefile.am | 24 ++++- configure.ac | 38 +++++++ src/logind-acl.c | 282 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/logind-acl.h | 40 ++++++++ src/logind-seat.c | 45 ++++---- src/logind-seat.h | 4 +- src/logind-session.c | 7 +- src/logind.c | 34 ++++++- src/logind.h | 5 +- src/uaccess.c | 87 ++++++++++++++++ 11 files changed, 533 insertions(+), 34 deletions(-) create mode 100644 src/logind-acl.c create mode 100644 src/logind-acl.h create mode 100644 src/uaccess.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index c2820b03e2..7bd22c5211 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-uaccess systemd-logind systemd-hostnamed systemd-binfmt diff --git a/Makefile.am b/Makefile.am index 79ccbbe6e6..f455f21109 100644 --- a/Makefile.am +++ b/Makefile.am @@ -159,7 +159,8 @@ rootlibexec_PROGRAMS = \ systemd-detect-virt \ systemd-sysctl \ systemd-hostnamed \ - systemd-logind + systemd-logind \ + systemd-uaccess if ENABLE_BINFMT rootlibexec_PROGRAMS += \ @@ -813,6 +814,7 @@ systemd_logind_SOURCES = \ src/logind-seat.c \ src/logind-session.c \ src/logind-user.c \ + src/logind-acl.c \ src/dbus-common.c \ src/dbus-loop.c \ src/cgroup-util.c @@ -820,13 +822,29 @@ systemd_logind_SOURCES = \ systemd_logind_CFLAGS = \ $(AM_CFLAGS) \ $(DBUS_CFLAGS) \ - $(UDEV_CFLAGS) + $(UDEV_CFLAGS) \ + $(ACL_CFLAGS) systemd_logind_LDADD = \ libsystemd-basic.la \ libsystemd-daemon.la \ $(DBUS_LIBS) \ - $(UDEV_LIBS) + $(UDEV_LIBS) \ + $(ACL_LIBS) + +systemd_uaccess_SOURCES = \ + src/uaccess.c \ + src/logind-acl.c + +systemd_uaccess_CFLAGS = \ + $(AM_CFLAGS) \ + $(UDEV_CFLAGS) \ + $(ACL_CFLAGS) + +systemd_uaccess_LDADD = \ + libsystemd-basic.la \ + $(UDEV_LIBS) \ + $(ACL_LIBS) systemd_shutdown_SOURCES = \ src/mount-setup.c \ diff --git a/configure.ac b/configure.ac index eb5fb6a975..0dd185b258 100644 --- a/configure.ac +++ b/configure.ac @@ -193,6 +193,43 @@ fi AC_SUBST(PAM_LIBS) AM_CONDITIONAL([HAVE_PAM], [test "x$have_pam" != xno]) +AC_ARG_ENABLE([acl], + AS_HELP_STRING([--disable-acl],[Disable optional ACL support]), + [case "${enableval}" in + yes) have_acl=yes ;; + no) have_acl=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --disable-acl) ;; + esac], + [have_acl=auto]) + +if test "x${have_acl}" != xno ; then + AC_CHECK_HEADERS( + [sys/acl.h acl/libacl.h], + [have_acl=yes], + [if test "x$have_acl" = xyes ; then + AC_MSG_ERROR([*** ACL headers not found.]) + fi]) + + AC_CHECK_LIB( + [acl], + [acl_get_file], + [have_acl=yes], + [if test "x$have_acl" = xyes ; then + AC_MSG_ERROR([*** libacl not found.]) + fi]) + + if test "x$have_acl" = xyes ; then + ACL_LIBS="-lacl" + AC_DEFINE(HAVE_ACL, 1, [ACL available]) + else + have_acl=no + fi +else + ACL_LIBS= +fi +AC_SUBST(ACL_LIBS) +AM_CONDITIONAL([HAVE_ACL], [test "x$have_acl" != xno]) + AC_ARG_ENABLE([audit], AS_HELP_STRING([--disable-audit],[Disable optional AUDIT support]), [case "${enableval}" in @@ -496,6 +533,7 @@ echo " PAM: ${have_pam} AUDIT: ${have_audit} SELinux: ${have_selinux} + ACL: ${have_acl} binfmt: ${have_binfmt} prefix: ${prefix} root dir: ${with_rootdir} diff --git a/src/logind-acl.c b/src/logind-acl.c new file mode 100644 index 0000000000..3df104ff2c --- /dev/null +++ b/src/logind-acl.c @@ -0,0 +1,282 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "logind-acl.h" +#include "util.h" + +static int find_acl(acl_t acl, uid_t uid, acl_entry_t *entry) { + acl_entry_t i; + int found; + + assert(acl); + assert(entry); + + for (found = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); + found > 0; + found = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) { + + acl_tag_t tag; + uid_t *u; + bool b; + + if (acl_get_tag_type(i, &tag) < 0) + return -errno; + + if (tag != ACL_USER) + continue; + + u = acl_get_qualifier(i); + if (!u) + return -errno; + + b = *u == uid; + free(u); + + if (b) { + *entry = i; + return 1; + } + } + + if (found < 0) + return -errno; + + return 0; +} + +static int flush_acl(acl_t acl) { + acl_entry_t i; + int found; + bool changed = false; + + assert(acl); + + for (found = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); + found > 0; + found = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) { + + acl_tag_t tag; + + if (acl_get_tag_type(i, &tag) < 0) + return -errno; + + if (tag != ACL_USER) + continue; + + if (acl_delete_entry(acl, i) < 0) + return -errno; + + changed = true; + } + + if (found < 0) + return -errno; + + return changed; +} + +int devnode_acl(const char *path, + bool flush, + bool del, uid_t old_uid, + bool add, uid_t new_uid) { + + acl_t acl; + int r; + bool changed = false; + + assert(path); + + acl = acl_get_file(path, ACL_TYPE_ACCESS); + if (!acl) + return -errno; + + if (flush) { + + r = flush_acl(acl); + if (r < 0) + goto finish; + if (r > 0) + changed = true; + + } else if (del && old_uid > 0) { + acl_entry_t entry; + + r = find_acl(acl, old_uid, &entry); + if (r < 0) + goto finish; + + if (r > 0) { + if (acl_delete_entry(acl, entry) < 0) { + r = -errno; + goto finish; + } + + changed = true; + } + } + + if (add && new_uid > 0) { + acl_entry_t entry; + acl_permset_t permset; + int rd, wt; + + r = find_acl(acl, new_uid, &entry); + if (r < 0) + goto finish; + + if (r == 0) { + if (acl_create_entry(&acl, &entry) < 0) { + r = -errno; + goto finish; + } + + if (acl_set_tag_type(entry, ACL_USER) < 0 || + acl_set_qualifier(entry, &new_uid) < 0) { + r = -errno; + goto finish; + } + } + + if (acl_get_permset(entry, &permset) < 0) { + r = -errno; + goto finish; + } + + rd = acl_get_perm(permset, ACL_READ); + if (rd < 0) { + r = -errno; + goto finish; + } + + wt = acl_get_perm(permset, ACL_WRITE); + if (wt < 0) { + r = -errno; + goto finish; + } + + if (!rd || !wt) { + + if (acl_add_perm(permset, ACL_READ|ACL_WRITE) < 0) { + r = -errno; + goto finish; + } + + changed = true; + } + } + + if (!changed) + goto finish; + + if (acl_calc_mask(&acl) < 0) { + r = -errno; + goto finish; + } + + if (acl_set_file(path, ACL_TYPE_ACCESS, acl) < 0) { + r = -errno; + goto finish; + } + + r = 0; + +finish: + acl_free(acl); + + return r; +} + +int devnode_acl_all(struct udev *udev, + const char *seat, + bool flush, + bool del, uid_t old_uid, + bool add, uid_t new_uid) { + + struct udev_list_entry *item = NULL, *first = NULL; + struct udev_enumerate *e; + int r; + + assert(udev); + + if (!seat) + seat = "seat0"; + + e = udev_enumerate_new(udev); + if (!e) + return -ENOMEM; + + r = udev_enumerate_add_match_tag(e, "uaccess"); + if (r < 0) + goto finish; + + r = udev_enumerate_add_match_tag(e, seat); + if (r < 0) + goto finish; + + r = udev_enumerate_scan_devices(e); + if (r < 0) + goto finish; + + first = udev_enumerate_get_list_entry(e); + udev_list_entry_foreach(item, first) { + struct udev_device *d; + const char *node, *sn; + + d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); + if (!d) { + r = -ENOMEM; + goto finish; + } + + sn = udev_device_get_property_value(d, "SEAT"); + if (!sn) + sn = "seat0"; + + if (!streq(seat, sn)) { + udev_device_unref(d); + continue; + } + + node = udev_device_get_devnode(d); + udev_device_unref(d); + + if (!node) { + r = -ENOMEM; + goto finish; + } + + r = devnode_acl(node, flush, del, old_uid, add, new_uid); + if (r < 0) + goto finish; + } + +finish: + if (e) + udev_enumerate_unref(e); + + return r; +} diff --git a/src/logind-acl.h b/src/logind-acl.h new file mode 100644 index 0000000000..9c88a80644 --- /dev/null +++ b/src/logind-acl.h @@ -0,0 +1,40 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foologindaclhfoo +#define foologindaclhfoo + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +int devnode_acl(const char *path, + bool flush, + bool del, uid_t old_uid, + bool add, uid_t new_uid); + +int devnode_acl_all(struct udev *udev, + const char *seat, + bool flush, + bool del, uid_t old_uid, + bool add, uid_t new_uid); + +#endif diff --git a/src/logind-seat.c b/src/logind-seat.c index 315490043d..743ded66bd 100644 --- a/src/logind-seat.c +++ b/src/logind-seat.c @@ -25,8 +25,10 @@ #include #include #include +#include #include "logind-seat.h" +#include "logind-acl.h" #include "util.h" Seat *seat_new(Manager *m, const char *id) { @@ -179,40 +181,32 @@ int seat_preallocate_vts(Seat *s) { return r; } -int seat_apply_acls(Seat *s) { - assert(s); - - - return 0; -} - -static int vt_is_busy(int vtnr) { - struct vt_stat vt_stat; - int r = 0, fd; - - assert(vtnr >= 1); +int seat_apply_acls(Seat *s, Session *old_active) { + int r; - fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC); - if (fd < 0) - return -errno; + assert(s); - if (ioctl(fd, VT_GETSTATE, &vt_stat) < 0) - r = -errno; - else - r = !!(vt_stat.v_state & (1 << vtnr)); + r = devnode_acl_all(s->manager->udev, + s->id, + false, + !!old_active, old_active ? old_active->user->uid : 0, + !!s->active, s->active ? s->active->user->uid : 0); - close_nointr_nofail(fd); + if (r < 0) + log_error("Failed to apply ACLs: %s", strerror(-r)); return r; } -void seat_active_vt_changed(Seat *s, int vtnr) { +int seat_active_vt_changed(Seat *s, int vtnr) { Session *i; + Session *old_active; assert(s); assert(vtnr >= 1); assert(s->manager->vtconsole == s); + old_active = s->active; s->active = NULL; LIST_FOREACH(sessions_by_seat, i, s->sessions) @@ -221,10 +215,13 @@ void seat_active_vt_changed(Seat *s, int vtnr) { break; } - seat_apply_acls(s); + if (old_active == s->active) + return 0; + + seat_apply_acls(s, old_active); + manager_spawn_autovt(s->manager, vtnr); - if (vt_is_busy(vtnr) == 0) - manager_spawn_autovt(s->manager, vtnr); + return 0; } int seat_stop(Seat *s) { diff --git a/src/logind-seat.h b/src/logind-seat.h index 2fe7949bd9..4b6b3401d2 100644 --- a/src/logind-seat.h +++ b/src/logind-seat.h @@ -45,8 +45,8 @@ struct Seat { Seat *seat_new(Manager *m, const char *id); void seat_free(Seat *s); int seat_preallocate_vts(Seat *s); -void seat_active_vt_changed(Seat *s, int vtnr); -int seat_apply_acls(Seat *s); +int seat_active_vt_changed(Seat *s, int vtnr); +int seat_apply_acls(Seat *s, Session *old_active); int seat_stop(Seat *s); int seat_save(Seat *s); int seat_load(Seat *s); diff --git a/src/logind-session.c b/src/logind-session.c index 7bdf487e8d..c10f5e62c9 100644 --- a/src/logind-session.c +++ b/src/logind-session.c @@ -174,6 +174,7 @@ int session_load(Session *s) { int session_activate(Session *s) { int r; + Session *old_active; assert(s); @@ -192,9 +193,13 @@ int session_activate(Session *s) { if (r < 0) return r; + old_active = s->seat->active; s->seat->active = s; - return seat_apply_acls(s->seat); + seat_apply_acls(s->seat, old_active); + manager_spawn_autovt(s->manager, s->vtnr); + + return 0; } bool x11_display_is_local(const char *display) { diff --git a/src/logind.c b/src/logind.c index 0c26aaddbf..7e9b706d13 100644 --- a/src/logind.c +++ b/src/logind.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include "logind.h" #include "dbus-common.h" @@ -290,7 +292,7 @@ int manager_enumerate_devices(Manager *m) { e = udev_enumerate_new(m->udev); if (!e) - return -ENOMEM; + goto finish; if (udev_enumerate_add_match_subsystem(e, "graphics") < 0) goto finish; @@ -627,9 +629,37 @@ int manager_dispatch_console(Manager *m) { return 0; } +static int vt_is_busy(int vtnr) { + struct vt_stat vt_stat; + int r = 0, fd; + + assert(vtnr >= 1); + + fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC); + if (fd < 0) + return -errno; + + if (ioctl(fd, VT_GETSTATE, &vt_stat) < 0) + r = -errno; + else + r = !!(vt_stat.v_state & (1 << vtnr)); + + close_nointr_nofail(fd); + + return r; +} + int manager_spawn_autovt(Manager *m, int vtnr) { + int r; + assert(m); + r = vt_is_busy(vtnr); + if (r != 0) + return r; + + /* ... */ + return 0; } @@ -849,7 +879,7 @@ int manager_run(Manager *m) { int main(int argc, char *argv[]) { Manager *m = NULL; - int r = 0; + int r; log_set_target(LOG_TARGET_AUTO); log_parse_environment(); diff --git a/src/logind.h b/src/logind.h index 0d3bd899b2..be5dab7464 100644 --- a/src/logind.h +++ b/src/logind.h @@ -36,11 +36,12 @@ * * recreate VTs when disallocated * udev rules + * PAM rewrite * spawn user systemd + * dbus API + * * non-local X11 server - * udev-acl * reboot/shutdown halt management - * PAM rewrite */ typedef struct Manager Manager; diff --git a/src/uaccess.c b/src/uaccess.c new file mode 100644 index 0000000000..e55ab51f34 --- /dev/null +++ b/src/uaccess.c @@ -0,0 +1,87 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "logind-acl.h" +#include "util.h" +#include "log.h" + +int main(int argc, char *argv[]) { + int r; + const char *path, *seat; + char *p, *active_uid = NULL; + unsigned long ul; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + if (argc != 2) { + log_error("This program expects two argument."); + r = -EINVAL; + goto finish; + } + + path = argv[1]; + seat = argv[2]; + + p = strappend("/run/systemd/seat/", seat); + if (!p) { + log_error("Out of memory."); + goto finish; + } + + r = parse_env_file(p, NEWLINE, + "ACTIVE_UID", &active_uid, + NULL); + free(p); + + if (r < 0) { + if (errno == ENOENT) { + r = 0; + goto finish; + } + + log_error("Failed to read seat data for %s: %s", seat, strerror(-r)); + goto finish; + } + + r = safe_atolu(active_uid, &ul); + if (r < 0) { + log_error("Failed to parse active UID value %s: %s", active_uid, strerror(-r)); + goto finish; + } + + r = devnode_acl(path, true, false, 0, true, (uid_t) ul); + if (r < 0) { + log_error("Failed to apply ACL on %s: %s", path, strerror(-r)); + goto finish; + } + + r = 0; + +finish: + free(active_uid); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} -- cgit v1.2.3-54-g00ecf From f401e48c2db22ff9d1a05885b5599bebf19c2707 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 15 Jun 2011 15:37:39 +0200 Subject: mechanisms: add mechanisms to change system locale and clock --- .gitignore | 2 + Makefile.am | 53 ++- src/hostnamed.c | 163 +-------- src/localed.c | 599 ++++++++++++++++++++++++++++++++++ src/logind-seat-dbus.c | 30 +- src/logind-session-dbus.c | 10 + src/logind.h | 2 - src/org.freedesktop.locale1.conf | 27 ++ src/org.freedesktop.locale1.policy | 29 ++ src/org.freedesktop.locale1.service | 12 + src/org.freedesktop.login1.conf | 1 - src/org.freedesktop.timedate1.conf | 27 ++ src/org.freedesktop.timedate1.policy | 50 +++ src/org.freedesktop.timedate1.service | 12 + src/polkit.c | 190 +++++++++++ src/polkit.h | 35 ++ src/timedated.c | 544 ++++++++++++++++++++++++++++++ units/.gitignore | 2 + units/systemd-localed.service.in | 17 + units/systemd-timedated.service.in | 17 + 20 files changed, 1648 insertions(+), 174 deletions(-) create mode 100644 src/localed.c create mode 100644 src/org.freedesktop.locale1.conf create mode 100644 src/org.freedesktop.locale1.policy create mode 100644 src/org.freedesktop.locale1.service create mode 100644 src/org.freedesktop.timedate1.conf create mode 100644 src/org.freedesktop.timedate1.policy create mode 100644 src/org.freedesktop.timedate1.service create mode 100644 src/polkit.c create mode 100644 src/polkit.h create mode 100644 src/timedated.c create mode 100644 units/systemd-localed.service.in create mode 100644 units/systemd-timedated.service.in (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 7bd22c5211..92b8f73fac 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +systemd-localed +systemd-timedated systemd-uaccess systemd-logind systemd-hostnamed diff --git a/Makefile.am b/Makefile.am index 0bc51692d1..0439957ee6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -159,6 +159,8 @@ rootlibexec_PROGRAMS = \ systemd-detect-virt \ systemd-sysctl \ systemd-hostnamed \ + systemd-localed \ + systemd-timedated \ systemd-logind \ systemd-uaccess @@ -199,11 +201,15 @@ dist_pkgsysconf_DATA = \ dist_dbuspolicy_DATA = \ src/org.freedesktop.systemd1.conf \ - src/org.freedesktop.hostname1.conf + src/org.freedesktop.hostname1.conf \ + src/org.freedesktop.locale1.conf \ + src/org.freedesktop.timedate1.conf dist_dbussystemservice_DATA = \ src/org.freedesktop.systemd1.service \ - src/org.freedesktop.hostname1.service + src/org.freedesktop.hostname1.service \ + src/org.freedesktop.locale1.service \ + src/org.freedesktop.timedate1.service dist_udevrules_DATA = \ src/99-systemd.rules @@ -308,6 +314,8 @@ nodist_systemunit_DATA = \ units/systemd-logger.service \ units/systemd-shutdownd.service \ units/systemd-hostnamed.service \ + units/systemd-localed.service \ + units/systemd-timedated.service \ units/systemd-kmsg-syslogd.service \ units/systemd-modules-load.service \ units/systemd-vconsole-setup.service \ @@ -355,6 +363,8 @@ EXTRA_DIST = \ units/systemd-logger.service.in \ units/systemd-shutdownd.service.in \ units/systemd-hostnamed.service.in \ + units/systemd-localed.service.in \ + units/systemd-timedated.service.in \ units/systemd-kmsg-syslogd.service.in \ units/systemd-modules-load.service.in \ units/systemd-vconsole-setup.service.in \ @@ -443,7 +453,9 @@ nodist_polkitpolicy_DATA = \ src/org.freedesktop.systemd1.policy dist_polkitpolicy_DATA = \ - src/org.freedesktop.hostname1.policy + src/org.freedesktop.hostname1.policy \ + src/org.freedesktop.locale1.policy \ + src/org.freedesktop.timedate1.policy noinst_LTLIBRARIES = \ libsystemd-basic.la \ @@ -797,7 +809,8 @@ systemd_shutdownd_LDADD = \ systemd_hostnamed_SOURCES = \ src/hostnamed.c \ - src/dbus-common.c + src/dbus-common.c \ + src/polkit.c systemd_hostnamed_CFLAGS = \ $(AM_CFLAGS) \ @@ -808,6 +821,34 @@ systemd_hostnamed_LDADD = \ libsystemd-daemon.la \ $(DBUS_LIBS) +systemd_localed_SOURCES = \ + src/localed.c \ + src/dbus-common.c \ + src/polkit.c + +systemd_localed_CFLAGS = \ + $(AM_CFLAGS) \ + $(DBUS_CFLAGS) + +systemd_localed_LDADD = \ + libsystemd-basic.la \ + libsystemd-daemon.la \ + $(DBUS_LIBS) + +systemd_timedated_SOURCES = \ + src/timedated.c \ + src/dbus-common.c \ + src/polkit.c + +systemd_timedated_CFLAGS = \ + $(AM_CFLAGS) \ + $(DBUS_CFLAGS) + +systemd_timedated_LDADD = \ + libsystemd-basic.la \ + libsystemd-daemon.la \ + $(DBUS_LIBS) + systemd_logind_SOURCES = \ src/logind.c \ src/logind-dbus.c \ @@ -1449,7 +1490,9 @@ endif rm -f default.target ctrl-alt-del.target dbus-org.freedesktop.hostname1.service && \ $(LN_S) graphical.target default.target && \ $(LN_S) reboot.target ctrl-alt-del.target && \ - $(LN_S) systemd-hostnamed.service dbus-org.freedesktop.hostname1.service ) + $(LN_S) systemd-hostnamed.service dbus-org.freedesktop.hostname1.service && \ + $(LN_S) systemd-localed.service dbus-org.freedesktop.locale1.service && \ + $(LN_S) systemd-timedate.service dbus-org.freedesktop.timedate1.service ) ( cd $(DESTDIR)$(systemunitdir)/multi-user.target.wants && \ rm -f getty.target systemd-user-sessions.service systemd-ask-password-wall.path && \ $(LN_S) ../getty.target getty.target && \ diff --git a/src/hostnamed.c b/src/hostnamed.c index 68c5715b0e..f579e112ad 100644 --- a/src/hostnamed.c +++ b/src/hostnamed.c @@ -29,6 +29,7 @@ #include "util.h" #include "strv.h" #include "dbus-common.h" +#include "polkit.h" #define INTROSPECTION \ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ @@ -256,168 +257,6 @@ static int write_data_other(void) { return r; } -/* This mimics dbus_bus_get_unix_user() */ -static pid_t get_unix_process_id( - DBusConnection *connection, - const char *name, - DBusError *error) { - - DBusMessage *m = NULL, *reply = NULL; - uint32_t pid = 0; - - m = dbus_message_new_method_call( - DBUS_SERVICE_DBUS, - DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS, - "GetConnectionUnixProcessID"); - if (!m) { - dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL); - goto finish; - } - - if (!dbus_message_append_args( - m, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID)) { - dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL); - goto finish; - } - - reply = dbus_connection_send_with_reply_and_block(connection, m, -1, error); - if (!reply) - goto finish; - - if (dbus_set_error_from_message(error, reply)) - goto finish; - - if (!dbus_message_get_args( - reply, error, - DBUS_TYPE_UINT32, &pid, - DBUS_TYPE_INVALID)) - goto finish; - -finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - - return (pid_t) pid; -} - -static int verify_polkit( - DBusConnection *c, - DBusMessage *request, - const char *action, - bool interactive, - DBusError *error) { - - DBusMessage *m = NULL, *reply = NULL; - const char *unix_process = "unix-process", *pid = "pid", *starttime = "start-time", *cancel_id = ""; - const char *sender; - uint32_t flags = interactive ? 1 : 0; - pid_t pid_raw; - uint32_t pid_u32; - unsigned long long starttime_raw; - uint64_t starttime_u64; - DBusMessageIter iter_msg, iter_struct, iter_array, iter_dict, iter_variant; - int r; - dbus_bool_t authorized = FALSE; - - assert(c); - assert(request); - - sender = dbus_message_get_sender(request); - if (!sender) - return -EINVAL; - - pid_raw = get_unix_process_id(c, sender, error); - if (pid_raw == 0) - return -EINVAL; - - r = get_starttime_of_pid(pid_raw, &starttime_raw); - if (r < 0) - return r; - - m = dbus_message_new_method_call( - "org.freedesktop.PolicyKit1", - "/org/freedesktop/PolicyKit1/Authority", - "org.freedesktop.PolicyKit1.Authority", - "CheckAuthorization"); - if (!m) - return -ENOMEM; - - dbus_message_iter_init_append(m, &iter_msg); - - pid_u32 = (uint32_t) pid_raw; - starttime_u64 = (uint64_t) starttime_raw; - - if (!dbus_message_iter_open_container(&iter_msg, DBUS_TYPE_STRUCT, NULL, &iter_struct) || - !dbus_message_iter_append_basic(&iter_struct, DBUS_TYPE_STRING, &unix_process) || - !dbus_message_iter_open_container(&iter_struct, DBUS_TYPE_ARRAY, "{sv}", &iter_array) || - !dbus_message_iter_open_container(&iter_array, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict) || - !dbus_message_iter_append_basic(&iter_dict, DBUS_TYPE_STRING, &pid) || - !dbus_message_iter_open_container(&iter_dict, DBUS_TYPE_VARIANT, "u", &iter_variant) || - !dbus_message_iter_append_basic(&iter_variant, DBUS_TYPE_UINT32, &pid_u32) || - !dbus_message_iter_close_container(&iter_dict, &iter_variant) || - !dbus_message_iter_close_container(&iter_array, &iter_dict) || - !dbus_message_iter_open_container(&iter_array, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict) || - !dbus_message_iter_append_basic(&iter_dict, DBUS_TYPE_STRING, &starttime) || - !dbus_message_iter_open_container(&iter_dict, DBUS_TYPE_VARIANT, "t", &iter_variant) || - !dbus_message_iter_append_basic(&iter_variant, DBUS_TYPE_UINT64, &starttime_u64) || - !dbus_message_iter_close_container(&iter_dict, &iter_variant) || - !dbus_message_iter_close_container(&iter_array, &iter_dict) || - !dbus_message_iter_close_container(&iter_struct, &iter_array) || - !dbus_message_iter_close_container(&iter_msg, &iter_struct) || - !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_STRING, &action) || - !dbus_message_iter_open_container(&iter_msg, DBUS_TYPE_ARRAY, "{ss}", &iter_array) || - !dbus_message_iter_close_container(&iter_msg, &iter_array) || - !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_UINT32, &flags) || - !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_STRING, &cancel_id)) { - r = -ENOMEM; - goto finish; - } - - reply = dbus_connection_send_with_reply_and_block(c, m, -1, error); - if (!reply) { - r = -EIO; - goto finish; - } - - if (dbus_set_error_from_message(error, reply)) { - r = -EIO; - goto finish; - } - - if (!dbus_message_iter_init(reply, &iter_msg) || - dbus_message_iter_get_arg_type(&iter_msg) != DBUS_TYPE_STRUCT) { - r = -EIO; - goto finish; - } - - dbus_message_iter_recurse(&iter_msg, &iter_struct); - - if (dbus_message_iter_get_arg_type(&iter_struct) != DBUS_TYPE_BOOLEAN) { - r = -EIO; - goto finish; - } - - dbus_message_iter_get_basic(&iter_struct, &authorized); - - r = authorized ? 0 : -EPERM; - -finish: - - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - - return r; -} - static int bus_hostname_append_icon_name(DBusMessageIter *i, const char *property, void *userdata) { const char *name; diff --git a/src/localed.c b/src/localed.c new file mode 100644 index 0000000000..0fbe74787a --- /dev/null +++ b/src/localed.c @@ -0,0 +1,599 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include + +#include +#include +#include + +#include "util.h" +#include "strv.h" +#include "dbus-common.h" +#include "polkit.h" + +#define INTROSPECTION \ + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ + "\n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + BUS_PROPERTIES_INTERFACE \ + BUS_INTROSPECTABLE_INTERFACE \ + BUS_PEER_INTERFACE \ + "\n" + +#define INTERFACES_LIST \ + BUS_GENERIC_INTERFACES_LIST \ + "org.freedesktop.locale1\0" + +enum { + /* We don't list LC_ALL here on purpose. People should be + * using LANG instead. */ + + PROP_LANG, + PROP_LC_CTYPE, + PROP_LC_NUMERIC, + PROP_LC_TIME, + PROP_LC_COLLATE, + PROP_LC_MONETARY, + PROP_LC_MESSAGES, + PROP_LC_PAPER, + PROP_LC_NAME, + PROP_LC_ADDRESS, + PROP_LC_TELEPHONE, + PROP_LC_MEASUREMENT, + PROP_LC_IDENTIFICATION, + _PROP_MAX +}; + +static const char * const names[_PROP_MAX] = { + [PROP_LANG] = "LANG", + [PROP_LC_CTYPE] = "LC_CTYPE", + [PROP_LC_NUMERIC] = "LC_NUMERIC", + [PROP_LC_TIME] = "LC_TIME", + [PROP_LC_COLLATE] = "LC_COLLATE", + [PROP_LC_MONETARY] = "LC_MONETARY", + [PROP_LC_MESSAGES] = "LC_MESSAGES", + [PROP_LC_PAPER] = "LC_PAPER", + [PROP_LC_NAME] = "LC_NAME", + [PROP_LC_ADDRESS] = "LC_ADDRESS", + [PROP_LC_TELEPHONE] = "LC_TELEPHONE", + [PROP_LC_MEASUREMENT] = "LC_MEASUREMENT", + [PROP_LC_IDENTIFICATION] = "LC_IDENTIFICATION" +}; + +static char *data[_PROP_MAX] = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +static void free_data(void) { + int p; + + for (p = 0; p < _PROP_MAX; p++) { + free(data[p]); + data[p] = NULL; + } +} + +static void simplify(void) { + int p; + + for (p = 1; p < _PROP_MAX; p++) + if (isempty(data[p]) || streq_ptr(data[PROP_LANG], data[p])) { + free(data[p]); + data[p] = NULL; + } +} + +static int read_data(void) { + int r; + + free_data(); + + r = parse_env_file("/etc/locale.conf", NEWLINE, + "LANG", &data[PROP_LANG], + "LC_CTYPE", &data[PROP_LC_CTYPE], + "LC_NUMERIC", &data[PROP_LC_NUMERIC], + "LC_TIME", &data[PROP_LC_TIME], + "LC_COLLATE", &data[PROP_LC_COLLATE], + "LC_MONETARY", &data[PROP_LC_MONETARY], + "LC_MESSAGES", &data[PROP_LC_MESSAGES], + "LC_PAPER", &data[PROP_LC_PAPER], + "LC_NAME", &data[PROP_LC_NAME], + "LC_ADDRESS", &data[PROP_LC_ADDRESS], + "LC_TELEPHONE", &data[PROP_LC_TELEPHONE], + "LC_MEASUREMENT", &data[PROP_LC_MEASUREMENT], + "LC_IDENTIFICATION", &data[PROP_LC_IDENTIFICATION], + NULL); + + if (r == -ENOENT) { + int p; + + /* Fill in what we got passed from systemd. */ + + for (p = 0; p < _PROP_MAX; p++) { + char *e, *d; + + assert(names[p]); + + e = getenv(names[p]); + if (e) { + d = strdup(e); + if (!d) + return -ENOMEM; + } else + d = NULL; + + free(data[p]); + data[p] = d; + } + + r = 0; + } + + simplify(); + return r; +} + +static int write_data(void) { + int r, p; + char **l = NULL; + + r = load_env_file("/etc/locale.conf", &l); + if (r < 0 && r != -ENOENT) + return r; + + for (p = 0; p < _PROP_MAX; p++) { + char *t, **u; + + assert(names[p]); + + if (isempty(data[p])) { + l = strv_env_unset(l, names[p]); + continue; + } + + if (asprintf(&t, "%s=%s", names[p], data[p]) < 0) { + strv_free(l); + return -ENOMEM; + } + + u = strv_env_set(l, t); + free(t); + strv_free(l); + + if (!u) + return -ENOMEM; + + l = u; + } + + if (strv_isempty(l)) { + + if (unlink("/etc/locale.conf") < 0) + return errno == ENOENT ? 0 : -errno; + + return 0; + } + + r = write_env_file("/etc/locale.conf", l); + strv_free(l); + + return r; +} + +static void push_data(DBusConnection *bus) { + char **l_set = NULL, **l_unset = NULL, **t; + int c_set = 0, c_unset = 0, p; + DBusError error; + DBusMessage *m = NULL, *reply = NULL; + DBusMessageIter iter, sub; + + dbus_error_init(&error); + + assert(bus); + + l_set = new0(char*, _PROP_MAX); + l_unset = new0(char*, _PROP_MAX); + if (!l_set || !l_unset) { + log_error("Out of memory"); + goto finish; + } + + for (p = 0; p < _PROP_MAX; p++) { + assert(names[p]); + + if (isempty(data[p])) + l_unset[c_set++] = (char*) names[p]; + else { + char *s; + + if (asprintf(&s, "%s=%s", names[p], data[p]) < 0) { + log_error("Out of memory"); + goto finish; + } + + l_set[c_unset++] = s; + } + } + + assert(c_set + c_unset == _PROP_MAX); + m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnsetAndSetEnvironment"); + if (!m) { + log_error("Could not allocate message."); + goto finish; + } + + dbus_message_iter_init_append(m, &iter); + + if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) { + log_error("Out of memory."); + goto finish; + } + + STRV_FOREACH(t, l_unset) + if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, t)) { + log_error("Out of memory."); + goto finish; + } + + if (!dbus_message_iter_close_container(&iter, &sub) || + !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) { + log_error("Out of memory."); + goto finish; + } + + STRV_FOREACH(t, l_set) + if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, t)) { + log_error("Out of memory."); + goto finish; + } + + if (!dbus_message_iter_close_container(&iter, &sub)) { + log_error("Out of memory."); + goto finish; + } + + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); + if (!reply) { + log_error("Failed to set locale information: %s", bus_error_message(&error)); + goto finish; + } + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); + + strv_free(l_set); + free(l_unset); +} + +static int append_locale(DBusMessageIter *i, const char *property, void *userdata) { + int r, c = 0, p; + char **l; + + l = new0(char*, _PROP_MAX+1); + if (!l) + return -ENOMEM; + + for (p = 0; p < _PROP_MAX; p++) { + char *t; + + if (isempty(data[p])) + continue; + + if (asprintf(&t, "%s=%s", names[p], data[p]) < 0) { + strv_free(l); + return -ENOMEM; + } + + l[c++] = t; + } + + r = bus_property_append_strv(i, property, (void*) l); + strv_free(l); + + return r; +} + +static DBusHandlerResult locale_message_handler( + DBusConnection *connection, + DBusMessage *message, + void *userdata) { + + const BusProperty properties[] = { + { "org.freedesktop.locale1", "Locale", append_locale, "as", NULL}, + { NULL, NULL, NULL, NULL, NULL } + }; + + DBusMessage *reply = NULL, *changed = NULL; + DBusError error; + int r; + + assert(connection); + assert(message); + + dbus_error_init(&error); + + if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetLocale")) { + char **l = NULL, **i; + dbus_bool_t interactive; + DBusMessageIter iter; + bool modified = false; + bool passed[_PROP_MAX]; + int p; + + if (!dbus_message_iter_init(message, &iter)) + return bus_send_error_reply(connection, message, NULL, -EINVAL); + + r = bus_parse_strv_iter(&iter, &l); + if (r < 0) { + if (r == -ENOMEM) + goto oom; + + return bus_send_error_reply(connection, message, NULL, r); + } + + if (!dbus_message_iter_next(&iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN) { + strv_free(l); + return bus_send_error_reply(connection, message, NULL, -EINVAL); + } + + dbus_message_iter_get_basic(&iter, &interactive); + + zero(passed); + + /* Check whether a variable changed and if so valid */ + STRV_FOREACH(i, l) { + bool valid = false; + + for (p = 0; p < _PROP_MAX; p++) { + size_t k; + + k = strlen(names[p]); + if (startswith(*i, names[p]) && (*i)[k] == '=') { + valid = true; + passed[p] = true; + + if (!streq_ptr(*i + k + 1, data[p])) + modified = true; + + break; + } + } + + if (!valid) { + strv_free(l); + return bus_send_error_reply(connection, message, NULL, -EINVAL); + } + } + + /* Check whether a variable is unset */ + if (!modified) { + for (p = 0; p < _PROP_MAX; p++) + if (!isempty(data[p]) && !passed[p]) { + modified = true; + break; + } + } + + if (modified) { + + r = verify_polkit(connection, message, "org.freedesktop.locale1.set-locale", interactive, &error); + if (r < 0) { + strv_free(l); + return bus_send_error_reply(connection, message, &error, r); + } + + STRV_FOREACH(i, l) { + for (p = 0; p < _PROP_MAX; p++) { + size_t k; + + k = strlen(names[p]); + if (startswith(*i, names[p]) && (*i)[k] == '=') { + char *t; + + t = strdup(*i + k + 1); + if (!t) { + strv_free(l); + goto oom; + } + + free(data[p]); + data[p] = t; + + break; + } + } + } + + for (p = 0; p < _PROP_MAX; p++) { + if (passed[p]) + continue; + + free(data[p]); + data[p] = NULL; + } + + simplify(); + + r = write_data(); + if (r < 0) { + log_error("Failed to set locale: %s", strerror(-r)); + return bus_send_error_reply(connection, message, NULL, r); + } + + push_data(connection); + + log_info("Changed locale information."); + + changed = bus_properties_changed_new( + "/org/freedesktop/locale1", + "org.freedesktop.locale1", + "Locale\0"); + if (!changed) + goto oom; + } + + } else + return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties); + + if (!(reply = dbus_message_new_method_return(message))) + goto oom; + + if (!dbus_connection_send(connection, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + reply = NULL; + + if (changed) { + + if (!dbus_connection_send(connection, changed, NULL)) + goto oom; + + dbus_message_unref(changed); + } + + return DBUS_HANDLER_RESULT_HANDLED; + +oom: + if (reply) + dbus_message_unref(reply); + + if (changed) + dbus_message_unref(changed); + + dbus_error_free(&error); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; +} + +static int connect_bus(DBusConnection **_bus) { + static const DBusObjectPathVTable locale_vtable = { + .message_function = locale_message_handler + }; + DBusError error; + DBusConnection *bus = NULL; + int r; + + assert(_bus); + + dbus_error_init(&error); + + bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); + if (!bus) { + log_error("Failed to get system D-Bus connection: %s", error.message); + r = -ECONNREFUSED; + goto fail; + } + + if (!dbus_connection_register_object_path(bus, "/org/freedesktop/locale1", &locale_vtable, NULL)) { + log_error("Not enough memory"); + r = -ENOMEM; + goto fail; + } + + if (dbus_bus_request_name(bus, "org.freedesktop.locale1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) < 0) { + log_error("Failed to register name on bus: %s", error.message); + r = -EEXIST; + goto fail; + } + + if (_bus) + *_bus = bus; + + return 0; + +fail: + dbus_connection_close(bus); + dbus_connection_unref(bus); + + dbus_error_free(&error); + + return r; +} + +int main(int argc, char *argv[]) { + int r; + DBusConnection *bus = NULL; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + if (argc != 1) { + log_error("This program takes no arguments."); + r = -EINVAL; + goto finish; + } + + umask(0022); + + r = read_data(); + if (r < 0) { + log_error("Failed to read locale data: %s", strerror(-r)); + goto finish; + } + + r = connect_bus(&bus); + if (r < 0) + goto finish; + + while (dbus_connection_read_write_dispatch(bus, -1)) + ; + + r = 0; + +finish: + free_data(); + + if (bus) { + dbus_connection_flush(bus); + dbus_connection_close(bus); + dbus_connection_unref(bus); + } + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/logind-seat-dbus.c b/src/logind-seat-dbus.c index 63b1bd5ed8..4937d65f74 100644 --- a/src/logind-seat-dbus.c +++ b/src/logind-seat-dbus.c @@ -29,8 +29,12 @@ #define BUS_SEAT_INTERFACE \ " \n" \ " \n" \ + " \n" \ + " \n" \ + " \n" \ " \n" \ - " \n" \ + " \n" \ + " \n" \ " \n" \ " \n" \ @@ -125,6 +129,23 @@ static int bus_seat_append_sessions(DBusMessageIter *i, const char *property, vo return 0; } + +static int bus_seat_append_can_activate(DBusMessageIter *i, const char *property, void *data) { + Seat *s = data; + dbus_bool_t b; + + assert(i); + assert(property); + assert(s); + + b = s->manager->vtconsole == s; + + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b)) + return -ENOMEM; + + return 0; +} + static int get_seat_for_path(Manager *m, const char *path, Seat **_s) { Seat *s; char *id; @@ -156,9 +177,10 @@ static DBusHandlerResult seat_message_dispatch( DBusMessage *message) { const BusProperty properties[] = { - { "org.freedesktop.login1.Seat", "Id", bus_property_append_string, "s", s->id }, - { "org.freedesktop.login1.Seat", "Active", bus_seat_append_active, "(so)", s }, - { "org.freedesktop.login1.Seat", "Sessions", bus_seat_append_sessions, "a(so)", s }, + { "org.freedesktop.login1.Seat", "Id", bus_property_append_string, "s", s->id }, + { "org.freedesktop.login1.Seat", "ActiveSession", bus_seat_append_active, "(so)", s }, + { "org.freedesktop.login1.Seat", "CanActivateSessions", bus_seat_append_can_activate, "b", s }, + { "org.freedesktop.login1.Seat", "Sessions", bus_seat_append_sessions, "a(so)", s }, { NULL, NULL, NULL, NULL, NULL } }; diff --git a/src/logind-session-dbus.c b/src/logind-session-dbus.c index 41af65858e..539384b7d4 100644 --- a/src/logind-session-dbus.c +++ b/src/logind-session-dbus.c @@ -30,9 +30,16 @@ " \n" \ " \n" \ " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ " \n" \ " \n" \ " \n" \ + " \n" \ + " \n" \ " \n" \ " \n" \ " \n" \ @@ -48,6 +55,9 @@ " \n" \ " \n" \ " \n" \ + " \n" \ + " \n" \ + " \n" \ " \n" #define INTROSPECTION \ diff --git a/src/logind.h b/src/logind.h index e4b7a3c4e0..95db35ddfd 100644 --- a/src/logind.h +++ b/src/logind.h @@ -35,11 +35,9 @@ /* TODO: * * recreate VTs when disallocated - * udev rules * PAM rewrite * spawn user systemd * dbus API - * don't allow everybody to take logind name * * non-local X11 server * reboot/shutdown halt management diff --git a/src/org.freedesktop.locale1.conf b/src/org.freedesktop.locale1.conf new file mode 100644 index 0000000000..68273311e7 --- /dev/null +++ b/src/org.freedesktop.locale1.conf @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/org.freedesktop.locale1.policy b/src/org.freedesktop.locale1.policy new file mode 100644 index 0000000000..6c755fd6b7 --- /dev/null +++ b/src/org.freedesktop.locale1.policy @@ -0,0 +1,29 @@ + + + + + + + + The systemd Project + http://www.freedesktop.org/wiki/Software/systemd + + + Set system locale + Authentication is required to set the system locale. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + diff --git a/src/org.freedesktop.locale1.service b/src/org.freedesktop.locale1.service new file mode 100644 index 0000000000..29bd582459 --- /dev/null +++ b/src/org.freedesktop.locale1.service @@ -0,0 +1,12 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +[D-BUS Service] +Name=org.freedesktop.locale1 +Exec=/bin/false +User=root +SystemdService=dbus-org.freedesktop.locale1.service diff --git a/src/org.freedesktop.login1.conf b/src/org.freedesktop.login1.conf index 20008ea4aa..ebc499da67 100644 --- a/src/org.freedesktop.login1.conf +++ b/src/org.freedesktop.login1.conf @@ -20,7 +20,6 @@ - diff --git a/src/org.freedesktop.timedate1.conf b/src/org.freedesktop.timedate1.conf new file mode 100644 index 0000000000..c9c221b644 --- /dev/null +++ b/src/org.freedesktop.timedate1.conf @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/org.freedesktop.timedate1.policy b/src/org.freedesktop.timedate1.policy new file mode 100644 index 0000000000..5010efd6c5 --- /dev/null +++ b/src/org.freedesktop.timedate1.policy @@ -0,0 +1,50 @@ + + + + + + + + The systemd Project + http://www.freedesktop.org/wiki/Software/systemd + + + Set system time + Authentication is required to set the system time. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + + Set system timezone + Authentication is required to set the system timezone. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + + Set RTC to local timezone or UTC + Authentication is required to control whether + the RTC stores the local or UTC time. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + diff --git a/src/org.freedesktop.timedate1.service b/src/org.freedesktop.timedate1.service new file mode 100644 index 0000000000..c3120b66cb --- /dev/null +++ b/src/org.freedesktop.timedate1.service @@ -0,0 +1,12 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +[D-BUS Service] +Name=org.freedesktop.timedate1 +Exec=/bin/false +User=root +SystemdService=dbus-org.freedesktop.timedate1.service diff --git a/src/polkit.c b/src/polkit.c new file mode 100644 index 0000000000..5b67480fe5 --- /dev/null +++ b/src/polkit.c @@ -0,0 +1,190 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +#include "util.h" +#include "dbus-common.h" +#include "polkit.h" + +/* This mimics dbus_bus_get_unix_user() */ +static pid_t get_unix_process_id( + DBusConnection *connection, + const char *name, + DBusError *error) { + + DBusMessage *m = NULL, *reply = NULL; + uint32_t pid = 0; + + m = dbus_message_new_method_call( + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "GetConnectionUnixProcessID"); + if (!m) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL); + goto finish; + } + + if (!dbus_message_append_args( + m, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID)) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL); + goto finish; + } + + reply = dbus_connection_send_with_reply_and_block(connection, m, -1, error); + if (!reply) + goto finish; + + if (dbus_set_error_from_message(error, reply)) + goto finish; + + if (!dbus_message_get_args( + reply, error, + DBUS_TYPE_UINT32, &pid, + DBUS_TYPE_INVALID)) + goto finish; + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + return (pid_t) pid; +} + +int verify_polkit( + DBusConnection *c, + DBusMessage *request, + const char *action, + bool interactive, + DBusError *error) { + + DBusMessage *m = NULL, *reply = NULL; + const char *unix_process = "unix-process", *pid = "pid", *starttime = "start-time", *cancel_id = ""; + const char *sender; + uint32_t flags = interactive ? 1 : 0; + pid_t pid_raw; + uint32_t pid_u32; + unsigned long long starttime_raw; + uint64_t starttime_u64; + DBusMessageIter iter_msg, iter_struct, iter_array, iter_dict, iter_variant; + int r; + dbus_bool_t authorized = FALSE; + + assert(c); + assert(request); + + sender = dbus_message_get_sender(request); + if (!sender) + return -EINVAL; + + pid_raw = get_unix_process_id(c, sender, error); + if (pid_raw == 0) + return -EINVAL; + + r = get_starttime_of_pid(pid_raw, &starttime_raw); + if (r < 0) + return r; + + m = dbus_message_new_method_call( + "org.freedesktop.PolicyKit1", + "/org/freedesktop/PolicyKit1/Authority", + "org.freedesktop.PolicyKit1.Authority", + "CheckAuthorization"); + if (!m) + return -ENOMEM; + + dbus_message_iter_init_append(m, &iter_msg); + + pid_u32 = (uint32_t) pid_raw; + starttime_u64 = (uint64_t) starttime_raw; + + if (!dbus_message_iter_open_container(&iter_msg, DBUS_TYPE_STRUCT, NULL, &iter_struct) || + !dbus_message_iter_append_basic(&iter_struct, DBUS_TYPE_STRING, &unix_process) || + !dbus_message_iter_open_container(&iter_struct, DBUS_TYPE_ARRAY, "{sv}", &iter_array) || + !dbus_message_iter_open_container(&iter_array, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict) || + !dbus_message_iter_append_basic(&iter_dict, DBUS_TYPE_STRING, &pid) || + !dbus_message_iter_open_container(&iter_dict, DBUS_TYPE_VARIANT, "u", &iter_variant) || + !dbus_message_iter_append_basic(&iter_variant, DBUS_TYPE_UINT32, &pid_u32) || + !dbus_message_iter_close_container(&iter_dict, &iter_variant) || + !dbus_message_iter_close_container(&iter_array, &iter_dict) || + !dbus_message_iter_open_container(&iter_array, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict) || + !dbus_message_iter_append_basic(&iter_dict, DBUS_TYPE_STRING, &starttime) || + !dbus_message_iter_open_container(&iter_dict, DBUS_TYPE_VARIANT, "t", &iter_variant) || + !dbus_message_iter_append_basic(&iter_variant, DBUS_TYPE_UINT64, &starttime_u64) || + !dbus_message_iter_close_container(&iter_dict, &iter_variant) || + !dbus_message_iter_close_container(&iter_array, &iter_dict) || + !dbus_message_iter_close_container(&iter_struct, &iter_array) || + !dbus_message_iter_close_container(&iter_msg, &iter_struct) || + !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_STRING, &action) || + !dbus_message_iter_open_container(&iter_msg, DBUS_TYPE_ARRAY, "{ss}", &iter_array) || + !dbus_message_iter_close_container(&iter_msg, &iter_array) || + !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_UINT32, &flags) || + !dbus_message_iter_append_basic(&iter_msg, DBUS_TYPE_STRING, &cancel_id)) { + r = -ENOMEM; + goto finish; + } + + reply = dbus_connection_send_with_reply_and_block(c, m, -1, error); + if (!reply) { + r = -EIO; + goto finish; + } + + if (dbus_set_error_from_message(error, reply)) { + r = -EIO; + goto finish; + } + + if (!dbus_message_iter_init(reply, &iter_msg) || + dbus_message_iter_get_arg_type(&iter_msg) != DBUS_TYPE_STRUCT) { + r = -EIO; + goto finish; + } + + dbus_message_iter_recurse(&iter_msg, &iter_struct); + + if (dbus_message_iter_get_arg_type(&iter_struct) != DBUS_TYPE_BOOLEAN) { + r = -EIO; + goto finish; + } + + dbus_message_iter_get_basic(&iter_struct, &authorized); + + r = authorized ? 0 : -EPERM; + +finish: + + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + return r; +} diff --git a/src/polkit.h b/src/polkit.h new file mode 100644 index 0000000000..fc4e77118f --- /dev/null +++ b/src/polkit.h @@ -0,0 +1,35 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foopolkithfoo +#define foopolkithfoo + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include + +int verify_polkit( + DBusConnection *c, + DBusMessage *request, + const char *action, + bool interactive, + DBusError *error); + +#endif diff --git a/src/timedated.c b/src/timedated.c new file mode 100644 index 0000000000..4749648df9 --- /dev/null +++ b/src/timedated.c @@ -0,0 +1,544 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include + +#include +#include +#include + +#include "util.h" +#include "strv.h" +#include "dbus-common.h" +#include "polkit.h" + +#define NULL_ADJTIME_UTC "0.0 0 0\n0\nUTC\n" +#define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n" + +#define INTROSPECTION \ + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ + "\n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + BUS_PROPERTIES_INTERFACE \ + BUS_INTROSPECTABLE_INTERFACE \ + BUS_PEER_INTERFACE \ + "\n" + +#define INTERFACES_LIST \ + BUS_GENERIC_INTERFACES_LIST \ + "org.freedesktop.locale1\0" + +static char *zone = NULL; +static bool local_rtc = false; + +static void free_data(void) { + free(zone); + zone = NULL; + + local_rtc = false; +} + +static bool valid_timezone(const char *name) { + const char *p; + char *t; + bool slash = false; + int r; + struct stat st; + + assert(name); + + if (*name == '/' || *name == 0) + return false; + + for (p = name; *p; p++) { + if (!(*p >= '0' && *p <= '9') && + !(*p >= 'a' && *p <= 'z') && + !(*p >= 'A' && *p <= 'Z') && + !(*p == '-' || *p == '_' || *p == '+' || *p == '/')) + return false; + + if (*p == '/') { + + if (slash) + return false; + + slash = true; + } else + slash = false; + } + + if (slash) + return false; + + t = strappend("/usr/share/zoneinfo/", name); + if (!t) + return false; + + r = stat(t, &st); + free(t); + + if (r < 0) + return false; + + if (!S_ISREG(st.st_mode)) + return false; + + return true; +} + +static void verify_timezone(void) { + char *p, *a = NULL, *b = NULL; + size_t l, q; + int j, k; + + if (!zone) + return; + + p = strappend("/usr/share/zoneinfo/", zone); + if (!p) { + log_error("Out of memory"); + return; + } + + j = read_full_file("/etc/localtime", &a, &l); + k = read_full_file(p, &b, &q); + + free(p); + + if (j < 0 || k < 0 || l != q || memcmp(a, b, l)) { + log_warning("/etc/localtime and /etc/timezone out of sync."); + free(zone); + zone = NULL; + } + + free(a); + free(b); +} + +static int read_data(void) { + int r; + FILE *f; + + free_data(); + + r = read_one_line_file("/etc/timezone", &zone); + if (r < 0 && r != -ENOENT) + return r; + + verify_timezone(); + + f = fopen("/etc/adjtime", "r"); + if (f) { + char line[LINE_MAX]; + bool b; + + b = fgets(line, sizeof(line), f) && + fgets(line, sizeof(line), f) && + fgets(line, sizeof(line), f); + + fclose(f); + + if (!b) + return -EIO; + + truncate_nl(line); + local_rtc = streq(line, "LOCAL"); + + } else if (errno != ENOENT) + return -errno; + + return 0; +} + +static int write_data_timezone(void) { + int r = 0; + char *p; + + if (!zone) { + if (unlink("/etc/timezone") < 0 && errno != ENOENT) + r = -errno; + + if (unlink("/etc/localtime") < 0 && errno != ENOENT) + r = -errno; + + return r; + } + + p = strappend("/usr/share/zoneinfo/", zone); + if (!p) { + log_error("Out of memory"); + return -ENOMEM; + } + + r = symlink_or_copy_atomic(p, "/etc/localtime"); + free(p); + + if (r < 0) + return r; + + r = write_one_line_file_atomic("/etc/timezone", zone); + if (r < 0) + return r; + + return 0; +} + +static int write_data_local_rtc(void) { + int r; + char *s, *w; + + r = read_full_file("/etc/adjtime", &s, NULL); + if (r < 0) { + if (r != -ENOENT) + return r; + + if (!local_rtc) + return 0; + + w = strdup(NULL_ADJTIME_LOCAL); + if (!w) + return -ENOMEM; + } else { + char *p, *e; + size_t a, b; + + p = strchr(s, '\n'); + if (!p) { + free(s); + return -EIO; + } + + p = strchr(p+1, '\n'); + if (!p) { + free(s); + return -EIO; + } + + p++; + e = strchr(p, '\n'); + if (!p) { + free(s); + return -EIO; + } + + a = p - s; + b = strlen(e); + + w = new(char, a + (local_rtc ? 5 : 3) + b + 1); + if (!w) { + free(s); + return -ENOMEM; + } + + *(char*) mempcpy(stpcpy(mempcpy(w, s, a), local_rtc ? "LOCAL" : "UTC"), e, b) = 0; + + if (streq(w, NULL_ADJTIME_UTC)) { + free(w); + + if (unlink("/etc/adjtime") < 0) { + if (errno != ENOENT) + return -errno; + } + + return 0; + } + } + + r = write_one_line_file_atomic("/etc/adjtime", w); + free(w); + + return r; +} + +static DBusHandlerResult timedate_message_handler( + DBusConnection *connection, + DBusMessage *message, + void *userdata) { + + const BusProperty properties[] = { + { "org.freedesktop.timedate1", "Timezone", bus_property_append_string, "s", zone }, + { "org.freedesktop.timedate1", "LocalRTC", bus_property_append_bool, "b", &local_rtc }, + { NULL, NULL, NULL, NULL, NULL } + }; + + DBusMessage *reply = NULL, *changed = NULL; + DBusError error; + int r; + + assert(connection); + assert(message); + + dbus_error_init(&error); + + if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetTimezone")) { + const char *z; + dbus_bool_t interactive; + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_STRING, &z, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + if (!valid_timezone(z)) + return bus_send_error_reply(connection, message, NULL, -EINVAL); + + if (!streq_ptr(z, zone)) { + char *t; + + r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-timezone", interactive, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + t = strdup(z); + if (!t) + goto oom; + + free(zone); + zone = t; + + r = write_data_timezone(); + if (r < 0) { + log_error("Failed to set timezone: %s", strerror(-r)); + return bus_send_error_reply(connection, message, NULL, r); + } + + log_info("Changed timezone to '%s'.", zone); + + changed = bus_properties_changed_new( + "/org/freedesktop/timedate1", + "org.freedesktop.timedate1", + "Timezone\0"); + if (!changed) + goto oom; + } + + } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetLocalRTC")) { + dbus_bool_t lrtc; + dbus_bool_t interactive; + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_BOOLEAN, &lrtc, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + if (lrtc != local_rtc) { + r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-local-rtc", interactive, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + local_rtc = lrtc; + + r = write_data_local_rtc(); + if (r < 0) { + log_error("Failed to set RTC to local/UTC: %s", strerror(-r)); + return bus_send_error_reply(connection, message, NULL, r); + } + + log_info("Changed local RTC setting to '%s'.", yes_no(local_rtc)); + + changed = bus_properties_changed_new( + "/org/freedesktop/timedate1", + "org.freedesktop.timedate1", + "LocalRTC\0"); + if (!changed) + goto oom; + } + + } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetTime")) { + int64_t utc; + dbus_bool_t relative; + dbus_bool_t interactive; + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_INT64, &utc, + DBUS_TYPE_BOOLEAN, &relative, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + if (!relative && utc <= 0) + return bus_send_error_reply(connection, message, NULL, -EINVAL); + + if (!relative || utc != 0) { + struct timespec ts; + + r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-time", interactive, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + if (relative) + timespec_store(&ts, now(CLOCK_REALTIME) + utc); + else + timespec_store(&ts, utc); + + if (clock_settime(CLOCK_REALTIME, &ts) < 0) { + log_error("Failed to set local time: %m"); + return bus_send_error_reply(connection, message, NULL, -errno); + } + + log_info("Changed local time to %s", ctime(&ts.tv_sec)); + } + + } else + return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties); + + if (!(reply = dbus_message_new_method_return(message))) + goto oom; + + if (!dbus_connection_send(connection, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + reply = NULL; + + if (changed) { + + if (!dbus_connection_send(connection, changed, NULL)) + goto oom; + + dbus_message_unref(changed); + } + + return DBUS_HANDLER_RESULT_HANDLED; + +oom: + if (reply) + dbus_message_unref(reply); + + if (changed) + dbus_message_unref(changed); + + dbus_error_free(&error); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; +} + +static int connect_bus(DBusConnection **_bus) { + static const DBusObjectPathVTable timedate_vtable = { + .message_function = timedate_message_handler + }; + DBusError error; + DBusConnection *bus = NULL; + int r; + + assert(_bus); + + dbus_error_init(&error); + + bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); + if (!bus) { + log_error("Failed to get system D-Bus connection: %s", error.message); + r = -ECONNREFUSED; + goto fail; + } + + if (!dbus_connection_register_object_path(bus, "/org/freedesktop/timedate1", &timedate_vtable, NULL)) { + log_error("Not enough memory"); + r = -ENOMEM; + goto fail; + } + + if (dbus_bus_request_name(bus, "org.freedesktop.timedate1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) < 0) { + log_error("Failed to register name on bus: %s", error.message); + r = -EEXIST; + goto fail; + } + + if (_bus) + *_bus = bus; + + return 0; + +fail: + dbus_connection_close(bus); + dbus_connection_unref(bus); + + dbus_error_free(&error); + + return r; +} + +int main(int argc, char *argv[]) { + int r; + DBusConnection *bus = NULL; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + if (argc != 1) { + log_error("This program takes no arguments."); + r = -EINVAL; + goto finish; + } + + umask(0022); + + r = read_data(); + if (r < 0) { + log_error("Failed to read timezone data: %s", strerror(-r)); + goto finish; + } + + r = connect_bus(&bus); + if (r < 0) + goto finish; + + while (dbus_connection_read_write_dispatch(bus, -1)) + ; + + r = 0; + +finish: + free_data(); + + if (bus) { + dbus_connection_flush(bus); + dbus_connection_close(bus); + dbus_connection_unref(bus); + } + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/units/.gitignore b/units/.gitignore index f969466bd7..9634440955 100644 --- a/units/.gitignore +++ b/units/.gitignore @@ -1,3 +1,5 @@ +systemd-localed.service +systemd-timedated.service systemd-hostnamed.service console-shell.service systemd-sysctl.service diff --git a/units/systemd-localed.service.in b/units/systemd-localed.service.in new file mode 100644 index 0000000000..4be65df75d --- /dev/null +++ b/units/systemd-localed.service.in @@ -0,0 +1,17 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# See systemd.special(7) for details + +[Unit] +Description=Locale Service + +[Service] +ExecStart=@rootlibexecdir@/systemd-localed +Type=dbus +BusName=org.freedesktop.locale1 +CapabilityBoundingSet= diff --git a/units/systemd-timedated.service.in b/units/systemd-timedated.service.in new file mode 100644 index 0000000000..90ff4432ba --- /dev/null +++ b/units/systemd-timedated.service.in @@ -0,0 +1,17 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# See systemd.special(7) for details + +[Unit] +Description=Time & Date Service + +[Service] +ExecStart=@rootlibexecdir@/systemd-timedated +Type=dbus +BusName=org.freedesktop.timedate1 +CapabilityBoundingSet=CAP_SYS_TIME -- cgit v1.2.3-54-g00ecf From abca4822916b85ae5b0b2bef5d458ea2225d25ab Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 7 Jul 2011 03:29:56 +0200 Subject: loginctl: add basic implementation of loginctl for introspecting controlling sessions/users/seats --- .gitignore | 1 + Makefile.am | 16 ++ src/dbus-common.c | 16 ++ src/dbus-common.h | 2 + src/loginctl.c | 531 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/systemctl.c | 17 -- 6 files changed, 566 insertions(+), 17 deletions(-) create mode 100644 src/loginctl.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 92b8f73fac..b108fd9875 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +systemd-loginctl systemd-localed systemd-timedated systemd-uaccess diff --git a/Makefile.am b/Makefile.am index b4feb7a9a2..78e5caf35b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -116,6 +116,7 @@ endif rootbin_PROGRAMS = \ systemd \ systemctl \ + systemd-loginctl \ systemd-notify \ systemd-ask-password \ systemd-tty-ask-password-agent \ @@ -1130,6 +1131,21 @@ systemctl_LDADD = \ libsystemd-daemon.la \ $(DBUS_LIBS) +systemd_loginctl_SOURCES = \ + src/loginctl.c \ + src/dbus-common.c \ + src/cgroup-show.c \ + src/cgroup-util.c \ + src/pager.c + +systemd_loginctl_CFLAGS = \ + $(AM_CFLAGS) \ + $(DBUS_CFLAGS) + +systemd_loginctl_LDADD = \ + libsystemd-basic.la \ + $(DBUS_LIBS) + systemd_notify_SOURCES = \ src/notify.c \ src/sd-readahead.c diff --git a/src/dbus-common.c b/src/dbus-common.c index 5bfaf361fb..4b9b457fc3 100644 --- a/src/dbus-common.c +++ b/src/dbus-common.c @@ -821,3 +821,19 @@ int bus_append_strv_iter(DBusMessageIter *iter, char **l) { return 0; } + +int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *data, bool next) { + + assert(iter); + assert(data); + + if (dbus_message_iter_get_arg_type(iter) != type) + return -EIO; + + dbus_message_iter_get_basic(iter, data); + + if (!dbus_message_iter_next(iter) != !next) + return -EIO; + + return 0; +} diff --git a/src/dbus-common.h b/src/dbus-common.h index 04485e50e4..e3a3ecbe0e 100644 --- a/src/dbus-common.h +++ b/src/dbus-common.h @@ -163,4 +163,6 @@ int bus_parse_strv_iter(DBusMessageIter *iter, char ***_l); int bus_append_strv_iter(DBusMessageIter *iter, char **l); +int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *data, bool next); + #endif diff --git a/src/loginctl.c b/src/loginctl.c new file mode 100644 index 0000000000..022f69e7eb --- /dev/null +++ b/src/loginctl.c @@ -0,0 +1,531 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "log.h" +#include "util.h" +#include "macro.h" +#include "pager.h" +#include "dbus-common.h" +#include "build.h" + +static bool arg_no_pager = false; +static enum transport { + TRANSPORT_NORMAL, + TRANSPORT_SSH, + TRANSPORT_POLKIT +} arg_transport = TRANSPORT_NORMAL; +static const char *arg_host = NULL; + +static bool on_tty(void) { + static int t = -1; + + /* Note that this is invoked relatively early, before we start + * the pager. That means the value we return reflects whether + * we originally were started on a tty, not if we currently + * are. But this is intended, since we want colour and so on + * when run in our own pager. */ + + if (_unlikely_(t < 0)) + t = isatty(STDOUT_FILENO) > 0; + + return t; +} + +static void pager_open_if_enabled(void) { + on_tty(); + + if (!arg_no_pager) + pager_open(); +} + +static int list_sessions(DBusConnection *bus, char **args, unsigned n) { + DBusMessage *m = NULL, *reply = NULL; + DBusError error; + int r; + DBusMessageIter iter, sub, sub2; + unsigned k = 0; + + dbus_error_init(&error); + + assert(bus); + + pager_open_if_enabled(); + + m = dbus_message_new_method_call( + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "ListSessions"); + if (!m) { + log_error("Could not allocate message."); + return -ENOMEM; + } + + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); + if (!reply) { + log_error("Failed to issue method call: %s", bus_error_message(&error)); + r = -EIO; + goto finish; + } + + if (!dbus_message_iter_init(reply, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) { + log_error("Failed to parse reply."); + r = -EIO; + goto finish; + } + + dbus_message_iter_recurse(&iter, &sub); + + if (on_tty()) + printf("%10s %10s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT"); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + const char *id, *user, *seat, *object; + uint32_t uid; + + if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) { + log_error("Failed to parse reply."); + r = -EIO; + goto finish; + } + + dbus_message_iter_recurse(&sub, &sub2); + + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) { + log_error("Failed to parse reply."); + r = -EIO; + goto finish; + } + + printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat); + + k++; + + dbus_message_iter_next(&sub); + } + + if (on_tty()) + printf("\n%u sessions listed.\n", k); + + r = 0; + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); + + return r; +} + +static int list_users(DBusConnection *bus, char **args, unsigned n) { + DBusMessage *m = NULL, *reply = NULL; + DBusError error; + int r; + DBusMessageIter iter, sub, sub2; + unsigned k = 0; + + dbus_error_init(&error); + + assert(bus); + + pager_open_if_enabled(); + + m = dbus_message_new_method_call( + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "ListUsers"); + if (!m) { + log_error("Could not allocate message."); + return -ENOMEM; + } + + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); + if (!reply) { + log_error("Failed to issue method call: %s", bus_error_message(&error)); + r = -EIO; + goto finish; + } + + if (!dbus_message_iter_init(reply, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) { + log_error("Failed to parse reply."); + r = -EIO; + goto finish; + } + + dbus_message_iter_recurse(&iter, &sub); + + if (on_tty()) + printf("%10s %-16s\n", "UID", "USER"); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + const char *user, *object; + uint32_t uid; + + if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) { + log_error("Failed to parse reply."); + r = -EIO; + goto finish; + } + + dbus_message_iter_recurse(&sub, &sub2); + + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) { + log_error("Failed to parse reply."); + r = -EIO; + goto finish; + } + + printf("%10u %-16s\n", (unsigned) uid, user); + + k++; + + dbus_message_iter_next(&sub); + } + + if (on_tty()) + printf("\n%u users listed.\n", k); + + r = 0; + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); + + return r; +} + +static int list_seats(DBusConnection *bus, char **args, unsigned n) { + DBusMessage *m = NULL, *reply = NULL; + DBusError error; + int r; + DBusMessageIter iter, sub, sub2; + unsigned k = 0; + + dbus_error_init(&error); + + assert(bus); + + pager_open_if_enabled(); + + m = dbus_message_new_method_call( + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "ListSeats"); + if (!m) { + log_error("Could not allocate message."); + return -ENOMEM; + } + + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); + if (!reply) { + log_error("Failed to issue method call: %s", bus_error_message(&error)); + r = -EIO; + goto finish; + } + + if (!dbus_message_iter_init(reply, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) { + log_error("Failed to parse reply."); + r = -EIO; + goto finish; + } + + dbus_message_iter_recurse(&iter, &sub); + + if (on_tty()) + printf("%-16s\n", "SEAT"); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + const char *seat, *object; + + if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) { + log_error("Failed to parse reply."); + r = -EIO; + goto finish; + } + + dbus_message_iter_recurse(&sub, &sub2); + + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) { + log_error("Failed to parse reply."); + r = -EIO; + goto finish; + } + + printf("%-16s\n", seat); + + k++; + + dbus_message_iter_next(&sub); + } + + if (on_tty()) + printf("\n%u seats listed.\n", k); + + r = 0; + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); + + return r; +} + +static int help(void) { + + printf("%s [OPTIONS...] {COMMAND} ...\n\n" + "Send control commands to or query the login manager.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " -H --host=[user@]host\n" + " Show information for remote host\n" + " -P --privileged Acquire privileges before execution\n" + " --no-pager Do not pipe output into a pager.\n" + "Commands:\n" + " list-sessions List sessions\n" + " list-users List users\n" + " list-seats List seats\n", + program_invocation_short_name); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_NO_PAGER + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "host", required_argument, NULL, 'H' }, + { "privileged",no_argument, NULL, 'P' }, + { NULL, 0, NULL, 0 } + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "hH:P", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + puts(PACKAGE_STRING); + puts(DISTRIBUTION); + puts(SYSTEMD_FEATURES); + return 0; + + case ARG_NO_PAGER: + arg_no_pager = true; + break; + + case 'P': + arg_transport = TRANSPORT_POLKIT; + break; + + case 'H': + arg_transport = TRANSPORT_SSH; + arg_host = optarg; + break; + + case '?': + return -EINVAL; + + default: + log_error("Unknown option code %c", c); + return -EINVAL; + } + } + + return 1; +} + +static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) { + + static const struct { + const char* verb; + const enum { + MORE, + LESS, + EQUAL + } argc_cmp; + const int argc; + int (* const dispatch)(DBusConnection *bus, char **args, unsigned n); + } verbs[] = { + { "list-sessions", LESS, 1, list_sessions }, + { "list-users", EQUAL, 1, list_users }, + { "list-seats", EQUAL, 1, list_seats }, + }; + + int left; + unsigned i; + + assert(argc >= 0); + assert(argv); + assert(error); + + left = argc - optind; + + if (left <= 0) + /* Special rule: no arguments means "list-sessions" */ + i = 0; + else { + if (streq(argv[optind], "help")) { + help(); + return 0; + } + + for (i = 0; i < ELEMENTSOF(verbs); i++) + if (streq(argv[optind], verbs[i].verb)) + break; + + if (i >= ELEMENTSOF(verbs)) { + log_error("Unknown operation %s", argv[optind]); + return -EINVAL; + } + } + + switch (verbs[i].argc_cmp) { + + case EQUAL: + if (left != verbs[i].argc) { + log_error("Invalid number of arguments."); + return -EINVAL; + } + + break; + + case MORE: + if (left < verbs[i].argc) { + log_error("Too few arguments."); + return -EINVAL; + } + + break; + + case LESS: + if (left > verbs[i].argc) { + log_error("Too many arguments."); + return -EINVAL; + } + + break; + + default: + assert_not_reached("Unknown comparison operator."); + } + + if (!bus) { + log_error("Failed to get D-Bus connection: %s", error->message); + return -EIO; + } + + return verbs[i].dispatch(bus, argv + optind, left); +} + +int main(int argc, char*argv[]) { + int r, retval = EXIT_FAILURE; + DBusConnection *bus = NULL; + DBusError error; + + dbus_error_init(&error); + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r < 0) + goto finish; + else if (r == 0) { + retval = EXIT_SUCCESS; + goto finish; + } + + if (arg_transport == TRANSPORT_NORMAL) + bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); + else if (arg_transport == TRANSPORT_POLKIT) + bus_connect_system_polkit(&bus, &error); + else if (arg_transport == TRANSPORT_SSH) + bus_connect_system_ssh(NULL, arg_host, &bus, &error); + else + assert_not_reached("Uh, invalid transport..."); + + r = loginctl_main(bus, argc, argv, &error); + retval = r < 0 ? EXIT_FAILURE : r; + +finish: + if (bus) { + dbus_connection_flush(bus); + dbus_connection_close(bus); + dbus_connection_unref(bus); + } + + dbus_error_free(&error); + dbus_shutdown(); + + pager_close(); + + return retval; +} diff --git a/src/systemctl.c b/src/systemctl.c index 005b45d4fe..556070bcba 100644 --- a/src/systemctl.c +++ b/src/systemctl.c @@ -33,7 +33,6 @@ #include #include #include - #include #include "log.h" @@ -278,22 +277,6 @@ static int translate_bus_error_to_exit_status(int r, const DBusError *error) { return EXIT_FAILURE; } -static int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *data, bool next) { - - assert(iter); - assert(data); - - if (dbus_message_iter_get_arg_type(iter) != type) - return -EIO; - - dbus_message_iter_get_basic(iter, data); - - if (!dbus_message_iter_next(iter) != !next) - return -EIO; - - return 0; -} - static void warn_wall(enum action action) { static const char *table[_ACTION_MAX] = { [ACTION_HALT] = "The system is going down for system halt NOW!", -- cgit v1.2.3-54-g00ecf From 74b91131ed09850ed487a2f7849147ff6f80194d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 14 Jul 2011 22:50:01 +0200 Subject: logind: introduce libsystemd-login.so as fast path to access logind data --- .gitignore | 2 + Makefile.am | 47 ++++++- TODO | 2 + libsystemd-login.pc.in | 18 +++ src/sd-login.c | 323 +++++++++++++++++++++++++++++++++++++++++++++++++ src/sd-login.h | 51 ++++++++ src/test-login.c | 65 ++++++++++ systemd.pc.in | 2 +- 8 files changed, 507 insertions(+), 3 deletions(-) create mode 100644 libsystemd-login.pc.in create mode 100644 src/sd-login.c create mode 100644 src/sd-login.h create mode 100644 src/test-login.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index b108fd9875..cd25404577 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +libsystemd-login.pc +test-login systemd-loginctl systemd-localed systemd-timedated diff --git a/Makefile.am b/Makefile.am index 53167ff20e..445cc1e37e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -19,6 +19,10 @@ ACLOCAL_AMFLAGS = -I m4 SUBDIRS = po +LIBSYSTEMD_LOGIN_CURRENT=0 +LIBSYSTEMD_LOGIN_REVISION=0 +LIBSYSTEMD_LOGIN_AGE=0 + # Dirs of external packages dbuspolicydir=@dbuspolicydir@ dbussessionservicedir=@dbussessionservicedir@ @@ -35,6 +39,7 @@ pkgsysconfdir=$(sysconfdir)/systemd userunitdir=$(prefix)/lib/systemd/user tmpfilesdir=$(prefix)/lib/tmpfiles.d usergeneratordir=$(pkglibexecdir)/user-generators +pkgincludedir=$(includedir)/systemd # And these are the special ones for / rootdir=@rootdir@ @@ -183,6 +188,12 @@ systemgenerator_PROGRAMS += \ systemd-cryptsetup-generator endif +lib_LTLIBRARIES = \ + libsystemd-login.la + +pkginclude_HEADERS = \ + src/sd-login.h + noinst_PROGRAMS = \ test-engine \ test-job-type \ @@ -192,7 +203,8 @@ noinst_PROGRAMS = \ test-daemon \ test-cgroup \ test-env-replace \ - test-strv + test-strv \ + test-login if HAVE_PAM pamlib_LTLIBRARIES = \ @@ -410,6 +422,7 @@ EXTRA_DIST = \ units/quotacheck.service.in \ units/user@.service.in \ systemd.pc.in \ + libsystemd-login.pc.in \ introspect.awk \ src/73-seat-late.rules.in @@ -472,7 +485,8 @@ dist_doc_DATA = \ src/sd-readahead.c pkgconfigdata_DATA = \ - systemd.pc + systemd.pc \ + libsystemd-login.pc # Passed through intltool only polkitpolicy_in_files = \ @@ -801,6 +815,16 @@ test_strv_CFLAGS = \ test_strv_LDADD = \ libsystemd-basic.la +test_login_SOURCES = \ + src/test-login.c + +test_login_CFLAGS = \ + $(AM_CFLAGS) + +test_login_LDADD = \ + libsystemd-basic.la \ + libsystemd-login.la + systemd_logger_SOURCES = \ src/logger.c \ src/tcpwrap.c @@ -946,6 +970,7 @@ systemd_uaccess_CFLAGS = \ systemd_uaccess_LDADD = \ libsystemd-basic.la \ + libsystemd-daemon.la \ $(UDEV_LIBS) \ $(ACL_LIBS) @@ -1339,6 +1364,21 @@ pam_systemd_la_LIBADD = \ $(PAM_LIBS) \ $(DBUS_LIBS) +libsystemd_login_la_CFLAGS = \ + $(AM_CFLAGS) \ + -fvisibility=hidden + +libsystemd_login_la_LDFLAGS = \ + -shared \ + -version-info $(LIBSYSTEMD_LOGIN_CURRENT):$(LIBSYSTEMD_LOGIN_REVISION):$(LIBSYSTEMD_LOGIN_AGE) + +libsystemd_login_la_SOURCES = \ + src/sd-login.c \ + src/cgroup-util.c + +libsystemd_login_la_LIBADD = \ + libsystemd-basic.la + SED_PROCESS = \ $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \ $(SED) -e 's,@rootlibexecdir\@,$(rootlibexecdir),g' \ @@ -1355,6 +1395,9 @@ SED_PROCESS = \ -e 's,@PACKAGE_NAME\@,$(PACKAGE_NAME),g' \ -e 's,@PACKAGE_URL\@,$(PACKAGE_URL),g' \ -e 's,@prefix\@,$(prefix),g' \ + -e 's,@exec_prefix\@,$(exec_prefix),g' \ + -e 's,@libdir\@,$(libdir),g' \ + -e 's,@includedir\@,$(includedir),g' \ < $< > $@ || rm $@ units/%: units/%.in Makefile diff --git a/TODO b/TODO index 8a807b48f9..1577565e03 100644 --- a/TODO +++ b/TODO @@ -20,6 +20,8 @@ F15 External: Features: +* logind: ensure ACLs are updated on login and logout + * warn if the user stops a service but not its associated socket * ensure we always set the facility when logging to kmsg diff --git a/libsystemd-login.pc.in b/libsystemd-login.pc.in new file mode 100644 index 0000000000..cd36a9cb35 --- /dev/null +++ b/libsystemd-login.pc.in @@ -0,0 +1,18 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: systemd +Description: systemd Login Utility Library +URL: @PACKAGE_URL@ +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lsystemd-login +Cflags: -I${includedir} diff --git a/src/sd-login.c b/src/sd-login.c new file mode 100644 index 0000000000..3ae850d801 --- /dev/null +++ b/src/sd-login.c @@ -0,0 +1,323 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "util.h" +#include "cgroup-util.h" +#include "macro.h" +#include "sd-login.h" + +_public_ int sd_pid_get_session(pid_t pid, char **session) { + int r; + char *cg_process, *cg_init, *p; + + if (pid == 0) + pid = getpid(); + + if (pid <= 0) + return -EINVAL; + + if (!session) + return -EINVAL; + + r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, pid, &cg_process); + if (r < 0) + return r; + + r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 1, &cg_init); + if (r < 0) { + free(cg_process); + return r; + } + + if (endswith(cg_init, "/system")) + cg_init[strlen(cg_init)-7] = 0; + else if (streq(cg_init, "/")) + cg_init[0] = 0; + + if (startswith(cg_process, cg_init)) + p = cg_process + strlen(cg_init); + else + p = cg_process; + + free(cg_init); + + if (!startswith(p, "/user/")) { + free(cg_process); + return -ENOENT; + } + + p += 6; + if (startswith(p, "shared/") || streq(p, "shared")) { + free(cg_process); + return -ENOENT; + } + + p = strchr(p, '/'); + if (!p) { + free(cg_process); + return -ENOENT; + } + + p++; + p = strndup(p, strcspn(p, "/")); + free(cg_process); + + if (!p) + return -ENOMEM; + + *session = p; + return 0; +} + +_public_ int sd_uid_get_state(uid_t uid, char**state) { + char *p, *s = NULL; + int r; + + if (!state) + return -EINVAL; + + if (asprintf(&p, "/run/systemd/users/%lu", (unsigned long) uid) < 0) + return -ENOMEM; + + r = parse_env_file(p, NEWLINE, "STATE", &s, NULL); + free(p); + + if (r == -ENOENT) { + free(s); + s = strdup("offline"); + if (!s) + return -ENOMEM; + + *state = s; + return 0; + } else if (r < 0) { + free(s); + return r; + } else if (!s) + return -EIO; + + *state = s; + return 0; +} + +static int uid_is_on_seat_internal(uid_t uid, const char *seat, const char *variable) { + char *p, *w, *t, *state, *s = NULL; + size_t l; + int r; + + if (!seat) + return -EINVAL; + + p = strappend("/run/systemd/seats/", seat); + if (!p) + return -ENOMEM; + + r = parse_env_file(p, NEWLINE, "UIDS", &s, NULL); + free(p); + + if (r < 0) { + free(s); + return r; + } + + if (!s) + return -EIO; + + if (asprintf(&t, "%lu", (unsigned long) uid) < 0) { + free(s); + return -ENOMEM; + } + + FOREACH_WORD(w, l, s, state) { + if (strncmp(t, w, l) == 0) { + free(s); + free(t); + + return 1; + } + } + + free(s); + free(t); + + return 0; +} + +_public_ int sd_uid_is_on_seat(uid_t uid, const char *seat) { + return uid_is_on_seat_internal(uid, seat, "UIDS"); +} + +_public_ int sd_uid_is_active_on_seat(uid_t uid, const char *seat) { + return uid_is_on_seat_internal(uid, seat, "ACTIVE_UID"); +} + +_public_ int sd_session_is_active(const char *session) { + int r; + char *p, *s = NULL; + + if (!session) + return -EINVAL; + + p = strappend("/run/systemd/sessions/", session); + if (!p) + return -ENOMEM; + + r = parse_env_file(p, NEWLINE, "ACTIVE", &s, NULL); + free(p); + + if (r < 0) { + free(s); + return r; + } + + if (!s) + return -EIO; + + r = parse_boolean(s); + free(s); + + return r; +} + +_public_ int sd_session_get_uid(const char *session, uid_t *uid) { + int r; + char *p, *s = NULL; + unsigned long ul; + + if (!session) + return -EINVAL; + if (!uid) + return -EINVAL; + + p = strappend("/run/systemd/sessions/", session); + if (!p) + return -ENOMEM; + + r = parse_env_file(p, NEWLINE, "UID", &s, NULL); + free(p); + + if (r < 0) { + free(s); + return r; + } + + if (!s) + return -EIO; + + r = safe_atolu(s, &ul); + free(s); + + if (r < 0) + return r; + + *uid = (uid_t) ul; + return 0; +} + +_public_ int sd_session_get_seat(const char *session, char **seat) { + char *p, *s = NULL; + int r; + + if (!session) + return -EINVAL; + if (!seat) + return -EINVAL; + + p = strappend("/run/systemd/sessions/", session); + if (!p) + return -ENOMEM; + + r = parse_env_file(p, NEWLINE, "SEAT", &s, NULL); + free(p); + + if (r < 0) { + free(s); + return r; + } + + if (isempty(s)) + return -ENOENT; + + *seat = s; + return 0; +} + +_public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) { + char *p, *s = NULL, *t = NULL; + int r; + + if (!seat) + return -EINVAL; + if (!session && !uid) + return -EINVAL; + + p = strappend("/run/systemd/seats/", seat); + if (!p) + return -ENOMEM; + + r = parse_env_file(p, NEWLINE, + "ACTIVE", &s, + "ACTIVE_UID", &t, + NULL); + free(p); + + if (r < 0) { + free(s); + free(t); + return r; + } + + if (session && !s) { + free(t); + return -EIO; + } + + if (uid && !t) { + free(s); + return -EIO; + } + + if (uid && t) { + unsigned long ul; + + r = safe_atolu(t, &ul); + if (r < 0) { + free(t); + free(s); + return r; + } + + *uid = (uid_t) ul; + } + + free(t); + + if (session && s) + *session = s; + else + free(s); + + return 0; +} diff --git a/src/sd-login.h b/src/sd-login.h new file mode 100644 index 0000000000..f23ac80526 --- /dev/null +++ b/src/sd-login.h @@ -0,0 +1,51 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foosdloginhfoo +#define foosdloginhfoo + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include + +/* Get session from PID */ +int sd_pid_get_session(pid_t pid, char **session); + +/* Get state from uid. Possible states: offline, lingering, online, active */ +int sd_uid_get_state(uid_t uid, char**state); + +/* Return 1 if uid has session on seat */ +int sd_uid_is_on_seat(uid_t uid, const char *seat); + +/* Return 1 if uid has active session on seat */ +int sd_uid_is_active_on_seat(uid_t uid, const char *seat); + +/* Return 1 if the session is a active */ +int sd_session_is_active(const char *session); + +/* Determine user id of session */ +int sd_session_get_uid(const char *session, uid_t *uid); + +/* Determine seat of session */ +int sd_session_get_seat(const char *session, char **seat); + +/* Return active session and user of seat */ +int sd_seat_get_active(const char *seat, char **session, uid_t *uid); + +#endif diff --git a/src/test-login.c b/src/test-login.c new file mode 100644 index 0000000000..97313fcbcb --- /dev/null +++ b/src/test-login.c @@ -0,0 +1,65 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include "sd-login.h" +#include "util.h" + +int main(int argc, char* argv[]) { + int r, k; + uid_t u, u2; + char *seat; + char *session; + char *state; + char *session2; + + assert_se(sd_pid_get_session(0, &session) == 0); + printf("session = %s\n", session); + + r = sd_session_is_active(session); + assert_se(r >= 0); + printf("active = %s\n", yes_no(r)); + + assert_se(sd_session_get_uid(session, &u) >= 0); + printf("uid = %lu\n", (unsigned long) u); + + assert_se(sd_session_get_seat(session, &seat) >= 0); + printf("seat = %s\n", seat); + + assert_se(sd_uid_get_state(u, &state) >= 0); + printf("state = %s\n", state); + + assert_se(sd_uid_is_on_seat(u, seat) > 0); + + k = sd_uid_is_active_on_seat(u, seat); + assert_se(k >= 0); + assert_se(!!r == !!r); + + assert_se(sd_seat_get_active(seat, &session2, &u2) >= 0); + printf("session2 = %s\n", session2); + printf("uid2 = %lu\n", (unsigned long) u2); + + free(session); + free(state); + free(session2); + free(seat); + + return 0; +} diff --git a/systemd.pc.in b/systemd.pc.in index 2e2217ec4b..29376e55b0 100644 --- a/systemd.pc.in +++ b/systemd.pc.in @@ -6,7 +6,7 @@ # (at your option) any later version. prefix=@prefix@ -exec_prefix=${prefix} +exec_prefix=@exec_prefix@ systemdsystemunitdir=@systemunitdir@ systemduserunitdir=@userunitdir@ systemdsystemconfdir=@pkgsysconfdir@/system -- cgit v1.2.3-54-g00ecf From 114a50f898a89bd7784c215ac5df95ec8c45a905 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 14 Jul 2011 23:06:31 +0200 Subject: sd-daemon: turn sd-daemon.c into a shared library --- .gitignore | 1 + Makefile.am | 41 ++++++++++++++++++++++++++++------------- libsystemd-daemon.pc.in | 18 ++++++++++++++++++ src/sd-daemon.c | 32 +++++++++++++++++++------------- 4 files changed, 66 insertions(+), 26 deletions(-) create mode 100644 libsystemd-daemon.pc.in (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index cd25404577..1c07f12d32 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +libsystemd-daemon.pc libsystemd-login.pc test-login systemd-loginctl diff --git a/Makefile.am b/Makefile.am index 445cc1e37e..767baf7cd3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,6 +23,10 @@ LIBSYSTEMD_LOGIN_CURRENT=0 LIBSYSTEMD_LOGIN_REVISION=0 LIBSYSTEMD_LOGIN_AGE=0 +LIBSYSTEMD_DAEMON_CURRENT=0 +LIBSYSTEMD_DAEMON_REVISION=0 +LIBSYSTEMD_DAEMON_AGE=0 + # Dirs of external packages dbuspolicydir=@dbuspolicydir@ dbussessionservicedir=@dbussessionservicedir@ @@ -189,6 +193,7 @@ systemgenerator_PROGRAMS += \ endif lib_LTLIBRARIES = \ + libsystemd-daemon.la \ libsystemd-login.la pkginclude_HEADERS = \ @@ -422,6 +427,7 @@ EXTRA_DIST = \ units/quotacheck.service.in \ units/user@.service.in \ systemd.pc.in \ + libsystemd-daemon.pc.in \ libsystemd-login.pc.in \ introspect.awk \ src/73-seat-late.rules.in @@ -486,6 +492,7 @@ dist_doc_DATA = \ pkgconfigdata_DATA = \ systemd.pc \ + libsystemd-daemon.pc \ libsystemd-login.pc # Passed through intltool only @@ -511,8 +518,7 @@ EXTRA_DIST += \ noinst_LTLIBRARIES = \ libsystemd-basic.la \ - libsystemd-core.la \ - libsystemd-daemon.la + libsystemd-core.la libsystemd_basic_la_SOURCES = \ src/util.c \ @@ -601,9 +607,6 @@ libsystemd_core_la_LIBADD = \ $(PAM_LIBS) \ $(AUDIT_LIBS) -libsystemd_daemon_la_SOURCES = \ - src/sd-daemon.c - # This is needed because automake is buggy in how it generates the # rules for C programs, but not Vala programs. We therefore can't # list the .h files as dependencies if we want make dist to work. @@ -1364,18 +1367,30 @@ pam_systemd_la_LIBADD = \ $(PAM_LIBS) \ $(DBUS_LIBS) -libsystemd_login_la_CFLAGS = \ +libsystemd_daemon_la_SOURCES = \ + src/sd-daemon.c + +libsystemd_daemon_la_CFLAGS = \ $(AM_CFLAGS) \ - -fvisibility=hidden + -fvisibility=hidden \ + -DSD_EXPORT_SYMBOLS -libsystemd_login_la_LDFLAGS = \ +libsystemd_daemon_la_LDFLAGS = \ -shared \ - -version-info $(LIBSYSTEMD_LOGIN_CURRENT):$(LIBSYSTEMD_LOGIN_REVISION):$(LIBSYSTEMD_LOGIN_AGE) + -version-info $(LIBSYSTEMD_DAEMON_CURRENT):$(LIBSYSTEMD_DAEMON_REVISION):$(LIBSYSTEMD_DAEMON_AGE) libsystemd_login_la_SOURCES = \ src/sd-login.c \ src/cgroup-util.c +libsystemd_login_la_CFLAGS = \ + $(AM_CFLAGS) \ + -fvisibility=hidden + +libsystemd_login_la_LDFLAGS = \ + -shared \ + -version-info $(LIBSYSTEMD_LOGIN_CURRENT):$(LIBSYSTEMD_LOGIN_REVISION):$(LIBSYSTEMD_LOGIN_AGE) + libsystemd_login_la_LIBADD = \ libsystemd-basic.la @@ -1512,22 +1527,22 @@ endif DBUS_PREPROCESS = $(CPP) -P $(DBUS_CFLAGS) -imacros dbus/dbus-protocol.h org.freedesktop.systemd1.%.xml: systemd - $(AM_V_GEN)$(OBJCOPY) -O binary -j introspect.$* $< $@.tmp && \ + $(AM_V_GEN)$(LIBTOOL) --mode=execute $(OBJCOPY) -O binary -j introspect.$* $< $@.tmp && \ $(STRINGS) $@.tmp | $(AWK) -f $(srcdir)/introspect.awk | \ $(DBUS_PREPROCESS) -o $@ - && rm $@.tmp org.freedesktop.hostname1.xml: systemd-hostnamed - $(AM_V_GEN)$(OBJCOPY) -O binary -j introspect.hostname1 $< $@.tmp && \ + $(AM_V_GEN)$(LIBTOOL) --mode=execute $(OBJCOPY) -O binary -j introspect.hostname1 $< $@.tmp && \ $(STRINGS) $@.tmp | $(AWK) -f $(srcdir)/introspect.awk | \ $(DBUS_PREPROCESS) -o $@ - && rm $@.tmp org.freedesktop.locale1.xml: systemd-localed - $(AM_V_GEN)$(OBJCOPY) -O binary -j introspect.locale1 $< $@.tmp && \ + $(AM_V_GEN)$(LIBTOOL) --mode=execute $(OBJCOPY) -O binary -j introspect.locale1 $< $@.tmp && \ $(STRINGS) $@.tmp | $(AWK) -f $(srcdir)/introspect.awk | \ $(DBUS_PREPROCESS) -o $@ - && rm $@.tmp org.freedesktop.timedate1.xml: systemd-timedated - $(AM_V_GEN)$(OBJCOPY) -O binary -j introspect.timedate1 $< $@.tmp && \ + $(AM_V_GEN)$(LIBTOOL) --mode=execute $(OBJCOPY) -O binary -j introspect.timedate1 $< $@.tmp && \ $(STRINGS) $@.tmp | $(AWK) -f $(srcdir)/introspect.awk | \ $(DBUS_PREPROCESS) -o $@ - && rm $@.tmp diff --git a/libsystemd-daemon.pc.in b/libsystemd-daemon.pc.in new file mode 100644 index 0000000000..2b6b9d6727 --- /dev/null +++ b/libsystemd-daemon.pc.in @@ -0,0 +1,18 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: systemd +Description: systemd Daemon Utility Library +URL: @PACKAGE_URL@ +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lsystemd-daemon +Cflags: -I${includedir} diff --git a/src/sd-daemon.c b/src/sd-daemon.c index a2ec74cceb..e68b70875c 100644 --- a/src/sd-daemon.c +++ b/src/sd-daemon.c @@ -49,13 +49,19 @@ #include "sd-daemon.h" -#if (__GNUC__ >= 4) && !defined(SD_EXPORT_SYMBOLS) -#define _sd_hidden_ __attribute__ ((visibility("hidden"))) +#if (__GNUC__ >= 4) +#ifdef SD_EXPORT_SYMBOLS +/* Export symbols */ +#define _sd_export_ __attribute__ ((visibility("default"))) #else -#define _sd_hidden_ +/* Don't export the symbols */ +#define _sd_export_ __attribute__ ((visibility("hidden"))) +#endif +#else +#define _sd_export_ #endif -_sd_hidden_ int sd_listen_fds(int unset_environment) { +_sd_export_ int sd_listen_fds(int unset_environment) { #if defined(DISABLE_SYSTEMD) || !defined(__linux__) return 0; @@ -136,7 +142,7 @@ finish: #endif } -_sd_hidden_ int sd_is_fifo(int fd, const char *path) { +_sd_export_ int sd_is_fifo(int fd, const char *path) { struct stat st_fd; if (fd < 0) @@ -169,7 +175,7 @@ _sd_hidden_ int sd_is_fifo(int fd, const char *path) { return 1; } -_sd_hidden_ int sd_is_special(int fd, const char *path) { +_sd_export_ int sd_is_special(int fd, const char *path) { struct stat st_fd; if (fd < 0) @@ -256,7 +262,7 @@ union sockaddr_union { struct sockaddr_storage storage; }; -_sd_hidden_ int sd_is_socket(int fd, int family, int type, int listening) { +_sd_export_ int sd_is_socket(int fd, int family, int type, int listening) { int r; if (family < 0) @@ -284,7 +290,7 @@ _sd_hidden_ int sd_is_socket(int fd, int family, int type, int listening) { return 1; } -_sd_hidden_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) { +_sd_export_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) { union sockaddr_union sockaddr; socklen_t l; int r; @@ -329,7 +335,7 @@ _sd_hidden_ int sd_is_socket_inet(int fd, int family, int type, int listening, u return 1; } -_sd_hidden_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) { +_sd_export_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) { union sockaddr_union sockaddr; socklen_t l; int r; @@ -372,7 +378,7 @@ _sd_hidden_ int sd_is_socket_unix(int fd, int type, int listening, const char *p return 1; } -_sd_hidden_ int sd_is_mq(int fd, const char *path) { +_sd_export_ int sd_is_mq(int fd, const char *path) { #if !defined(__linux__) return 0; #else @@ -409,7 +415,7 @@ _sd_hidden_ int sd_is_mq(int fd, const char *path) { #endif } -_sd_hidden_ int sd_notify(int unset_environment, const char *state) { +_sd_export_ int sd_notify(int unset_environment, const char *state) { #if defined(DISABLE_SYSTEMD) || !defined(__linux__) || !defined(SOCK_CLOEXEC) return 0; #else @@ -477,7 +483,7 @@ finish: #endif } -_sd_hidden_ int sd_notifyf(int unset_environment, const char *format, ...) { +_sd_export_ int sd_notifyf(int unset_environment, const char *format, ...) { #if defined(DISABLE_SYSTEMD) || !defined(__linux__) return 0; #else @@ -499,7 +505,7 @@ _sd_hidden_ int sd_notifyf(int unset_environment, const char *format, ...) { #endif } -_sd_hidden_ int sd_booted(void) { +_sd_export_ int sd_booted(void) { #if defined(DISABLE_SYSTEMD) || !defined(__linux__) return 0; #else -- cgit v1.2.3-54-g00ecf From 3fd476bb11f22969fe761ed17cc64298479c5850 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 14 Jul 2011 23:52:16 +0200 Subject: git: hide more files from git --- .gitignore | 3 +++ m4/.gitignore | 1 + po/.gitignore | 3 +++ 3 files changed, 7 insertions(+) create mode 100644 po/.gitignore (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 1c07f12d32..c02e6bd801 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +org.freedesktop.hostname1.xml +org.freedesktop.locale1.xml +org.freedesktop.timedate1.xml libsystemd-daemon.pc libsystemd-login.pc test-login diff --git a/m4/.gitignore b/m4/.gitignore index 38066ddf7c..55eaa803a1 100644 --- a/m4/.gitignore +++ b/m4/.gitignore @@ -1,3 +1,4 @@ +intltool.m4 libtool.m4 ltoptions.m4 ltsugar.m4 diff --git a/po/.gitignore b/po/.gitignore new file mode 100644 index 0000000000..251edd4c81 --- /dev/null +++ b/po/.gitignore @@ -0,0 +1,3 @@ +POTFILES +Makefile.in.in +.intltool-merge-cache -- cgit v1.2.3-54-g00ecf From 830964834f330836b9d33752e83de09d4f38da87 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 Jul 2011 04:21:18 +0200 Subject: install: add new installer implementation This new installer will replace the current code of "systemctl enable" but also be available via D-Bus. It adds a couple of new features: - Mask/Unmask calls - Reenable call - Preset call - Support for enabling units temporarily (i.e. in /run/systemd instead of /etc/systemd) - Enumeration of installed units - Support for out-of-search-path units systemctl and D-Bus are not hooked up with this yet --- .gitignore | 1 + Makefile.am | 16 +- src/install.c | 1944 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/install.h | 86 +++ src/test-install.c | 264 +++++++ src/util.c | 81 +++ src/util.h | 6 + 7 files changed, 2397 insertions(+), 1 deletion(-) create mode 100644 src/install.c create mode 100644 src/install.h create mode 100644 src/test-install.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index c02e6bd801..6cfb3ed265 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +test-install org.freedesktop.hostname1.xml org.freedesktop.locale1.xml org.freedesktop.timedate1.xml diff --git a/Makefile.am b/Makefile.am index 70d1c92353..af44b13908 100644 --- a/Makefile.am +++ b/Makefile.am @@ -210,7 +210,8 @@ noinst_PROGRAMS = \ test-cgroup \ test-env-replace \ test-strv \ - test-login + test-login \ + test-install if HAVE_PAM pamlib_LTLIBRARIES = \ @@ -831,6 +832,19 @@ test_login_LDADD = \ libsystemd-basic.la \ libsystemd-login.la +test_install_SOURCES = \ + src/test-install.c \ + src/install.c \ + src/path-lookup.c \ + src/unit-name.c + +test_install_CFLAGS = \ + $(AM_CFLAGS) \ + $(DBUS_CFLAGS) + +test_install_LDADD = \ + libsystemd-basic.la + systemd_logger_SOURCES = \ src/logger.c \ src/tcpwrap.c diff --git a/src/install.c b/src/install.c new file mode 100644 index 0000000000..e1c69444f9 --- /dev/null +++ b/src/install.c @@ -0,0 +1,1944 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty . +***/ + +#include +#include +#include +#include +#include + +#include "util.h" +#include "hashmap.h" +#include "set.h" +#include "path-lookup.h" +#include "strv.h" +#include "unit-name.h" +#include "install.h" +#include "conf-parser.h" + +typedef struct { + char *name; + char *path; + + char **aliases; + char **wanted_by; +} InstallInfo; + +typedef struct { + Hashmap *will_install; + Hashmap *have_installed; +} InstallContext; + +static int lookup_paths_init_from_scope(LookupPaths *paths, UnitFileScope scope) { + assert(paths); + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + zero(*paths); + + return lookup_paths_init(paths, + scope == UNIT_FILE_SYSTEM ? MANAGER_SYSTEM : MANAGER_USER, + scope == UNIT_FILE_USER); +} + +static int get_config_path(UnitFileScope scope, bool runtime, const char *root_dir, char **ret) { + char *p = NULL; + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(ret); + + switch (scope) { + + case UNIT_FILE_SYSTEM: + + if (root_dir && runtime) + return -EINVAL; + + if (runtime) + p = strdup("/run/systemd/system"); + else if (root_dir) + asprintf(&p, "%s/%s", root_dir, SYSTEM_CONFIG_UNIT_PATH); + else + p = strdup(SYSTEM_CONFIG_UNIT_PATH); + + break; + + case UNIT_FILE_GLOBAL: + + if (root_dir) + return -EINVAL; + + if (runtime) + p = strdup("/run/systemd/user"); + else + p = strdup(USER_CONFIG_UNIT_PATH); + break; + + case UNIT_FILE_USER: + + if (root_dir || runtime) + return -EINVAL; + + r = user_config_home(&p); + if (r <= 0) + return r < 0 ? r : -ENOENT; + + break; + + default: + assert_not_reached("Bad scope"); + } + + if (!p) + return -ENOMEM; + + *ret = p; + return 0; +} + +static int add_file_change( + UnitFileChange **changes, + unsigned *n_changes, + UnitFileChangeType type, + const char *path, + const char *source) { + + UnitFileChange *c; + unsigned i; + + assert(type >= 0); + assert(type < _UNIT_FILE_CHANGE_TYPE_MAX); + assert(path); + assert(!changes == !n_changes); + + if (!changes) + return 0; + + c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange)); + if (!c) + return -ENOMEM; + + *changes = c; + i = *n_changes; + + c[i].type = type; + c[i].path = strdup(path); + if (!c[i].path) + return -ENOMEM; + + if (source) { + c[i].source = strdup(source); + if (!c[i].source) { + free(c[i].path); + return -ENOMEM; + } + } else + c[i].source = NULL; + + *n_changes = i+1; + return 0; +} + +static int mark_symlink_for_removal( + Set **remove_symlinks_to, + const char *p) { + + char *n; + int r; + + assert(p); + + r = set_ensure_allocated(remove_symlinks_to, string_hash_func, string_compare_func); + if (r < 0) + return r; + + n = strdup(p); + if (!n) + return -ENOMEM; + + path_kill_slashes(n); + + r = set_put(*remove_symlinks_to, n); + if (r < 0) { + free(n); + return r == -EEXIST ? 0 : r; + } + + return 0; +} + +static int remove_marked_symlinks_fd( + Set *remove_symlinks_to, + int fd, + const char *path, + const char *config_path, + bool *deleted, + UnitFileChange **changes, + unsigned *n_changes) { + + int r = 0; + DIR *d; + struct dirent buffer, *de; + + assert(remove_symlinks_to); + assert(fd >= 0); + assert(path); + assert(config_path); + assert(deleted); + + d = fdopendir(fd); + if (!d) { + close_nointr_nofail(fd); + return -errno; + } + + rewinddir(d); + + for (;;) { + int k; + + k = readdir_r(d, &buffer, &de); + if (k != 0) { + r = -errno; + break; + } + + if (!de) + break; + + if (ignore_file(de->d_name)) + continue; + + dirent_ensure_type(d, de); + + if (de->d_type == DT_DIR) { + int nfd, q; + char *p; + + nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); + if (nfd < 0) { + if (errno == ENOENT) + continue; + + if (r == 0) + r = -errno; + continue; + } + + p = path_make_absolute(de->d_name, path); + if (!p) { + close_nointr_nofail(nfd); + r = -ENOMEM; + break; + } + + /* This will close nfd, regardless whether it succeeds or not */ + q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, deleted, changes, n_changes); + free(p); + + if (r == 0) + r = q; + + } else if (de->d_type == DT_LNK) { + char *p, *dest; + int q; + bool found; + + p = path_make_absolute(de->d_name, path); + if (!p) { + r = -ENOMEM; + break; + } + + q = readlink_and_canonicalize(p, &dest); + if (q < 0) { + free(p); + + if (q == -ENOENT) + continue; + + if (r == 0) + r = q; + continue; + } + + found = + set_get(remove_symlinks_to, dest) || + set_get(remove_symlinks_to, file_name_from_path(dest)); + + if (found) { + + if (unlink(p) < 0 && errno != ENOENT) { + + if (r == 0) + r = -errno; + } else { + rmdir_parents(p, config_path); + path_kill_slashes(p); + + add_file_change(changes, n_changes, UNIT_FILE_UNLINK, p, NULL); + + if (!set_get(remove_symlinks_to, p)) { + + q = mark_symlink_for_removal(&remove_symlinks_to, p); + if (q < 0) { + if (r == 0) + r = q; + } else + *deleted = true; + } + } + } + + free(p); + free(dest); + } + } + + closedir(d); + + return r; +} + +static int remove_marked_symlinks( + Set *remove_symlinks_to, + const char *config_path, + UnitFileChange **changes, + unsigned *n_changes) { + + int fd, r = 0; + bool deleted; + + assert(config_path); + + if (set_size(remove_symlinks_to) <= 0) + return 0; + + fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); + if (fd < 0) + return -errno; + + do { + int q, cfd; + deleted = false; + + cfd = dup(fd); + if (cfd < 0) { + r = -errno; + break; + } + + /* This takes possession of cfd and closes it */ + q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, &deleted, changes, n_changes); + if (r == 0) + r = q; + } while (deleted); + + close_nointr_nofail(fd); + + return r; +} + +static int find_symlinks_fd( + const char *name, + int fd, + const char *path, + const char *config_path, + bool *same_name_link) { + + int r = 0; + DIR *d; + struct dirent buffer, *de; + + assert(name); + assert(fd >= 0); + assert(path); + assert(config_path); + assert(same_name_link); + + d = fdopendir(fd); + if (!d) { + close_nointr_nofail(fd); + return -errno; + } + + for (;;) { + int k; + + k = readdir_r(d, &buffer, &de); + if (k != 0) { + r = -errno; + break; + } + + if (!de) + break; + + if (ignore_file(de->d_name)) + continue; + + dirent_ensure_type(d, de); + + if (de->d_type == DT_DIR) { + int nfd, q; + char *p; + + nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); + if (nfd < 0) { + if (errno == ENOENT) + continue; + + if (r == 0) + r = -errno; + continue; + } + + p = path_make_absolute(de->d_name, path); + if (!p) { + close_nointr_nofail(nfd); + r = -ENOMEM; + break; + } + + /* This will close nfd, regardless whether it succeeds or not */ + q = find_symlinks_fd(name, nfd, p, config_path, same_name_link); + free(p); + + if (q > 0) { + r = 1; + break; + } + + if (r == 0) + r = q; + + } else if (de->d_type == DT_LNK) { + char *p, *dest; + bool found_path, found_dest, b = false; + int q; + + /* Acquire symlink name */ + p = path_make_absolute(de->d_name, path); + if (!p) { + r = -ENOMEM; + break; + } + + /* Acquire symlink destination */ + q = readlink_and_canonicalize(p, &dest); + if (q < 0) { + free(p); + + if (q == -ENOENT) + continue; + + if (r == 0) + r = q; + continue; + } + + /* Check if the symlink itself matches what we + * are looking for */ + if (path_is_absolute(name)) + found_path = path_equal(p, name); + else + found_path = streq(de->d_name, name); + + /* Check if what the symlink points to + * matches what we are looking for */ + if (path_is_absolute(name)) + found_dest = path_equal(dest, name); + else + found_dest = streq(file_name_from_path(dest), name); + + free(dest); + + if (found_path && found_dest) { + char *t; + + /* Filter out same name links in the main + * config path */ + t = path_make_absolute(name, config_path); + if (!t) { + free(p); + free(dest); + r = -ENOMEM; + break; + } + + b = path_equal(t, p); + free(t); + } + + free(p); + + if (b) + *same_name_link = true; + else if (found_path || found_dest) { + r = 1; + break; + } + } + } + + closedir(d); + + return r; +} + +static int find_symlinks( + const char *name, + const char *config_path, + bool *same_name_link) { + + int fd; + + assert(name); + assert(config_path); + assert(same_name_link); + + fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); + if (fd < 0) + return -errno; + + /* This takes possession of fd and closes it */ + return find_symlinks_fd(name, fd, config_path, config_path, same_name_link); +} + +static int find_symlinks_in_scope( + UnitFileScope scope, + const char *root_dir, + const char *name, + UnitFileState *state) { + + int r; + char *path; + bool same_name_link_runtime = false, same_name_link = false; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(name); + + if (scope == UNIT_FILE_SYSTEM || scope == UNIT_FILE_GLOBAL) { + + /* First look in runtime config path */ + r = get_config_path(scope, true, root_dir, &path); + if (r < 0) + return r; + + r = find_symlinks(name, path, &same_name_link_runtime); + free(path); + + if (r < 0) + return r; + else if (r > 0) { + *state = UNIT_FILE_ENABLED_RUNTIME; + return r; + } + } + + /* Then look in the normal config path */ + r = get_config_path(scope, false, root_dir, &path); + if (r < 0) + return r; + + r = find_symlinks(name, path, &same_name_link); + free(path); + + if (r < 0) + return r; + else if (r > 0) { + *state = UNIT_FILE_ENABLED; + return r; + } + + /* Hmm, we didn't find it, but maybe we found the same name + * link? */ + if (same_name_link_runtime) { + *state = UNIT_FILE_LINKED_RUNTIME; + return 1; + } else if (same_name_link) { + *state = UNIT_FILE_LINKED; + return 1; + } + + return 0; +} + +int unit_file_mask( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char *files[], + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + char **i, *prefix; + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + r = get_config_path(scope, runtime, root_dir, &prefix); + if (r < 0) + return r; + + STRV_FOREACH(i, files) { + char *path; + + if (!unit_name_is_valid_no_type(*i, true)) { + if (r == 0) + r = -EINVAL; + continue; + } + + path = path_make_absolute(*i, prefix); + if (!path) { + r = -ENOMEM; + break; + } + + if (symlink("/dev/null", path) >= 0) { + add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null"); + + free(path); + continue; + } + + if (errno == EEXIST) { + + if (null_or_empty_path(path) > 0) { + free(path); + continue; + } + + if (force) { + unlink(path); + + if (symlink("/dev/null", path) >= 0) { + + add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); + add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null"); + + free(path); + continue; + } + } + + if (r == 0) + r = -EEXIST; + } else { + if (r == 0) + r = -errno; + } + + free(path); + } + + free(prefix); + + return r; +} + +int unit_file_unmask( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char *files[], + UnitFileChange **changes, + unsigned *n_changes) { + + char **i, *config_path = NULL; + int r, q; + Set *remove_symlinks_to = NULL; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + r = get_config_path(scope, runtime, root_dir, &config_path); + if (r < 0) + goto finish; + + STRV_FOREACH(i, files) { + char *path; + + if (!unit_name_is_valid_no_type(*i, true)) { + if (r == 0) + r = -EINVAL; + continue; + } + + path = path_make_absolute(*i, config_path); + if (!path) { + r = -ENOMEM; + break; + } + + q = null_or_empty_path(path); + if (q > 0) { + if (unlink(path) >= 0) { + mark_symlink_for_removal(&remove_symlinks_to, path); + add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); + + free(path); + continue; + } + + q = -errno; + } + + if (q != -ENOENT && r == 0) + r = q; + + free(path); + } + + +finish: + q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); + if (r == 0) + r = q; + + set_free_free(remove_symlinks_to); + free(config_path); + + return r; +} + +int unit_file_link( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char *files[], + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + LookupPaths paths; + char **i, *config_path = NULL; + int r, q; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + zero(paths); + + r = lookup_paths_init_from_scope(&paths, scope); + if (r < 0) + return r; + + r = get_config_path(scope, runtime, root_dir, &config_path); + if (r < 0) + goto finish; + + STRV_FOREACH(i, files) { + char *path, *fn; + struct stat st; + + fn = file_name_from_path(*i); + + if (!path_is_absolute(*i) || + !unit_name_is_valid_no_type(fn, true)) { + if (r == 0) + r = -EINVAL; + continue; + } + + if (lstat(*i, &st) < 0) { + if (r == 0) + r = -errno; + continue; + } + + if (!S_ISREG(st.st_mode)) { + r = -ENOENT; + continue; + } + + q = in_search_path(*i, paths.unit_path); + if (q < 0) { + r = q; + break; + } + + if (q > 0) + continue; + + path = path_make_absolute(fn, config_path); + if (!path) { + r = -ENOMEM; + break; + } + + if (symlink(*i, path) >= 0) { + add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i); + + free(path); + continue; + } + + if (errno == EEXIST) { + char *dest = NULL; + + q = readlink_and_make_absolute(path, &dest); + + if (q < 0 && errno != ENOENT) { + free(path); + + if (r == 0) + r = q; + + continue; + } + + if (q >= 0 && path_equal(dest, *i)) { + free(dest); + free(path); + continue; + } + + free(dest); + + if (force) { + unlink(path); + + if (symlink(*i, path) >= 0) { + + add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); + add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i); + + free(path); + continue; + } + } + + if (r == 0) + r = -EEXIST; + } else { + if (r == 0) + r = -errno; + } + + free(path); + } + + finish: + lookup_paths_free(&paths); + free(config_path); + + return r; +} + +void unit_file_list_free(Hashmap *h) { + UnitFileList *i; + + while ((i = hashmap_steal_first(h))) { + free(i->path); + free(i); + } + + hashmap_free(h); +} + +void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) { + unsigned i; + + assert(changes || n_changes == 0); + + if (!changes) + return; + + for (i = 0; i < n_changes; i++) { + free(changes[i].path); + free(changes[i].source); + } + + free(changes); +} + +static void install_info_free(InstallInfo *i) { + assert(i); + + free(i->name); + free(i->path); + strv_free(i->aliases); + strv_free(i->wanted_by); + free(i); +} + +static void install_info_hashmap_free(Hashmap *m) { + InstallInfo *i; + + if (!m) + return; + + while ((i = hashmap_steal_first(m))) + install_info_free(i); + + hashmap_free(m); +} + +static void install_context_done(InstallContext *c) { + assert(c); + + install_info_hashmap_free(c->will_install); + install_info_hashmap_free(c->have_installed); + + c->will_install = c->have_installed = NULL; +} + +static int install_info_add( + InstallContext *c, + const char *name, + const char *path) { + InstallInfo *i = NULL; + int r; + + assert(c); + assert(name || path); + + if (!name) + name = file_name_from_path(path); + + if (!unit_name_is_valid_no_type(name, true)) + return -EINVAL; + + if (hashmap_get(c->have_installed, name) || + hashmap_get(c->will_install, name)) + return 0; + + r = hashmap_ensure_allocated(&c->will_install, string_hash_func, string_compare_func); + if (r < 0) + return r; + + i = new0(InstallInfo, 1); + if (!i) + return -ENOMEM; + + i->name = strdup(name); + if (!i->name) { + r = -ENOMEM; + goto fail; + } + + if (path) { + i->path = strdup(path); + if (!i->path) { + r = -ENOMEM; + goto fail; + } + } + + r = hashmap_put(c->will_install, i->name, i); + if (r < 0) + goto fail; + + return 0; + +fail: + if (i) + install_info_free(i); + + return r; +} + +static int install_info_add_auto( + InstallContext *c, + const char *name_or_path) { + + assert(c); + assert(name_or_path); + + if (path_is_absolute(name_or_path)) + return install_info_add(c, NULL, name_or_path); + else + return install_info_add(c, name_or_path, NULL); +} + +static int config_parse_also( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + char *w; + size_t l; + char *state; + InstallContext *c = data; + + assert(filename); + assert(lvalue); + assert(rvalue); + + FOREACH_WORD_QUOTED(w, l, rvalue, state) { + char *n; + int r; + + n = strndup(w, l); + if (!n) + return -ENOMEM; + + r = install_info_add(c, n, NULL); + if (r < 0) { + free(n); + return r; + } + + free(n); + } + + return 0; +} + +static int unit_file_load( + InstallContext *c, + InstallInfo *info, + const char *path, + bool allow_symlink) { + + const ConfigItem items[] = { + { "Alias", config_parse_strv, 0, &info->aliases, "Install" }, + { "WantedBy", config_parse_strv, 0, &info->wanted_by, "Install" }, + { "Also", config_parse_also, 0, c, "Install" }, + { NULL, NULL, 0, NULL, NULL } + }; + + int fd; + FILE *f; + int r; + + assert(c); + assert(info); + assert(path); + + fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|(allow_symlink ? 0 : O_NOFOLLOW)); + if (fd < 0) + return -errno; + + f = fdopen(fd, "re"); + if (!f) { + close_nointr_nofail(fd); + return -ENOMEM; + } + + r = config_parse(path, f, NULL, items, true, info); + fclose(f); + if (r < 0) + return r; + + return strv_length(info->aliases) + strv_length(info->wanted_by); +} + +static int unit_file_search( + InstallContext *c, + InstallInfo *info, + LookupPaths *paths, + const char *root_dir, + bool allow_symlink) { + + char **p; + int r; + + assert(c); + assert(info); + assert(paths); + + if (info->path) + return unit_file_load(c, info, info->path, allow_symlink); + + assert(info->name); + + STRV_FOREACH(p, paths->unit_path) { + char *path = NULL; + + if (isempty(root_dir)) + asprintf(&path, "%s/%s", *p, info->name); + else + asprintf(&path, "%s/%s/%s", root_dir, *p, info->name); + + if (!path) + return -ENOMEM; + + r = unit_file_load(c, info, path, allow_symlink); + + if (r >= 0) + info->path = path; + else + free(path); + + if (r != -ENOENT && r != -ELOOP) + return r; + } + + return -ENOENT; +} + +static int unit_file_can_install( + LookupPaths *paths, + const char *root_dir, + const char *name, + bool allow_symlink) { + + InstallContext c; + InstallInfo *i; + int r; + + assert(paths); + assert(name); + + zero(c); + + r = install_info_add_auto(&c, name); + if (r < 0) + return r; + + assert_se(i = hashmap_first(c.will_install)); + + r = unit_file_search(&c, i, paths, root_dir, allow_symlink); + + if (r >= 0) + r = strv_length(i->aliases) + strv_length(i->wanted_by); + + install_context_done(&c); + + return r; +} + +static int create_symlink( + const char *old_path, + const char *new_path, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + char *dest; + int r; + + assert(old_path); + assert(new_path); + + mkdir_parents(new_path, 0755); + + if (symlink(old_path, new_path) >= 0) { + add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path); + return 0; + } + + if (errno != EEXIST) + return -errno; + + r = readlink_and_make_absolute(new_path, &dest); + if (r < 0) + return r; + + if (path_equal(dest, old_path)) { + free(dest); + return 0; + } + + free(dest); + + if (force) + return -EEXIST; + + unlink(new_path); + + if (symlink(old_path, new_path) >= 0) { + add_file_change(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL); + add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path); + return 0; + } + + return -errno; +} + +static int install_info_symlink_alias( + InstallInfo *i, + const char *config_path, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + char **s; + int r = 0, q; + + assert(i); + assert(config_path); + + STRV_FOREACH(s, i->aliases) { + char *alias_path; + + alias_path = path_make_absolute(*s, config_path); + + if (!alias_path) + return -ENOMEM; + + q = create_symlink(i->path, alias_path, force, changes, n_changes); + free(alias_path); + + if (r == 0) + r = q; + } + + return r; +} + +static int install_info_symlink_wants( + InstallInfo *i, + const char *config_path, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + char **s; + int r = 0, q; + + assert(i); + assert(config_path); + + STRV_FOREACH(s, i->wanted_by) { + char *path; + + if (!unit_name_is_valid_no_type(*s, true)) { + r = -EINVAL; + continue; + } + + if (asprintf(&path, "%s/%s.wants/%s", config_path, *s, i->name) < 0) + return -ENOMEM; + + q = create_symlink(i->path, path, force, changes, n_changes); + free(path); + + if (r == 0) + r = q; + } + + return r; +} + +static int install_info_symlink_link( + InstallInfo *i, + LookupPaths *paths, + const char *config_path, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + int r; + char *path; + + assert(i); + assert(paths); + assert(config_path); + assert(i->path); + + r = in_search_path(i->path, paths->unit_path); + if (r != 0) + return r; + + if (asprintf(&path, "%s/%s", config_path, i->name) < 0) + return -ENOMEM; + + r = create_symlink(i->path, path, force, changes, n_changes); + free(path); + + return r; +} + +static int install_info_apply( + InstallInfo *i, + LookupPaths *paths, + const char *config_path, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + int r, q; + + assert(i); + assert(paths); + assert(config_path); + + r = install_info_symlink_alias(i, config_path, force, changes, n_changes); + + q = install_info_symlink_wants(i, config_path, force, changes, n_changes); + if (r == 0) + r = q; + + q = install_info_symlink_link(i, paths, config_path, force, changes, n_changes); + if (r == 0) + r = q; + + return r; +} + +static int install_context_apply( + InstallContext *c, + LookupPaths *paths, + const char *config_path, + const char *root_dir, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + InstallInfo *i; + int r = 0, q; + + assert(c); + assert(paths); + assert(config_path); + + while ((i = hashmap_first(c->will_install))) { + + q = hashmap_ensure_allocated(&c->have_installed, string_hash_func, string_compare_func); + if (q < 0) + return q; + + assert_se(hashmap_move_one(c->have_installed, c->will_install, i->name) == 0); + + q = unit_file_search(c, i, paths, root_dir, false); + if (q < 0) { + if (r >= 0) + r = q; + + return r; + } else if (r >= 0) + r += q; + + q = install_info_apply(i, paths, config_path, force, changes, n_changes); + if (r >= 0 && q < 0) + r = q; + } + + return r; +} + +static int install_context_mark_for_removal( + InstallContext *c, + LookupPaths *paths, + Set **remove_symlinks_to, + const char *config_path, + const char *root_dir) { + + InstallInfo *i; + int r = 0, q; + + assert(c); + assert(paths); + assert(config_path); + + /* Marks all items for removal */ + + while ((i = hashmap_first(c->will_install))) { + + q = hashmap_ensure_allocated(&c->have_installed, string_hash_func, string_compare_func); + if (q < 0) + return q; + + assert_se(hashmap_move_one(c->have_installed, c->will_install, i->name) == 0); + + q = unit_file_search(c, i, paths, root_dir, false); + if (q < 0) { + if (r >= 0) + r = q; + + return r; + } else if (r >= 0) + r += q; + + q = mark_symlink_for_removal(remove_symlinks_to, i->name); + if (r >= 0 && q < 0) + r = q; + } + + return r; +} + +int unit_file_enable( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char *files[], + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + LookupPaths paths; + InstallContext c; + char **i, *config_path = NULL; + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + zero(paths); + zero(c); + + r = lookup_paths_init_from_scope(&paths, scope); + if (r < 0) + return r; + + r = get_config_path(scope, runtime, root_dir, &config_path); + if (r < 0) + goto finish; + + STRV_FOREACH(i, files) { + r = install_info_add_auto(&c, *i); + if (r < 0) + goto finish; + } + + r = install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes); + +finish: + install_context_done(&c); + lookup_paths_free(&paths); + free(config_path); + + return r; +} + +int unit_file_disable( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char *files[], + UnitFileChange **changes, + unsigned *n_changes) { + + LookupPaths paths; + InstallContext c; + char **i, *config_path = NULL; + Set *remove_symlinks_to = NULL; + int r, q; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + zero(paths); + zero(c); + + r = lookup_paths_init_from_scope(&paths, scope); + if (r < 0) + return r; + + r = get_config_path(scope, runtime, root_dir, &config_path); + if (r < 0) + goto finish; + + STRV_FOREACH(i, files) { + r = install_info_add_auto(&c, *i); + if (r < 0) + goto finish; + } + + r = install_context_mark_for_removal(&c, &paths, &remove_symlinks_to, config_path, root_dir); + + q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); + if (r == 0) + r = 1; + +finish: + install_context_done(&c); + lookup_paths_free(&paths); + set_free_free(remove_symlinks_to); + free(config_path); + + return r; +} + +int unit_file_reenable( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char *files[], + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + LookupPaths paths; + InstallContext c; + char **i, *config_path = NULL; + Set *remove_symlinks_to = NULL; + int r, q; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + zero(paths); + zero(c); + + r = lookup_paths_init_from_scope(&paths, scope); + if (r < 0) + return r; + + r = get_config_path(scope, runtime, root_dir, &config_path); + if (r < 0) + goto finish; + + STRV_FOREACH(i, files) { + r = mark_symlink_for_removal(&remove_symlinks_to, *i); + if (r < 0) + goto finish; + + r = install_info_add_auto(&c, *i); + if (r < 0) + goto finish; + } + + r = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); + + q = install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes); + if (r == 0) + r = q; + +finish: + lookup_paths_free(&paths); + install_context_done(&c); + set_free_free(remove_symlinks_to); + free(config_path); + + return r; +} + +UnitFileState unit_file_get_state( + UnitFileScope scope, + const char *root_dir, + const char *name) { + + LookupPaths paths; + UnitFileState state = _UNIT_FILE_STATE_INVALID; + char **i, *path = NULL; + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(name); + + zero(paths); + + if (root_dir && scope != UNIT_FILE_SYSTEM) + return -EINVAL; + + if (!unit_name_is_valid_no_type(name, true)) + return -EINVAL; + + r = lookup_paths_init_from_scope(&paths, scope); + if (r < 0) + return r; + + STRV_FOREACH(i, paths.unit_path) { + struct stat st; + + free(path); + path = NULL; + + if (root_dir) + asprintf(&path, "%s/%s/%s", root_dir, *i, name); + else + asprintf(&path, "%s/%s", *i, name); + + if (!path) { + r = -ENOMEM; + goto finish; + } + + if (lstat(path, &st) < 0) { + if (errno == ENOENT) + continue; + + r = -errno; + goto finish; + } + + if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) { + r = -ENOENT; + goto finish; + } + + r = null_or_empty_path(path); + if (r < 0 && r != -ENOENT) + goto finish; + else if (r > 0) { + state = path_startswith(*i, "/run") ? + UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED; + r = 0; + goto finish; + } + + r = find_symlinks_in_scope(scope, root_dir, name, &state); + if (r < 0) { + goto finish; + } else if (r > 0) { + r = 0; + goto finish; + } + + r = unit_file_can_install(&paths, root_dir, path, true); + if (r < 0 && errno != -ENOENT) + goto finish; + else if (r > 0) { + state = UNIT_FILE_DISABLED; + r = 0; + goto finish; + } else if (r == 0) { + state = UNIT_FILE_STATIC; + r = 0; + goto finish; + } + } + +finish: + lookup_paths_free(&paths); + free(path); + + return r < 0 ? r : state; +} + +int unit_file_query_preset(UnitFileScope scope, const char *name) { + char **files, **i; + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(name); + + if (scope == UNIT_FILE_SYSTEM) + r = conf_files_list(&files, ".preset", + "/etc/systemd/system.preset", + "/usr/local/lib/systemd/system.preset", + "/usr/lib/systemd/system.preset", + "/lib/systemd/system.preset", + NULL); + else if (scope == UNIT_FILE_GLOBAL) + r = conf_files_list(&files, ".preset", + "/etc/systemd/user.preset", + "/usr/local/lib/systemd/user.preset", + "/usr/lib/systemd/user.preset", + NULL); + else + return 1; + + if (r < 0) + return r; + + STRV_FOREACH(i, files) { + FILE *f; + + f = fopen(*i, "re"); + if (!f) { + if (errno == ENOENT) + continue; + + r = -errno; + goto finish; + } + + for (;;) { + char line[LINE_MAX], *l; + + if (!fgets(line, sizeof(line), f)) + break; + + l = strstrip(line); + if (!*l) + continue; + + if (strchr(COMMENTS, *l)) + continue; + + if (first_word(l, "enable")) { + l += 6; + l += strspn(l, WHITESPACE); + + if (fnmatch(l, name, FNM_NOESCAPE) == 0) { + r = 1; + fclose(f); + goto finish; + } + } else if (first_word(l, "disable")) { + l += 7; + l += strspn(l, WHITESPACE); + + if (fnmatch(l, name, FNM_NOESCAPE) == 0) { + r = 0; + fclose(f); + goto finish; + } + } else + log_debug("Couldn't parse line '%s'", l); + } + + fclose(f); + } + + /* Default is "enable" */ + r = 1; + +finish: + strv_free(files); + + return r; +} + +int unit_file_preset( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char *files[], + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + LookupPaths paths; + InstallContext plus, minus; + char **i, *config_path = NULL; + Set *remove_symlinks_to = NULL; + int r, q; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + zero(paths); + zero(plus); + zero(minus); + + r = lookup_paths_init_from_scope(&paths, scope); + if (r < 0) + return r; + + r = get_config_path(scope, runtime, root_dir, &config_path); + if (r < 0) + goto finish; + + STRV_FOREACH(i, files) { + + if (!unit_name_is_valid_no_type(*i, true)) { + r = -EINVAL; + goto finish; + } + + r = unit_file_query_preset(scope, *i); + if (r < 0) + goto finish; + + if (r) + r = install_info_add_auto(&plus, *i); + else + r = install_info_add_auto(&minus, *i); + + if (r < 0) + goto finish; + } + + r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir); + + q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); + if (r == 0) + r = q; + + q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes); + if (r == 0) + r = q; + +finish: + lookup_paths_free(&paths); + install_context_done(&plus); + install_context_done(&minus); + set_free_free(remove_symlinks_to); + free(config_path); + + return r; +} + +int unit_file_get_list( + UnitFileScope scope, + const char *root_dir, + Hashmap *h) { + + LookupPaths paths; + char **i, *buf = NULL; + DIR *d = NULL; + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(h); + + zero(paths); + + if (root_dir && scope != UNIT_FILE_SYSTEM) + return -EINVAL; + + r = lookup_paths_init_from_scope(&paths, scope); + if (r < 0) + return r; + + STRV_FOREACH(i, paths.unit_path) { + struct dirent buffer, *de; + const char *units_dir; + + free(buf); + buf = NULL; + + if (root_dir) { + if (asprintf(&buf, "%s/%s", root_dir, *i) < 0) { + r = -ENOMEM; + goto finish; + } + units_dir = buf; + } else + units_dir = *i; + + if (d) + closedir(d); + + d = opendir(units_dir); + if (!d) { + if (errno == ENOENT) + continue; + + r = -errno; + goto finish; + } + + for (;;) { + UnitFileList *f; + + r = readdir_r(d, &buffer, &de); + if (r != 0) { + r = -r; + goto finish; + } + + if (!de) + break; + + if (ignore_file(de->d_name)) + continue; + + if (!unit_name_is_valid_no_type(de->d_name, true)) + continue; + + if (hashmap_get(h, de->d_name)) + continue; + + r = dirent_ensure_type(d, de); + if (r < 0) { + if (errno == ENOENT) + continue; + + goto finish; + } + + if (de->d_type != DT_LNK && de->d_type != DT_REG) + continue; + + f = new0(UnitFileList, 1); + if (!f) { + r = -ENOMEM; + goto finish; + } + + f->path = path_make_absolute(de->d_name, units_dir); + if (!f->path) { + free(f); + r = -ENOMEM; + goto finish; + } + + r = null_or_empty_path(f->path); + if (r < 0) { + free(f->path); + free(f); + goto finish; + } else if (r > 0) { + f->state = + path_startswith(*i, "/run") ? + UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED; + goto found; + } + + r = find_symlinks_in_scope(scope, root_dir, de->d_name, &f->state); + if (r < 0) { + free(f->path); + free(f); + goto finish; + } else if (r > 0) + goto found; + + r = unit_file_can_install(&paths, root_dir, f->path, true); + if (r < 0) { + free(f->path); + free(f); + goto finish; + } else if (r > 0) { + f->state = UNIT_FILE_DISABLED; + goto found; + } else if (r == 0) { + f->state = UNIT_FILE_STATIC; + goto found; + } + + free(f->path); + free(f); + continue; + + found: + r = hashmap_put(h, file_name_from_path(f->path), f); + if (r < 0) { + free(f->path); + free(f); + goto finish; + } + } + } + +finish: + lookup_paths_free(&paths); + free(buf); + + if (d) + closedir(d); + + return r; +} + +static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = { + [UNIT_FILE_ENABLED] = "enabled", + [UNIT_FILE_ENABLED_RUNTIME] = "enabled-runtie", + [UNIT_FILE_LINKED] = "linked", + [UNIT_FILE_LINKED_RUNTIME] = "linked-runtime", + [UNIT_FILE_MASKED] = "masked", + [UNIT_FILE_MASKED_RUNTIME] = "masked-runtime", + [UNIT_FILE_STATIC] = "static", + [UNIT_FILE_DISABLED] = "disabled" +}; + +DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState); diff --git a/src/install.h b/src/install.h new file mode 100644 index 0000000000..a3eacf51ce --- /dev/null +++ b/src/install.h @@ -0,0 +1,86 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef fooinstallhfoo +#define fooinstallhfoo + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include "hashmap.h" + +typedef enum UnitFileScope { + UNIT_FILE_SYSTEM, + UNIT_FILE_GLOBAL, + UNIT_FILE_USER, + _UNIT_FILE_SCOPE_MAX, + _UNIT_FILE_SCOPE_INVALID = -1 +} UnitFileScope; + +typedef enum UnitFileState { + UNIT_FILE_ENABLED, + UNIT_FILE_ENABLED_RUNTIME, + UNIT_FILE_LINKED, + UNIT_FILE_LINKED_RUNTIME, + UNIT_FILE_MASKED, + UNIT_FILE_MASKED_RUNTIME, + UNIT_FILE_STATIC, + UNIT_FILE_DISABLED, + _UNIT_FILE_STATE_MAX, + _UNIT_FILE_STATE_INVALID = -1 +} UnitFileState; + +typedef enum UnitFileChangeType { + UNIT_FILE_SYMLINK, + UNIT_FILE_UNLINK, + _UNIT_FILE_CHANGE_TYPE_MAX, + _UNIT_FILE_CHANGE_TYPE_INVALID = -1 +} UnitFileChangeType; + +typedef struct UnitFileChange { + UnitFileChangeType type; + char *path; + char *source; +} UnitFileChange; + +typedef struct UnitFileList { + char *path; + UnitFileState state; +} UnitFileList; + +int unit_file_enable(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes); +int unit_file_disable(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], UnitFileChange **changes, unsigned *n_changes); +int unit_file_reenable(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes); +int unit_file_link(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes); +int unit_file_preset(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes); +int unit_file_mask(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes); +int unit_file_unmask(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], UnitFileChange **changes, unsigned *n_changes); + +UnitFileState unit_file_get_state(UnitFileScope scope, const char *root_dir, const char *filename); + +int unit_file_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h); + +void unit_file_list_free(Hashmap *h); +void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes); + +int unit_file_query_preset(UnitFileScope scope, const char *name); + +const char *unit_file_state_to_string(UnitFileState s); +UnitFileState unit_file_state_from_string(const char *s); + +#endif diff --git a/src/test-install.c b/src/test-install.c new file mode 100644 index 0000000000..f8e87e0c74 --- /dev/null +++ b/src/test-install.c @@ -0,0 +1,264 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "util.h" +#include "install.h" + +static void dump_changes(UnitFileChange *c, unsigned n) { + unsigned i; + + assert(n == 0 || c); + + for (i = 0; i < n; i++) { + if (c[i].type == UNIT_FILE_UNLINK) + printf("rm '%s'\n", c[i].path); + else if (c[i].type == UNIT_FILE_SYMLINK) + printf("ln -s '%s' '%s'\n", c[i].source, c[i].path); + } +} + +int main(int argc, char* argv[]) { + Hashmap *h; + UnitFileList *p; + Iterator i; + int r; + const char *const files[] = { "avahi-daemon.service", NULL }; + const char *const files2[] = { "/home/lennart/test.service", NULL }; + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + + h = hashmap_new(string_hash_func, string_compare_func); + r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h); + assert_se(r == 0); + + HASHMAP_FOREACH(p, h, i) { + UnitFileState s; + + s = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, file_name_from_path(p->path)); + + assert_se(p->state == s); + + fprintf(stderr, "%s (%s)\n", + p->path, + unit_file_state_to_string(p->state)); + } + + unit_file_list_free(h); + + log_error("enable"); + + r = unit_file_enable(UNIT_FILE_SYSTEM, false, NULL, (char**) files, false, &changes, &n_changes); + assert_se(r >= 0); + + log_error("enable2"); + + r = unit_file_enable(UNIT_FILE_SYSTEM, false, NULL, (char**) files, false, &changes, &n_changes); + assert_se(r >= 0); + + dump_changes(changes, n_changes); + unit_file_changes_free(changes, n_changes); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_ENABLED); + + log_error("disable"); + + changes = NULL; + n_changes = 0; + + r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files, &changes, &n_changes); + assert_se(r >= 0); + + dump_changes(changes, n_changes); + unit_file_changes_free(changes, n_changes); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_DISABLED); + + log_error("mask"); + changes = NULL; + n_changes = 0; + + r = unit_file_mask(UNIT_FILE_SYSTEM, false, NULL, (char**) files, false, &changes, &n_changes); + assert_se(r >= 0); + log_error("mask2"); + r = unit_file_mask(UNIT_FILE_SYSTEM, false, NULL, (char**) files, false, &changes, &n_changes); + assert_se(r >= 0); + + dump_changes(changes, n_changes); + unit_file_changes_free(changes, n_changes); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_MASKED); + + log_error("unmask"); + changes = NULL; + n_changes = 0; + + r = unit_file_unmask(UNIT_FILE_SYSTEM, false, NULL, (char**) files, &changes, &n_changes); + assert_se(r >= 0); + log_error("unmask2"); + r = unit_file_unmask(UNIT_FILE_SYSTEM, false, NULL, (char**) files, &changes, &n_changes); + assert_se(r >= 0); + + dump_changes(changes, n_changes); + unit_file_changes_free(changes, n_changes); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_DISABLED); + + log_error("mask"); + changes = NULL; + n_changes = 0; + + r = unit_file_mask(UNIT_FILE_SYSTEM, false, NULL, (char**) files, false, &changes, &n_changes); + assert_se(r >= 0); + + dump_changes(changes, n_changes); + unit_file_changes_free(changes, n_changes); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_MASKED); + + log_error("disable"); + changes = NULL; + n_changes = 0; + + r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files, &changes, &n_changes); + assert_se(r >= 0); + log_error("disable2"); + r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files, &changes, &n_changes); + assert_se(r >= 0); + + dump_changes(changes, n_changes); + unit_file_changes_free(changes, n_changes); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_MASKED); + + log_error("umask"); + changes = NULL; + n_changes = 0; + + r = unit_file_unmask(UNIT_FILE_SYSTEM, false, NULL, (char**) files, &changes, &n_changes); + assert_se(r >= 0); + + dump_changes(changes, n_changes); + unit_file_changes_free(changes, n_changes); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_DISABLED); + + log_error("enable files2"); + changes = NULL; + n_changes = 0; + + r = unit_file_enable(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, false, &changes, &n_changes); + assert_se(r >= 0); + + dump_changes(changes, n_changes); + unit_file_changes_free(changes, n_changes); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, file_name_from_path(files2[0])) == UNIT_FILE_ENABLED); + + log_error("disable files2"); + changes = NULL; + n_changes = 0; + + r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, &changes, &n_changes); + assert_se(r >= 0); + + dump_changes(changes, n_changes); + unit_file_changes_free(changes, n_changes); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, file_name_from_path(files2[0])) == _UNIT_FILE_STATE_INVALID); + + log_error("link files2"); + changes = NULL; + n_changes = 0; + + r = unit_file_link(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, false, &changes, &n_changes); + assert_se(r >= 0); + + dump_changes(changes, n_changes); + unit_file_changes_free(changes, n_changes); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, file_name_from_path(files2[0])) == UNIT_FILE_LINKED); + + log_error("disable files2"); + changes = NULL; + n_changes = 0; + + r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, &changes, &n_changes); + assert_se(r >= 0); + + dump_changes(changes, n_changes); + unit_file_changes_free(changes, n_changes); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, file_name_from_path(files2[0])) == _UNIT_FILE_STATE_INVALID); + + log_error("link files2"); + changes = NULL; + n_changes = 0; + + r = unit_file_link(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, false, &changes, &n_changes); + assert_se(r >= 0); + + dump_changes(changes, n_changes); + unit_file_changes_free(changes, n_changes); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, file_name_from_path(files2[0])) == UNIT_FILE_LINKED); + + log_error("reenable files2"); + changes = NULL; + n_changes = 0; + + r = unit_file_reenable(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, false, &changes, &n_changes); + assert_se(r >= 0); + + dump_changes(changes, n_changes); + unit_file_changes_free(changes, n_changes); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, file_name_from_path(files2[0])) == UNIT_FILE_ENABLED); + + log_error("disable files2"); + changes = NULL; + n_changes = 0; + + r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, &changes, &n_changes); + assert_se(r >= 0); + + dump_changes(changes, n_changes); + unit_file_changes_free(changes, n_changes); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, file_name_from_path(files2[0])) == _UNIT_FILE_STATE_INVALID); + log_error("preset files"); + changes = NULL; + n_changes = 0; + + r = unit_file_preset(UNIT_FILE_SYSTEM, false, NULL, (char**) files, false, &changes, &n_changes); + assert_se(r >= 0); + + dump_changes(changes, n_changes); + unit_file_changes_free(changes, n_changes); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, file_name_from_path(files[0])) == UNIT_FILE_ENABLED); + + return 0; +} diff --git a/src/util.c b/src/util.c index 3187ec3768..4e441a7dbe 100644 --- a/src/util.c +++ b/src/util.c @@ -1157,6 +1157,29 @@ int readlink_and_make_absolute(const char *p, char **r) { return 0; } +int readlink_and_canonicalize(const char *p, char **r) { + char *t, *s; + int j; + + assert(p); + assert(r); + + j = readlink_and_make_absolute(p, &t); + if (j < 0) + return j; + + s = canonicalize_file_name(t); + if (s) { + free(t); + *r = s; + } else + *r = t; + + path_kill_slashes(*r); + + return 0; +} + int parent_of_path(const char *path, char **_r) { const char *e, *a = NULL, *b = NULL, *p; char *r; @@ -3944,6 +3967,17 @@ bool null_or_empty(struct stat *st) { return false; } +int null_or_empty_path(const char *fn) { + struct stat st; + + assert(fn); + + if (stat(fn, &st) < 0) + return -errno; + + return null_or_empty(&st); +} + DIR *xopendirat(int fd, const char *name, int flags) { int nfd; DIR *d; @@ -5268,6 +5302,53 @@ int glob_exists(const char *path) { return r; } +int dirent_ensure_type(DIR *d, struct dirent *de) { + struct stat st; + + assert(d); + assert(de); + + if (de->d_type != DT_UNKNOWN) + return 0; + + if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) + return -errno; + + de->d_type = + S_ISREG(st.st_mode) ? DT_REG : + S_ISDIR(st.st_mode) ? DT_DIR : + S_ISLNK(st.st_mode) ? DT_LNK : + S_ISFIFO(st.st_mode) ? DT_FIFO : + S_ISSOCK(st.st_mode) ? DT_SOCK : + S_ISCHR(st.st_mode) ? DT_CHR : + S_ISBLK(st.st_mode) ? DT_BLK : + DT_UNKNOWN; + + return 0; +} + +int in_search_path(const char *path, char **search) { + char **i, *parent; + int r; + + r = parent_of_path(path, &parent); + if (r < 0) + return r; + + r = 0; + + STRV_FOREACH(i, search) { + if (path_equal(parent, *i)) { + r = 1; + break; + } + } + + free(parent); + + return r; +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", diff --git a/src/util.h b/src/util.h index 7a4bf81c8d..f39c01fbd7 100644 --- a/src/util.h +++ b/src/util.h @@ -217,6 +217,7 @@ char **replace_env_argv(char **argv, char **env); int readlink_malloc(const char *p, char **r); int readlink_and_make_absolute(const char *p, char **r); +int readlink_and_canonicalize(const char *p, char **r); char *file_name_from_path(const char *p); bool is_path(const char *p); @@ -385,6 +386,7 @@ int wait_for_terminate_and_warn(const char *name, pid_t pid); _noreturn_ void freeze(void); bool null_or_empty(struct stat *st); +int null_or_empty_path(const char *fn); DIR *xopendirat(int dirfd, const char *name, int flags); @@ -448,6 +450,10 @@ int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **h int glob_exists(const char *path); +int dirent_ensure_type(DIR *d, struct dirent *de); + +int in_search_path(const char *path, char **search); + #define NULSTR_FOREACH(i, l) \ for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1) -- cgit v1.2.3-54-g00ecf From 346bce1f4cff0096177c613987cdc80fa4ec134e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 30 Aug 2011 22:42:49 +0200 Subject: stdout-bridge: rename logger to stdout-syslog-bridge to make it more descriptive --- .gitignore | 2 +- Makefile.am | 18 +- TODO | 2 - man/daemon.xml | 2 +- man/systemd.exec.xml | 3 +- man/systemd.special.xml.in | 12 +- man/systemd.xml | 4 +- src/execute.c | 4 +- src/execute.h | 2 +- src/logger.c | 694 -------------------------- src/special.h | 2 +- src/stdout-syslog-bridge.c | 694 ++++++++++++++++++++++++++ src/unit.c | 2 +- units/.gitignore | 2 +- units/systemd-logger.service.in | 20 - units/systemd-logger.socket | 21 - units/systemd-stdout-syslog-bridge.service.in | 20 + units/systemd-stdout-syslog-bridge.socket | 21 + 18 files changed, 762 insertions(+), 763 deletions(-) delete mode 100644 src/logger.c create mode 100644 src/stdout-syslog-bridge.c delete mode 100644 units/systemd-logger.service.in delete mode 100644 units/systemd-logger.socket create mode 100644 units/systemd-stdout-syslog-bridge.service.in create mode 100644 units/systemd-stdout-syslog-bridge.socket (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 6cfb3ed265..f4a8a45b4a 100644 --- a/.gitignore +++ b/.gitignore @@ -58,7 +58,7 @@ systemd-initctl systemd test-engine test-job-type -systemd-logger +systemd-stdout-syslog-bridge systemctl systemadm .dirstamp diff --git a/Makefile.am b/Makefile.am index 56446b011f..b0c1d97e63 100644 --- a/Makefile.am +++ b/Makefile.am @@ -150,7 +150,7 @@ bin_PROGRAMS += \ endif rootlibexec_PROGRAMS = \ - systemd-logger \ + systemd-stdout-syslog-bridge \ systemd-cgroups-agent \ systemd-initctl \ systemd-update-utmp \ @@ -354,7 +354,7 @@ dist_systemunit_DATA = \ units/sockets.target \ units/swap.target \ units/systemd-initctl.socket \ - units/systemd-logger.socket \ + units/systemd-stdout-syslog-bridge.socket \ units/systemd-shutdownd.socket \ units/syslog.socket \ units/dev-hugepages.automount \ @@ -395,7 +395,7 @@ nodist_systemunit_DATA = \ units/serial-getty@.service \ units/console-shell.service \ units/systemd-initctl.service \ - units/systemd-logger.service \ + units/systemd-stdout-syslog-bridge.service \ units/systemd-shutdownd.service \ units/systemd-logind.service \ units/systemd-kmsg-syslogd.service \ @@ -458,7 +458,7 @@ EXTRA_DIST = \ units/console-shell.service.m4 \ units/rescue.service.m4 \ units/systemd-initctl.service.in \ - units/systemd-logger.service.in \ + units/systemd-stdout-syslog-bridge.service.in \ units/systemd-shutdownd.service.in \ units/systemd-logind.service.in \ units/systemd-kmsg-syslogd.service.in \ @@ -946,11 +946,11 @@ test_install_CFLAGS = \ test_install_LDADD = \ libsystemd-basic.la -systemd_logger_SOURCES = \ - src/logger.c \ +systemd_stdout_syslog_bridge_SOURCES = \ + src/stdout-syslog-bridge.c \ src/tcpwrap.c -systemd_logger_LDADD = \ +systemd_stdout_syslog_bridge_LDADD = \ libsystemd-basic.la \ libsystemd-daemon.la \ $(LIBWRAP_LIBS) @@ -1766,8 +1766,8 @@ endif rm -f user && \ $(LN_S) $(pkgsysconfdir)/user user ) ( cd $(DESTDIR)$(systemunitdir)/sockets.target.wants && \ - rm -f systemd-initctl.socket systemd-logger.socket systemd-shutdownd.socket syslog.socket && \ - $(LN_S) ../systemd-logger.socket systemd-logger.socket && \ + rm -f systemd-initctl.socket systemd-stdout-syslog-bridge.socket systemd-shutdownd.socket syslog.socket && \ + $(LN_S) ../systemd-stdout-syslog-bridge.socket systemd-stdout-syslog-bridge.socket && \ $(LN_S) ../systemd-initctl.socket systemd-initctl.socket && \ $(LN_S) ../systemd-shutdownd.socket systemd-shutdownd.socket && \ $(LN_S) ../syslog.socket syslog.socket ) diff --git a/TODO b/TODO index 56d0a9df6f..6d562eabb5 100644 --- a/TODO +++ b/TODO @@ -39,8 +39,6 @@ Features: * allow Type=simple with PIDFile= https://bugzilla.redhat.com/show_bug.cgi?id=723942 -* rename systemd-logger to systemd-stdio-syslog-bridge - * file bugs against sysklogd, syslog-ng because of StandardOuput=null * turn default stdout/stderr to syslog (after rsyslog got updated) diff --git a/man/daemon.xml b/man/daemon.xml index d5a8491850..4673794f09 100644 --- a/man/daemon.xml +++ b/man/daemon.xml @@ -323,7 +323,7 @@ Instead of using the syslog() call to log directly to the - system logger, a new-style daemon may + system syslog service, a new-style daemon may choose to simply log to STDERR via fprintf(), which is then forwarded to syslog by the init system. If log diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index bf32a89b3a..c7da8e312e 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -383,7 +383,8 @@ terminal. connects standard output to the syslog3 - system logger. + system syslog + service. connects it with the kernel log buffer which is accessible via dmesg1. diff --git a/man/systemd.special.xml.in b/man/systemd.special.xml.in index 1369b4b822..218754051e 100644 --- a/man/systemd.special.xml.in +++ b/man/systemd.special.xml.in @@ -80,8 +80,8 @@ syslog.target, systemd-initctl.service, systemd-initctl.socket, - systemd-logger.service, - systemd-logger.socket, + systemd-stdout-syslog-bridge.service, + systemd-stdout-syslog-bridge.socket, time-sync.target, umount.target @@ -563,7 +563,7 @@ - systemd-logger.service + systemd-stdout-syslog-bridge.service This is internally used by systemd to provide syslog @@ -571,15 +571,15 @@ maintains. This is a socket-activated service, see - system-logger.socket. + system-stdout-syslog-bridge.socket. - systemd-logger.socket + systemd-stdout-syslog-bridge.socket Socket activation unit for - system-logger.service. systemd + system-stdout-syslog-bridge.service. systemd will automatically add dependencies of types Requires and After to all units that diff --git a/man/systemd.xml b/man/systemd.xml index fc4810767a..d66b23027a 100644 --- a/man/systemd.xml +++ b/man/systemd.xml @@ -1074,10 +1074,10 @@ - /run/systemd/logger + /run/systemd/stdout-syslog-bridge Used internally by the - systemd-logger.service + systemd-stdout-syslog-bridge.service unit to connect STDOUT and/or STDERR of spawned processes to syslog3 diff --git a/src/execute.c b/src/execute.c index 1ab3218517..53e7e77fde 100644 --- a/src/execute.c +++ b/src/execute.c @@ -188,9 +188,9 @@ static int connect_logger_as(const ExecContext *context, ExecOutput output, cons zero(sa); sa.sa.sa_family = AF_UNIX; - strncpy(sa.un.sun_path, LOGGER_SOCKET, sizeof(sa.un.sun_path)); + strncpy(sa.un.sun_path, STDOUT_SYSLOG_BRIDGE_SOCKET, sizeof(sa.un.sun_path)); - if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + sizeof(LOGGER_SOCKET) - 1) < 0) { + if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + sizeof(STDOUT_SYSLOG_BRIDGE_SOCKET) - 1) < 0) { close_nointr_nofail(fd); return -errno; } diff --git a/src/execute.h b/src/execute.h index d5fb61ba26..77a2257e9b 100644 --- a/src/execute.h +++ b/src/execute.h @@ -40,7 +40,7 @@ struct CGroupAttribute; #include "list.h" #include "util.h" -#define LOGGER_SOCKET "/run/systemd/logger" +#define STDOUT_SYSLOG_BRIDGE_SOCKET "/run/systemd/stdout-syslog-bridge" typedef enum KillMode { KILL_CONTROL_GROUP = 0, diff --git a/src/logger.c b/src/logger.c deleted file mode 100644 index 435d5a7620..0000000000 --- a/src/logger.c +++ /dev/null @@ -1,694 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "util.h" -#include "log.h" -#include "list.h" -#include "sd-daemon.h" -#include "tcpwrap.h" -#include "def.h" - -#define STREAMS_MAX 4096 -#define SERVER_FD_MAX 16 -#define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC)) - -typedef struct Stream Stream; - -typedef struct Server { - int syslog_fd; - int kmsg_fd; - int epoll_fd; - - unsigned n_server_fd; - - bool syslog_is_stream; - - LIST_HEAD(Stream, streams); - unsigned n_streams; -} Server; - -typedef enum StreamTarget { - STREAM_SYSLOG, - STREAM_KMSG -} StreamTarget; - -typedef enum StreamState { - STREAM_TARGET, - STREAM_PRIORITY, - STREAM_PROCESS, - STREAM_PREFIX, - STREAM_RUNNING -} StreamState; - -struct Stream { - Server *server; - - StreamState state; - - int fd; - - StreamTarget target; - int priority; - char *process; - pid_t pid; - uid_t uid; - gid_t gid; - - bool prefix:1; - bool tee_console:1; - - char buffer[LINE_MAX]; - size_t length; - - LIST_FIELDS(Stream, stream); -}; - -static int stream_log(Stream *s, char *p, usec_t ts) { - - char header_priority[16], header_time[64], header_pid[16]; - struct iovec iovec[5]; - int priority; - - assert(s); - assert(p); - - priority = s->priority; - - if (s->prefix) - parse_syslog_priority(&p, &priority); - - if (*p == 0) - return 0; - - /* Patch in LOG_USER facility if necessary */ - if ((priority & LOG_FACMASK) == 0) - priority = LOG_USER | LOG_PRI(priority); - - /* - * The format glibc uses to talk to the syslog daemon is: - * - * time process[pid]: msg - * - * The format the kernel uses is: - * - * msg\n - * - * We extend the latter to include the process name and pid. - */ - - snprintf(header_priority, sizeof(header_priority), "<%i>", priority); - char_array_0(header_priority); - - if (s->target == STREAM_SYSLOG) { - time_t t; - struct tm *tm; - - t = (time_t) (ts / USEC_PER_SEC); - if (!(tm = localtime(&t))) - return -EINVAL; - - if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0) - return -EINVAL; - } - - snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) s->pid); - char_array_0(header_pid); - - zero(iovec); - IOVEC_SET_STRING(iovec[0], header_priority); - - if (s->target == STREAM_SYSLOG) { - struct msghdr msghdr; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; - } control; - struct ucred *ucred; - - zero(control); - control.cmsghdr.cmsg_level = SOL_SOCKET; - control.cmsghdr.cmsg_type = SCM_CREDENTIALS; - control.cmsghdr.cmsg_len = CMSG_LEN(sizeof(struct ucred)); - - ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr); - ucred->pid = s->pid; - ucred->uid = s->uid; - ucred->gid = s->gid; - - IOVEC_SET_STRING(iovec[1], header_time); - IOVEC_SET_STRING(iovec[2], s->process); - IOVEC_SET_STRING(iovec[3], header_pid); - IOVEC_SET_STRING(iovec[4], p); - - /* When using syslog via SOCK_STREAM separate the messages by NUL chars */ - if (s->server->syslog_is_stream) - iovec[4].iov_len++; - - zero(msghdr); - msghdr.msg_iov = iovec; - msghdr.msg_iovlen = ELEMENTSOF(iovec); - msghdr.msg_control = &control; - msghdr.msg_controllen = control.cmsghdr.cmsg_len; - - for (;;) { - ssize_t n; - - if ((n = sendmsg(s->server->syslog_fd, &msghdr, MSG_NOSIGNAL)) < 0) { - - if (errno == ESRCH) { - pid_t our_pid; - - /* Hmm, maybe the process this - * line originates from is - * dead? Then let's patch in - * our own pid and retry, - * since we have nothing - * better */ - - our_pid = getpid(); - - if (ucred->pid != our_pid) { - ucred->pid = our_pid; - continue; - } - } - - return -errno; - } - - if (!s->server->syslog_is_stream || - (size_t) n >= IOVEC_TOTAL_SIZE(iovec, ELEMENTSOF(iovec))) - break; - - IOVEC_INCREMENT(iovec, ELEMENTSOF(iovec), n); - } - - } else if (s->target == STREAM_KMSG) { - IOVEC_SET_STRING(iovec[1], s->process); - IOVEC_SET_STRING(iovec[2], header_pid); - IOVEC_SET_STRING(iovec[3], p); - IOVEC_SET_STRING(iovec[4], (char*) "\n"); - - if (writev(s->server->kmsg_fd, iovec, ELEMENTSOF(iovec)) < 0) - return -errno; - } else - assert_not_reached("Unknown log target"); - - if (s->tee_console) { - int console; - - if ((console = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC)) >= 0) { - IOVEC_SET_STRING(iovec[0], s->process); - IOVEC_SET_STRING(iovec[1], header_pid); - IOVEC_SET_STRING(iovec[2], p); - IOVEC_SET_STRING(iovec[3], (char*) "\n"); - - writev(console, iovec, 4); - } - - } - - return 0; -} - -static int stream_line(Stream *s, char *p, usec_t ts) { - int r; - - assert(s); - assert(p); - - p = strstrip(p); - - switch (s->state) { - - case STREAM_TARGET: - if (streq(p, "syslog") || streq(p, "syslog+console")) - s->target = STREAM_SYSLOG; - else if (streq(p, "kmsg") || streq(p, "kmsg+console")) { - - if (s->server->kmsg_fd >= 0 && s->uid == 0) - s->target = STREAM_KMSG; - else { - log_warning("/dev/kmsg logging not available."); - return -EPERM; - } - } else { - log_warning("Failed to parse log target line."); - return -EBADMSG; - } - - if (endswith(p, "+console")) - s->tee_console = true; - - s->state = STREAM_PRIORITY; - return 0; - - case STREAM_PRIORITY: - if ((r = safe_atoi(p, &s->priority)) < 0) { - log_warning("Failed to parse log priority line: %m"); - return r; - } - - if (s->priority < 0) { - log_warning("Log priority negative: %m"); - return -ERANGE; - } - - s->state = STREAM_PROCESS; - return 0; - - case STREAM_PROCESS: - if (!(s->process = strdup(p))) - return -ENOMEM; - - s->state = STREAM_PREFIX; - return 0; - - case STREAM_PREFIX: - - if ((r = parse_boolean(p)) < 0) - return r; - - s->prefix = r; - s->state = STREAM_RUNNING; - return 0; - - case STREAM_RUNNING: - return stream_log(s, p, ts); - } - - assert_not_reached("Unknown stream state"); -} - -static int stream_scan(Stream *s, usec_t ts) { - char *p; - size_t remaining; - int r = 0; - - assert(s); - - p = s->buffer; - remaining = s->length; - for (;;) { - char *newline; - - if (!(newline = memchr(p, '\n', remaining))) - break; - - *newline = 0; - - if ((r = stream_line(s, p, ts)) >= 0) { - remaining -= newline-p+1; - p = newline+1; - } - } - - if (p > s->buffer) { - memmove(s->buffer, p, remaining); - s->length = remaining; - } - - return r; -} - -static int stream_process(Stream *s, usec_t ts) { - ssize_t l; - int r; - assert(s); - - if ((l = read(s->fd, s->buffer+s->length, LINE_MAX-s->length)) < 0) { - - if (errno == EAGAIN) - return 0; - - log_warning("Failed to read from stream: %m"); - return -errno; - } - - - if (l == 0) - return 0; - - s->length += l; - r = stream_scan(s, ts); - - if (r < 0) - return r; - - return 1; -} - -static void stream_free(Stream *s) { - assert(s); - - if (s->server) { - assert(s->server->n_streams > 0); - s->server->n_streams--; - LIST_REMOVE(Stream, stream, s->server->streams, s); - - } - - if (s->fd >= 0) { - if (s->server) - epoll_ctl(s->server->epoll_fd, EPOLL_CTL_DEL, s->fd, NULL); - - close_nointr_nofail(s->fd); - } - - free(s->process); - free(s); -} - -static int stream_new(Server *s, int server_fd) { - Stream *stream; - int fd; - struct ucred ucred; - socklen_t len = sizeof(ucred); - struct epoll_event ev; - int r; - - assert(s); - - if ((fd = accept4(server_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC)) < 0) - return -errno; - - if (s->n_streams >= STREAMS_MAX) { - log_warning("Too many connections, refusing connection."); - close_nointr_nofail(fd); - return 0; - } - - if (!socket_tcpwrap(fd, "systemd-logger")) { - close_nointr_nofail(fd); - return 0; - } - - if (!(stream = new0(Stream, 1))) { - close_nointr_nofail(fd); - return -ENOMEM; - } - - stream->fd = fd; - - if (getsockopt(stream->fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) { - r = -errno; - goto fail; - } - - if (shutdown(fd, SHUT_WR) < 0) { - r = -errno; - goto fail; - } - - zero(ev); - ev.data.ptr = stream; - ev.events = EPOLLIN; - if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) { - r = -errno; - goto fail; - } - - stream->pid = ucred.pid; - stream->uid = ucred.uid; - stream->gid = ucred.gid; - - stream->server = s; - LIST_PREPEND(Stream, stream, s->streams, stream); - s->n_streams ++; - - return 0; - -fail: - stream_free(stream); - return r; -} - -static void server_done(Server *s) { - unsigned i; - assert(s); - - while (s->streams) - stream_free(s->streams); - - for (i = 0; i < s->n_server_fd; i++) - close_nointr_nofail(SD_LISTEN_FDS_START+i); - - if (s->syslog_fd >= 0) - close_nointr_nofail(s->syslog_fd); - - if (s->epoll_fd >= 0) - close_nointr_nofail(s->epoll_fd); - - if (s->kmsg_fd >= 0) - close_nointr_nofail(s->kmsg_fd); -} - -static int server_init(Server *s, unsigned n_sockets) { - int r; - unsigned i; - union { - struct sockaddr sa; - struct sockaddr_un un; - } sa; - - assert(s); - assert(n_sockets > 0); - - zero(*s); - - s->n_server_fd = n_sockets; - s->syslog_fd = -1; - s->kmsg_fd = -1; - - if ((s->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) { - r = -errno; - log_error("Failed to create epoll object: %m"); - goto fail; - } - - for (i = 0; i < n_sockets; i++) { - struct epoll_event ev; - int fd; - - fd = SD_LISTEN_FDS_START+i; - - if ((r = sd_is_socket(fd, AF_UNSPEC, SOCK_STREAM, 1)) < 0) { - log_error("Failed to determine file descriptor type: %s", strerror(-r)); - goto fail; - } - - if (!r) { - log_error("Wrong file descriptor type."); - r = -EINVAL; - goto fail; - } - - /* We use ev.data.ptr instead of ev.data.fd here, - * since on 64bit archs fd is 32bit while a pointer is - * 64bit. To make sure we can easily distinguish fd - * values and pointer values we want to make sure to - * write the full field unconditionally. */ - - zero(ev); - ev.events = EPOLLIN; - ev.data.ptr = INT_TO_PTR(fd); - if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) { - r = -errno; - log_error("Failed to add server fd to epoll object: %m"); - goto fail; - } - } - - zero(sa); - sa.un.sun_family = AF_UNIX; - strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path)); - - if ((s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) { - r = -errno; - log_error("Failed to create log fd: %m"); - goto fail; - } - - if (connect(s->syslog_fd, &sa.sa, sizeof(sa)) < 0) { - close_nointr_nofail(s->syslog_fd); - - if ((s->syslog_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0)) < 0) { - r = -errno; - log_error("Failed to create log fd: %m"); - goto fail; - } - - if (connect(s->syslog_fd, &sa.sa, sizeof(sa)) < 0) { - r = -errno; - log_error("Failed to connect log socket to /dev/log: %m"); - goto fail; - } - - s->syslog_is_stream = true; - } else - s->syslog_is_stream = false; - - /* /dev/kmsg logging is strictly optional */ - if ((s->kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC)) < 0) - log_warning("Failed to open /dev/kmsg for logging, disabling kernel log buffer support: %m"); - - return 0; - -fail: - server_done(s); - return r; -} - -static int process_event(Server *s, struct epoll_event *ev) { - int r; - - assert(s); - - /* Yes, this is a bit ugly, we assume that that valid pointers - * are > SD_LISTEN_FDS_START+SERVER_FD_MAX. Which is certainly - * true on Linux (and probably most other OSes, too, since the - * first 4k usually are part of a separate null pointer - * dereference page. */ - - if (PTR_TO_INT(ev->data.ptr) >= SD_LISTEN_FDS_START && - PTR_TO_INT(ev->data.ptr) < SD_LISTEN_FDS_START+(int)s->n_server_fd) { - - if (ev->events != EPOLLIN) { - log_info("Got invalid event from epoll. (1)"); - return -EIO; - } - - if ((r = stream_new(s, PTR_TO_INT(ev->data.ptr))) < 0) { - log_info("Failed to accept new connection: %s", strerror(-r)); - return r; - } - - } else { - usec_t ts; - Stream *stream = ev->data.ptr; - - ts = now(CLOCK_REALTIME); - - if (!(ev->events & EPOLLIN)) { - log_info("Got invalid event from epoll. (2)"); - stream_free(stream); - return 0; - } - - if ((r = stream_process(stream, ts)) <= 0) { - - if (r < 0) - log_info("Got error on stream: %s", strerror(-r)); - - stream_free(stream); - return 0; - } - } - - return 0; -} - -int main(int argc, char *argv[]) { - Server server; - int r = EXIT_FAILURE, n; - - if (getppid() != 1) { - log_error("This program should be invoked by init only."); - return EXIT_FAILURE; - } - - if (argc > 1) { - log_error("This program does not take arguments."); - return EXIT_FAILURE; - } - - log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); - log_parse_environment(); - log_open(); - - umask(0022); - - if ((n = sd_listen_fds(true)) < 0) { - log_error("Failed to read listening file descriptors from environment: %s", strerror(-r)); - return EXIT_FAILURE; - } - - if (n <= 0 || n > SERVER_FD_MAX) { - log_error("No or too many file descriptors passed."); - return EXIT_FAILURE; - } - - if (server_init(&server, (unsigned) n) < 0) - return EXIT_FAILURE; - - log_debug("systemd-logger running as pid %lu", (unsigned long) getpid()); - - sd_notify(false, - "READY=1\n" - "STATUS=Processing requests..."); - - for (;;) { - struct epoll_event event; - int k; - - if ((k = epoll_wait(server.epoll_fd, - &event, 1, - server.n_streams <= 0 ? TIMEOUT_MSEC : -1)) < 0) { - - if (errno == EINTR) - continue; - - log_error("epoll_wait() failed: %m"); - goto fail; - } - - if (k <= 0) - break; - - if (process_event(&server, &event) < 0) - goto fail; - } - - r = EXIT_SUCCESS; - - log_debug("systemd-logger stopped as pid %lu", (unsigned long) getpid()); - -fail: - sd_notify(false, - "STATUS=Shutting down..."); - - server_done(&server); - - return r; -} diff --git a/src/special.h b/src/special.h index 08dae11a25..614e53ca1b 100644 --- a/src/special.h +++ b/src/special.h @@ -68,7 +68,7 @@ /* Services systemd relies on */ #define SPECIAL_DBUS_SERVICE "dbus.service" #define SPECIAL_DBUS_SOCKET "dbus.socket" -#define SPECIAL_LOGGER_SOCKET "systemd-logger.socket" +#define SPECIAL_STDOUT_SYSLOG_BRIDGE_SOCKET "systemd-stdout-syslog-bridge.socket" #define SPECIAL_SYSLOG_SOCKET "syslog.socket" /* Magic init signals */ diff --git a/src/stdout-syslog-bridge.c b/src/stdout-syslog-bridge.c new file mode 100644 index 0000000000..48a301f6a7 --- /dev/null +++ b/src/stdout-syslog-bridge.c @@ -0,0 +1,694 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "log.h" +#include "list.h" +#include "sd-daemon.h" +#include "tcpwrap.h" +#include "def.h" + +#define STREAMS_MAX 4096 +#define SERVER_FD_MAX 16 +#define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC)) + +typedef struct Stream Stream; + +typedef struct Server { + int syslog_fd; + int kmsg_fd; + int epoll_fd; + + unsigned n_server_fd; + + bool syslog_is_stream; + + LIST_HEAD(Stream, streams); + unsigned n_streams; +} Server; + +typedef enum StreamTarget { + STREAM_SYSLOG, + STREAM_KMSG +} StreamTarget; + +typedef enum StreamState { + STREAM_TARGET, + STREAM_PRIORITY, + STREAM_PROCESS, + STREAM_PREFIX, + STREAM_RUNNING +} StreamState; + +struct Stream { + Server *server; + + StreamState state; + + int fd; + + StreamTarget target; + int priority; + char *process; + pid_t pid; + uid_t uid; + gid_t gid; + + bool prefix:1; + bool tee_console:1; + + char buffer[LINE_MAX]; + size_t length; + + LIST_FIELDS(Stream, stream); +}; + +static int stream_log(Stream *s, char *p, usec_t ts) { + + char header_priority[16], header_time[64], header_pid[16]; + struct iovec iovec[5]; + int priority; + + assert(s); + assert(p); + + priority = s->priority; + + if (s->prefix) + parse_syslog_priority(&p, &priority); + + if (*p == 0) + return 0; + + /* Patch in LOG_USER facility if necessary */ + if ((priority & LOG_FACMASK) == 0) + priority = LOG_USER | LOG_PRI(priority); + + /* + * The format glibc uses to talk to the syslog daemon is: + * + * time process[pid]: msg + * + * The format the kernel uses is: + * + * msg\n + * + * We extend the latter to include the process name and pid. + */ + + snprintf(header_priority, sizeof(header_priority), "<%i>", priority); + char_array_0(header_priority); + + if (s->target == STREAM_SYSLOG) { + time_t t; + struct tm *tm; + + t = (time_t) (ts / USEC_PER_SEC); + if (!(tm = localtime(&t))) + return -EINVAL; + + if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0) + return -EINVAL; + } + + snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) s->pid); + char_array_0(header_pid); + + zero(iovec); + IOVEC_SET_STRING(iovec[0], header_priority); + + if (s->target == STREAM_SYSLOG) { + struct msghdr msghdr; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; + } control; + struct ucred *ucred; + + zero(control); + control.cmsghdr.cmsg_level = SOL_SOCKET; + control.cmsghdr.cmsg_type = SCM_CREDENTIALS; + control.cmsghdr.cmsg_len = CMSG_LEN(sizeof(struct ucred)); + + ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr); + ucred->pid = s->pid; + ucred->uid = s->uid; + ucred->gid = s->gid; + + IOVEC_SET_STRING(iovec[1], header_time); + IOVEC_SET_STRING(iovec[2], s->process); + IOVEC_SET_STRING(iovec[3], header_pid); + IOVEC_SET_STRING(iovec[4], p); + + /* When using syslog via SOCK_STREAM separate the messages by NUL chars */ + if (s->server->syslog_is_stream) + iovec[4].iov_len++; + + zero(msghdr); + msghdr.msg_iov = iovec; + msghdr.msg_iovlen = ELEMENTSOF(iovec); + msghdr.msg_control = &control; + msghdr.msg_controllen = control.cmsghdr.cmsg_len; + + for (;;) { + ssize_t n; + + if ((n = sendmsg(s->server->syslog_fd, &msghdr, MSG_NOSIGNAL)) < 0) { + + if (errno == ESRCH) { + pid_t our_pid; + + /* Hmm, maybe the process this + * line originates from is + * dead? Then let's patch in + * our own pid and retry, + * since we have nothing + * better */ + + our_pid = getpid(); + + if (ucred->pid != our_pid) { + ucred->pid = our_pid; + continue; + } + } + + return -errno; + } + + if (!s->server->syslog_is_stream || + (size_t) n >= IOVEC_TOTAL_SIZE(iovec, ELEMENTSOF(iovec))) + break; + + IOVEC_INCREMENT(iovec, ELEMENTSOF(iovec), n); + } + + } else if (s->target == STREAM_KMSG) { + IOVEC_SET_STRING(iovec[1], s->process); + IOVEC_SET_STRING(iovec[2], header_pid); + IOVEC_SET_STRING(iovec[3], p); + IOVEC_SET_STRING(iovec[4], (char*) "\n"); + + if (writev(s->server->kmsg_fd, iovec, ELEMENTSOF(iovec)) < 0) + return -errno; + } else + assert_not_reached("Unknown log target"); + + if (s->tee_console) { + int console; + + if ((console = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC)) >= 0) { + IOVEC_SET_STRING(iovec[0], s->process); + IOVEC_SET_STRING(iovec[1], header_pid); + IOVEC_SET_STRING(iovec[2], p); + IOVEC_SET_STRING(iovec[3], (char*) "\n"); + + writev(console, iovec, 4); + } + + } + + return 0; +} + +static int stream_line(Stream *s, char *p, usec_t ts) { + int r; + + assert(s); + assert(p); + + p = strstrip(p); + + switch (s->state) { + + case STREAM_TARGET: + if (streq(p, "syslog") || streq(p, "syslog+console")) + s->target = STREAM_SYSLOG; + else if (streq(p, "kmsg") || streq(p, "kmsg+console")) { + + if (s->server->kmsg_fd >= 0 && s->uid == 0) + s->target = STREAM_KMSG; + else { + log_warning("/dev/kmsg logging not available."); + return -EPERM; + } + } else { + log_warning("Failed to parse log target line."); + return -EBADMSG; + } + + if (endswith(p, "+console")) + s->tee_console = true; + + s->state = STREAM_PRIORITY; + return 0; + + case STREAM_PRIORITY: + if ((r = safe_atoi(p, &s->priority)) < 0) { + log_warning("Failed to parse log priority line: %m"); + return r; + } + + if (s->priority < 0) { + log_warning("Log priority negative: %m"); + return -ERANGE; + } + + s->state = STREAM_PROCESS; + return 0; + + case STREAM_PROCESS: + if (!(s->process = strdup(p))) + return -ENOMEM; + + s->state = STREAM_PREFIX; + return 0; + + case STREAM_PREFIX: + + if ((r = parse_boolean(p)) < 0) + return r; + + s->prefix = r; + s->state = STREAM_RUNNING; + return 0; + + case STREAM_RUNNING: + return stream_log(s, p, ts); + } + + assert_not_reached("Unknown stream state"); +} + +static int stream_scan(Stream *s, usec_t ts) { + char *p; + size_t remaining; + int r = 0; + + assert(s); + + p = s->buffer; + remaining = s->length; + for (;;) { + char *newline; + + if (!(newline = memchr(p, '\n', remaining))) + break; + + *newline = 0; + + if ((r = stream_line(s, p, ts)) >= 0) { + remaining -= newline-p+1; + p = newline+1; + } + } + + if (p > s->buffer) { + memmove(s->buffer, p, remaining); + s->length = remaining; + } + + return r; +} + +static int stream_process(Stream *s, usec_t ts) { + ssize_t l; + int r; + assert(s); + + if ((l = read(s->fd, s->buffer+s->length, LINE_MAX-s->length)) < 0) { + + if (errno == EAGAIN) + return 0; + + log_warning("Failed to read from stream: %m"); + return -errno; + } + + + if (l == 0) + return 0; + + s->length += l; + r = stream_scan(s, ts); + + if (r < 0) + return r; + + return 1; +} + +static void stream_free(Stream *s) { + assert(s); + + if (s->server) { + assert(s->server->n_streams > 0); + s->server->n_streams--; + LIST_REMOVE(Stream, stream, s->server->streams, s); + + } + + if (s->fd >= 0) { + if (s->server) + epoll_ctl(s->server->epoll_fd, EPOLL_CTL_DEL, s->fd, NULL); + + close_nointr_nofail(s->fd); + } + + free(s->process); + free(s); +} + +static int stream_new(Server *s, int server_fd) { + Stream *stream; + int fd; + struct ucred ucred; + socklen_t len = sizeof(ucred); + struct epoll_event ev; + int r; + + assert(s); + + if ((fd = accept4(server_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC)) < 0) + return -errno; + + if (s->n_streams >= STREAMS_MAX) { + log_warning("Too many connections, refusing connection."); + close_nointr_nofail(fd); + return 0; + } + + if (!socket_tcpwrap(fd, "systemd-stdout-syslog-bridge")) { + close_nointr_nofail(fd); + return 0; + } + + if (!(stream = new0(Stream, 1))) { + close_nointr_nofail(fd); + return -ENOMEM; + } + + stream->fd = fd; + + if (getsockopt(stream->fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) { + r = -errno; + goto fail; + } + + if (shutdown(fd, SHUT_WR) < 0) { + r = -errno; + goto fail; + } + + zero(ev); + ev.data.ptr = stream; + ev.events = EPOLLIN; + if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) { + r = -errno; + goto fail; + } + + stream->pid = ucred.pid; + stream->uid = ucred.uid; + stream->gid = ucred.gid; + + stream->server = s; + LIST_PREPEND(Stream, stream, s->streams, stream); + s->n_streams ++; + + return 0; + +fail: + stream_free(stream); + return r; +} + +static void server_done(Server *s) { + unsigned i; + assert(s); + + while (s->streams) + stream_free(s->streams); + + for (i = 0; i < s->n_server_fd; i++) + close_nointr_nofail(SD_LISTEN_FDS_START+i); + + if (s->syslog_fd >= 0) + close_nointr_nofail(s->syslog_fd); + + if (s->epoll_fd >= 0) + close_nointr_nofail(s->epoll_fd); + + if (s->kmsg_fd >= 0) + close_nointr_nofail(s->kmsg_fd); +} + +static int server_init(Server *s, unsigned n_sockets) { + int r; + unsigned i; + union { + struct sockaddr sa; + struct sockaddr_un un; + } sa; + + assert(s); + assert(n_sockets > 0); + + zero(*s); + + s->n_server_fd = n_sockets; + s->syslog_fd = -1; + s->kmsg_fd = -1; + + if ((s->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) { + r = -errno; + log_error("Failed to create epoll object: %m"); + goto fail; + } + + for (i = 0; i < n_sockets; i++) { + struct epoll_event ev; + int fd; + + fd = SD_LISTEN_FDS_START+i; + + if ((r = sd_is_socket(fd, AF_UNSPEC, SOCK_STREAM, 1)) < 0) { + log_error("Failed to determine file descriptor type: %s", strerror(-r)); + goto fail; + } + + if (!r) { + log_error("Wrong file descriptor type."); + r = -EINVAL; + goto fail; + } + + /* We use ev.data.ptr instead of ev.data.fd here, + * since on 64bit archs fd is 32bit while a pointer is + * 64bit. To make sure we can easily distinguish fd + * values and pointer values we want to make sure to + * write the full field unconditionally. */ + + zero(ev); + ev.events = EPOLLIN; + ev.data.ptr = INT_TO_PTR(fd); + if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) { + r = -errno; + log_error("Failed to add server fd to epoll object: %m"); + goto fail; + } + } + + zero(sa); + sa.un.sun_family = AF_UNIX; + strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path)); + + if ((s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) { + r = -errno; + log_error("Failed to create log fd: %m"); + goto fail; + } + + if (connect(s->syslog_fd, &sa.sa, sizeof(sa)) < 0) { + close_nointr_nofail(s->syslog_fd); + + if ((s->syslog_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0)) < 0) { + r = -errno; + log_error("Failed to create log fd: %m"); + goto fail; + } + + if (connect(s->syslog_fd, &sa.sa, sizeof(sa)) < 0) { + r = -errno; + log_error("Failed to connect log socket to /dev/log: %m"); + goto fail; + } + + s->syslog_is_stream = true; + } else + s->syslog_is_stream = false; + + /* /dev/kmsg logging is strictly optional */ + if ((s->kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC)) < 0) + log_warning("Failed to open /dev/kmsg for logging, disabling kernel log buffer support: %m"); + + return 0; + +fail: + server_done(s); + return r; +} + +static int process_event(Server *s, struct epoll_event *ev) { + int r; + + assert(s); + + /* Yes, this is a bit ugly, we assume that that valid pointers + * are > SD_LISTEN_FDS_START+SERVER_FD_MAX. Which is certainly + * true on Linux (and probably most other OSes, too, since the + * first 4k usually are part of a separate null pointer + * dereference page. */ + + if (PTR_TO_INT(ev->data.ptr) >= SD_LISTEN_FDS_START && + PTR_TO_INT(ev->data.ptr) < SD_LISTEN_FDS_START+(int)s->n_server_fd) { + + if (ev->events != EPOLLIN) { + log_info("Got invalid event from epoll. (1)"); + return -EIO; + } + + if ((r = stream_new(s, PTR_TO_INT(ev->data.ptr))) < 0) { + log_info("Failed to accept new connection: %s", strerror(-r)); + return r; + } + + } else { + usec_t ts; + Stream *stream = ev->data.ptr; + + ts = now(CLOCK_REALTIME); + + if (!(ev->events & EPOLLIN)) { + log_info("Got invalid event from epoll. (2)"); + stream_free(stream); + return 0; + } + + if ((r = stream_process(stream, ts)) <= 0) { + + if (r < 0) + log_info("Got error on stream: %s", strerror(-r)); + + stream_free(stream); + return 0; + } + } + + return 0; +} + +int main(int argc, char *argv[]) { + Server server; + int r = EXIT_FAILURE, n; + + if (getppid() != 1) { + log_error("This program should be invoked by init only."); + return EXIT_FAILURE; + } + + if (argc > 1) { + log_error("This program does not take arguments."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_parse_environment(); + log_open(); + + umask(0022); + + if ((n = sd_listen_fds(true)) < 0) { + log_error("Failed to read listening file descriptors from environment: %s", strerror(-r)); + return EXIT_FAILURE; + } + + if (n <= 0 || n > SERVER_FD_MAX) { + log_error("No or too many file descriptors passed."); + return EXIT_FAILURE; + } + + if (server_init(&server, (unsigned) n) < 0) + return EXIT_FAILURE; + + log_debug("systemd-stdout-syslog-bridge running as pid %lu", (unsigned long) getpid()); + + sd_notify(false, + "READY=1\n" + "STATUS=Processing requests..."); + + for (;;) { + struct epoll_event event; + int k; + + if ((k = epoll_wait(server.epoll_fd, + &event, 1, + server.n_streams <= 0 ? TIMEOUT_MSEC : -1)) < 0) { + + if (errno == EINTR) + continue; + + log_error("epoll_wait() failed: %m"); + goto fail; + } + + if (k <= 0) + break; + + if (process_event(&server, &event) < 0) + goto fail; + } + + r = EXIT_SUCCESS; + + log_debug("systemd-stdout-syslog-bridge stopped as pid %lu", (unsigned long) getpid()); + +fail: + sd_notify(false, + "STATUS=Shutting down..."); + + server_done(&server); + + return r; +} diff --git a/src/unit.c b/src/unit.c index e0f4a1bb31..031e61993d 100644 --- a/src/unit.c +++ b/src/unit.c @@ -573,7 +573,7 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { * logging daemon is run first. */ if (u->meta.manager->running_as == MANAGER_SYSTEM) - if ((r = unit_add_two_dependencies_by_name(u, UNIT_REQUIRES, UNIT_AFTER, SPECIAL_LOGGER_SOCKET, NULL, true)) < 0) + if ((r = unit_add_two_dependencies_by_name(u, UNIT_REQUIRES, UNIT_AFTER, SPECIAL_STDOUT_SYSLOG_BRIDGE_SOCKET, NULL, true)) < 0) return r; return 0; diff --git a/units/.gitignore b/units/.gitignore index ac700e8044..cc92c73022 100644 --- a/units/.gitignore +++ b/units/.gitignore @@ -33,7 +33,7 @@ systemd-shutdownd.service systemd-random-seed-load.service systemd-random-seed-save.service systemd-initctl.service -systemd-logger.service +systemd-stdout-syslog-bridge.service getty@.service systemd-update-utmp-runlevel.service systemd-update-utmp-shutdown.service diff --git a/units/systemd-logger.service.in b/units/systemd-logger.service.in deleted file mode 100644 index 5f7fe40939..0000000000 --- a/units/systemd-logger.service.in +++ /dev/null @@ -1,20 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. - -# See systemd.special(7) for details - -[Unit] -Description=Stdio Syslog Bridge -DefaultDependencies=no -Requires=syslog.socket -After=syslog.socket - -[Service] -ExecStart=@rootlibexecdir@/systemd-logger -NotifyAccess=all -StandardOutput=null -CapabilityBoundingSet=CAP_SYS_ADMIN CAP_SETUID CAP_SETGID diff --git a/units/systemd-logger.socket b/units/systemd-logger.socket deleted file mode 100644 index 7178cc8246..0000000000 --- a/units/systemd-logger.socket +++ /dev/null @@ -1,21 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. - -# See systemd.special(7) for details - -[Unit] -Description=Stdio Syslog Bridge Socket -DefaultDependencies=no -Before=sockets.target - -# Mount and swap units need this. If this socket unit is removed by an -# isolate request the mount and and swap units would be removed too, -# hence let's exclude this from isolate requests. -IgnoreOnIsolate=yes - -[Socket] -ListenStream=/run/systemd/logger diff --git a/units/systemd-stdout-syslog-bridge.service.in b/units/systemd-stdout-syslog-bridge.service.in new file mode 100644 index 0000000000..23a5137068 --- /dev/null +++ b/units/systemd-stdout-syslog-bridge.service.in @@ -0,0 +1,20 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# See systemd.special(7) for details + +[Unit] +Description=STDOUT Syslog Bridge +DefaultDependencies=no +Requires=syslog.socket +After=syslog.socket + +[Service] +ExecStart=@rootlibexecdir@/systemd-stdout-syslog-bridge +NotifyAccess=all +StandardOutput=null +CapabilityBoundingSet=CAP_SYS_ADMIN CAP_SETUID CAP_SETGID diff --git a/units/systemd-stdout-syslog-bridge.socket b/units/systemd-stdout-syslog-bridge.socket new file mode 100644 index 0000000000..0706efd596 --- /dev/null +++ b/units/systemd-stdout-syslog-bridge.socket @@ -0,0 +1,21 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# See systemd.special(7) for details + +[Unit] +Description=Stdio Syslog Bridge Socket +DefaultDependencies=no +Before=sockets.target + +# Mount and swap units need this. If this socket unit is removed by an +# isolate request the mount and and swap units would be removed too, +# hence let's exclude this from isolate requests. +IgnoreOnIsolate=yes + +[Socket] +ListenStream=/run/systemd/stdout-syslog-bridge -- cgit v1.2.3-54-g00ecf From 87d2c1ff6a7375f03476767e6f59454bcc5cd04b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 7 Oct 2011 21:06:39 +0200 Subject: journal: add preliminary incomplete implementation --- .gitignore | 6 +- Makefile.am | 63 +- po/.gitignore | 1 + src/automount.c | 2 +- src/cgroup-show.c | 2 +- src/journal/Makefile | 1 + src/journal/journal-def.h | 139 +++++ src/journal/journal-private.h | 53 ++ src/journal/journalctl.c | 91 +++ src/journal/journald.c | 460 ++++++++++++++ src/journal/lookup3.c | 1003 +++++++++++++++++++++++++++++++ src/journal/lookup3.h | 25 + src/journal/sd-journal.c | 1333 +++++++++++++++++++++++++++++++++++++++++ src/journal/sd-journal.h | 74 +++ src/journal/test-journal.c | 93 +++ src/journal/wjournal.c | 57 ++ src/journal/wjournal.h | 39 ++ src/kmsg-syslogd.c | 216 ++----- src/loginctl.c | 2 +- src/machine-id-setup.c | 40 +- src/manager.c | 4 +- src/pam-module.c | 34 +- src/sd-id128.c | 210 +++++++ src/sd-id128.h | 56 ++ src/stdout-syslog-bridge.c | 3 +- src/systemctl.c | 4 +- src/test-id128.c | 49 ++ src/util.c | 276 +++++++-- src/util.h | 14 +- tmpfiles.d/Makefile | 1 + 30 files changed, 4076 insertions(+), 275 deletions(-) create mode 120000 src/journal/Makefile create mode 100644 src/journal/journal-def.h create mode 100644 src/journal/journal-private.h create mode 100644 src/journal/journalctl.c create mode 100644 src/journal/journald.c create mode 100644 src/journal/lookup3.c create mode 100644 src/journal/lookup3.h create mode 100644 src/journal/sd-journal.c create mode 100644 src/journal/sd-journal.h create mode 100644 src/journal/test-journal.c create mode 100644 src/journal/wjournal.c create mode 100644 src/journal/wjournal.h create mode 100644 src/sd-id128.c create mode 100644 src/sd-id128.h create mode 100644 src/test-id128.c create mode 120000 tmpfiles.d/Makefile (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index f4a8a45b4a..265801ff5f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +systemd-journalctl +systemd-journald +test-id128 +test-journal test-install org.freedesktop.hostname1.xml org.freedesktop.locale1.xml @@ -91,7 +95,7 @@ install-sh missing stamp-* *.stamp -Makefile +/Makefile ltmain.sh *.tar.bz2 *.tar.gz diff --git a/Makefile.am b/Makefile.am index f4a17aa7f5..9bf92ad7ac 100644 --- a/Makefile.am +++ b/Makefile.am @@ -134,7 +134,8 @@ rootbin_PROGRAMS = \ systemd-ask-password \ systemd-tty-ask-password-agent \ systemd-tmpfiles \ - systemd-machine-id-setup + systemd-machine-id-setup \ + systemd-journalctl bin_PROGRAMS = \ systemd-cgls \ @@ -173,7 +174,8 @@ rootlibexec_PROGRAMS = \ systemd-detect-virt \ systemd-sysctl \ systemd-logind \ - systemd-uaccess + systemd-uaccess \ + systemd-journald if ENABLE_BINFMT rootlibexec_PROGRAMS += \ @@ -225,7 +227,9 @@ noinst_PROGRAMS = \ test-env-replace \ test-strv \ test-login \ - test-install + test-install \ + test-id128 \ + test-journal if HAVE_PAM pamlib_LTLIBRARIES = \ @@ -685,7 +689,8 @@ libsystemd_core_la_SOURCES = \ src/dbus-common.c \ src/sd-daemon.c \ src/install.c \ - src/cgroup-attr.c + src/cgroup-attr.c \ + src/sd-id128.c nodist_libsystemd_core_la_SOURCES = \ src/load-fragment-gperf.c \ @@ -947,6 +952,53 @@ test_install_CFLAGS = \ test_install_LDADD = \ libsystemd-basic.la +test_id128_SOURCES = \ + src/test-id128.c \ + src/sd-id128.c + +test_id128_CFLAGS = \ + $(AM_CFLAGS) + +test_id128_LDADD = \ + libsystemd-basic.la + +test_journal_SOURCES = \ + src/journal/test-journal.c \ + src/journal/sd-journal.c \ + src/journal/lookup3.c \ + src/sd-id128.c + +test_journal_CFLAGS = \ + $(AM_CFLAGS) + +test_journal_LDADD = \ + libsystemd-basic.la + +systemd_journald_SOURCES = \ + src/journal/journald.c \ + src/journal/sd-journal.c \ + src/journal/lookup3.c \ + src/sd-id128.c + +systemd_journald_CFLAGS = \ + $(AM_CFLAGS) + +systemd_journald_LDADD = \ + libsystemd-basic.la \ + libsystemd-daemon.la + +systemd_journalctl_SOURCES = \ + src/journal/journalctl.c \ + src/journal/sd-journal.c \ + src/journal/lookup3.c \ + src/sd-id128.c + +systemd_journalctl_CFLAGS = \ + $(AM_CFLAGS) + +systemd_journalctl_LDADD = \ + libsystemd-basic.la + systemd_stdout_syslog_bridge_SOURCES = \ src/stdout-syslog-bridge.c \ src/tcpwrap.c @@ -1142,7 +1194,8 @@ systemd_tmpfiles_LDADD = \ systemd_machine_id_setup_SOURCES = \ src/machine-id-setup.c \ - src/machine-id-main.c + src/machine-id-main.c \ + src/sd-id128.c systemd_machine_id_setup_CFLAGS = \ $(AM_CFLAGS) diff --git a/po/.gitignore b/po/.gitignore index 251edd4c81..ee1215654d 100644 --- a/po/.gitignore +++ b/po/.gitignore @@ -1,3 +1,4 @@ POTFILES Makefile.in.in .intltool-merge-cache +Makefile diff --git a/src/automount.c b/src/automount.c index 29b807de51..6cf3c311b3 100644 --- a/src/automount.c +++ b/src/automount.c @@ -770,7 +770,7 @@ static void automount_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { if (packet.v5_packet.pid > 0) { char *p = NULL; - get_process_name(packet.v5_packet.pid, &p); + get_process_comm(packet.v5_packet.pid, &p); log_debug("Got direct mount request for %s, triggered by %lu (%s)", packet.v5_packet.name, (unsigned long) packet.v5_packet.pid, strna(p)); free(p); diff --git a/src/cgroup-show.c b/src/cgroup-show.c index bc9c216329..03c942c66e 100644 --- a/src/cgroup-show.c +++ b/src/cgroup-show.c @@ -133,7 +133,7 @@ static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigne for (i = 0; i < n; i++) { char *t = NULL; - get_process_cmdline(pids[i], n_columns, &t); + get_process_cmdline(pids[i], n_columns, true, &t); printf("%s%s %*lu %s\n", prefix, diff --git a/src/journal/Makefile b/src/journal/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/journal/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/journal/journal-def.h b/src/journal/journal-def.h new file mode 100644 index 0000000000..0d865ae2a2 --- /dev/null +++ b/src/journal/journal-def.h @@ -0,0 +1,139 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foojournaldefhfoo +#define foojournaldefhfoo + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include + +#include "macro.h" +#include "sd-id128.h" + +typedef struct Header Header; +typedef struct ObjectHeader ObjectHeader; +typedef union Object Object; +typedef struct DataObject DataObject; +typedef struct EntryObject EntryObject; +typedef struct HashTableObject HashTableObject; +typedef struct BisectTableObject BisectTableObject; +typedef struct EntryItem EntryItem; +typedef struct HashItem HashItem; + +/* Object types */ +enum { + OBJECT_UNUSED, + OBJECT_DATA, + OBJECT_ENTRY, + OBJECT_HASH_TABLE, + OBJECT_BISECT_TABLE +}; + +_packed_ struct ObjectHeader { + uint8_t type; + uint8_t reserved[3]; + uint64_t size; + uint8_t payload[]; +}; + +_packed_ struct DataObject { + ObjectHeader object; + uint64_t hash; + uint64_t head_entry_offset; + uint64_t tail_entry_offset; + uint64_t prev_hash_offset; + uint64_t next_hash_offset; + uint8_t payload[]; +}; + +_packed_ struct EntryItem { + uint64_t object_offset; + uint64_t prev_entry_offset; + uint64_t next_entry_offset; +}; + +_packed_ struct EntryObject { + ObjectHeader object; + uint64_t seqnum; + uint64_t realtime; + uint64_t monotonic; + uint64_t prev_entry_offset; + uint64_t next_entry_offset; + EntryItem items[]; +}; + +_packed_ struct HashItem { + uint64_t head_hash_offset; + uint64_t tail_hash_offset; +}; + +_packed_ struct HashTableObject { + ObjectHeader object; + HashItem table[]; +}; + +_packed_ struct BisectTableObject { + ObjectHeader object; + uint64_t table[]; +}; + +union Object { + ObjectHeader object; + DataObject data; + EntryObject entry; + HashTableObject hash_table; + BisectTableObject bisect_table; +}; + +enum { + STATE_OFFLINE, + STATE_ONLINE, + STATE_ARCHIVED +}; + +_packed_ struct Header { + uint8_t signature[8]; /* "LPKSHHRH" */ + uint32_t compatible_flags; + uint32_t incompatible_flags; + uint32_t state; + uint8_t reserved[4]; + sd_id128_t file_id; + sd_id128_t machine_id; + sd_id128_t boot_id; + uint64_t arena_offset; + uint64_t arena_size; + uint64_t arena_max_size; + uint64_t arena_min_size; + uint64_t arena_keep_free; + uint64_t hash_table_offset; /* for looking up data objects */ + uint64_t hash_table_size; + uint64_t bisect_table_offset; /* for looking up entry objects */ + uint64_t bisect_table_size; + uint64_t head_object_offset; + uint64_t tail_object_offset; + uint64_t head_entry_offset; + uint64_t tail_entry_offset; + uint64_t last_bisect_offset; + uint64_t n_objects; + uint64_t seqnum_base; + uint64_t seqnum; +}; + +#endif diff --git a/src/journal/journal-private.h b/src/journal/journal-private.h new file mode 100644 index 0000000000..863a39893b --- /dev/null +++ b/src/journal/journal-private.h @@ -0,0 +1,53 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foojournalprivatehfoo +#define foojournalprivatehfoo + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include + +#include "sd-journal.h" +#include "journal-def.h" +#include "util.h" + +typedef struct JournalFile JournalFile; + +int journal_file_open(sd_journal *j, const char *fname, int flags, mode_t mode, JournalFile **ret); + +void journal_file_close(JournalFile *j); + +int journal_file_move_to_object(JournalFile *f, uint64_t offset, Object **ret); + +uint64_t journal_file_entry_n_items(Object *o); + +int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const struct iovec iovec[], unsigned n_iovec, Object **ret, uint64_t *offset); + +int journal_file_move_to_entry(JournalFile *f, uint64_t seqnum, Object **ret, uint64_t *offset); + +int journal_file_find_first_entry(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset); +int journal_file_find_last_entry(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset); + +int journal_file_next_entry(JournalFile *f, Object *o, Object **ret, uint64_t *offset); +int journal_file_prev_entry(JournalFile *f, Object *o, Object **ret, uint64_t *offset); + +void journal_file_dump(JournalFile *f); + +#endif diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c new file mode 100644 index 0000000000..838e8436e4 --- /dev/null +++ b/src/journal/journalctl.c @@ -0,0 +1,91 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "journal-private.h" + +int main(int argc, char *argv[]) { + int r; + JournalFile *f; + Object *o = NULL; + + log_parse_environment(); + log_open(); + + r = journal_file_open(NULL, "/var/log/journal/system.journal", O_RDONLY, 0644, &f); + if (r == -ENOENT) + r = journal_file_open(NULL, "/run/log/journal/system.journal", O_RDONLY, 0644, &f); + + if (r < 0) { + log_error("Failed to open journal: %s", strerror(-r)); + return EXIT_FAILURE; + } + + for (;;) { + uint64_t offset; + uint64_t n, i; + + r = journal_file_next_entry(f, o, &o, &offset); + if (r < 0) { + log_error("Failed to read journal: %s", strerror(-r)); + goto finish; + } + + if (r == 0) + break; + + printf("entry: %llu\n", (unsigned long long) le64toh(o->entry.seqnum)); + + n = journal_file_entry_n_items(o); + for (i = 0; i < n; i++) { + uint64_t p, l; + + p = le64toh(o->entry.items[i].object_offset); + r = journal_file_move_to_object(f, p, &o); + if (r < 0) { + log_error("Failed to move to data: %s", strerror(-r)); + goto finish; + } + + if (le64toh(o->object.type) != OBJECT_DATA) { + log_error("Invalid file"); + goto finish; + } + + l = o->object.size - offsetof(Object, data.payload); + printf("\t[%.*s]\n", (int) l, o->data.payload); + + r = journal_file_move_to_object(f, offset, &o); + if (r < 0) { + log_error("Failed to move back to entry: %s", strerror(-r)); + goto finish; + } + } + } + +finish: + journal_file_close(f); + + return 0; +} diff --git a/src/journal/journald.c b/src/journal/journald.c new file mode 100644 index 0000000000..9297ca6fb7 --- /dev/null +++ b/src/journal/journald.c @@ -0,0 +1,460 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include "hashmap.h" +#include "journal-private.h" +#include "sd-daemon.h" +#include "socket-util.h" + +typedef struct Server { + int syslog_fd; + int epoll_fd; + int signal_fd; + + JournalFile *system_journal; + Hashmap *user_journals; +} Server; + +static void process_message(Server *s, const char *buf, struct ucred *ucred, struct timeval *tv) { + char *message = NULL, *pid = NULL, *uid = NULL, *gid = NULL, + *source_time = NULL, *boot_id = NULL, *machine_id = NULL, + *comm = NULL, *cmdline = NULL, *hostname = NULL, + *audit_session = NULL, *audit_loginuid = NULL, + *syslog_priority = NULL, *syslog_facility = NULL, + *exe = NULL; + dual_timestamp ts; + struct iovec iovec[15]; + unsigned n = 0; + char idbuf[33]; + sd_id128_t id; + int r; + char *t; + int priority = LOG_USER | LOG_INFO; + + dual_timestamp_get(&ts); + + parse_syslog_priority((char**) &buf, &priority); + skip_syslog_date((char**) &buf); + + if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0) + IOVEC_SET_STRING(iovec[n++], syslog_priority); + + if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0) + IOVEC_SET_STRING(iovec[n++], syslog_facility); + + message = strappend("MESSAGE=", buf); + if (message) + IOVEC_SET_STRING(iovec[n++], message); + + if (ucred) { + uint32_t session; + uid_t loginuid; + + if (asprintf(&pid, "PID=%lu", (unsigned long) ucred->pid) >= 0) + IOVEC_SET_STRING(iovec[n++], pid); + + if (asprintf(&uid, "UID=%lu", (unsigned long) ucred->uid) >= 0) + IOVEC_SET_STRING(iovec[n++], uid); + + if (asprintf(&gid, "GID=%lu", (unsigned long) ucred->gid) >= 0) + IOVEC_SET_STRING(iovec[n++], gid); + + r = get_process_comm(ucred->pid, &t); + if (r >= 0) { + comm = strappend("COMM=", t); + if (comm) + IOVEC_SET_STRING(iovec[n++], comm); + free(t); + } + + r = get_process_exe(ucred->pid, &t); + if (r >= 0) { + exe = strappend("EXE=", t); + if (comm) + IOVEC_SET_STRING(iovec[n++], exe); + free(t); + } + + r = get_process_cmdline(ucred->pid, LINE_MAX, false, &t); + if (r >= 0) { + cmdline = strappend("CMDLINE=", t); + if (cmdline) + IOVEC_SET_STRING(iovec[n++], cmdline); + free(t); + } + + r = audit_session_from_pid(ucred->pid, &session); + if (r >= 0) + if (asprintf(&audit_session, "AUDIT_SESSION=%lu", (unsigned long) session) >= 0) + IOVEC_SET_STRING(iovec[n++], audit_session); + + r = audit_loginuid_from_pid(ucred->pid, &loginuid); + if (r >= 0) + if (asprintf(&audit_loginuid, "AUDIT_LOGINUID=%lu", (unsigned long) loginuid) >= 0) + IOVEC_SET_STRING(iovec[n++], audit_loginuid); + } + + if (tv) { + if (asprintf(&source_time, "SOURCE_REALTIME_TIMESTAMP=%llu", + (unsigned long long) timeval_load(tv)) >= 0) + IOVEC_SET_STRING(iovec[n++], source_time); + } + + r = sd_id128_get_boot(&id); + if (r >= 0) + if (asprintf(&boot_id, "BOOT_ID=%s", sd_id128_to_string(id, idbuf)) >= 0) + IOVEC_SET_STRING(iovec[n++], boot_id); + + r = sd_id128_get_machine(&id); + if (r >= 0) + if (asprintf(&machine_id, "MACHINE_ID=%s", sd_id128_to_string(id, idbuf)) >= 0) + IOVEC_SET_STRING(iovec[n++], machine_id); + + t = gethostname_malloc(); + if (t) { + hostname = strappend("HOSTNAME=", t); + if (hostname) + IOVEC_SET_STRING(iovec[n++], hostname); + free(t); + } + + r = journal_file_append_entry(s->system_journal, &ts, iovec, n, NULL, NULL); + if (r < 0) + log_error("Failed to write entry: %s", strerror(-r)); + + + free(message); + free(pid); + free(uid); + free(gid); + free(comm); + free(cmdline); + free(source_time); + free(boot_id); + free(machine_id); + free(hostname); + free(audit_session); + free(audit_loginuid); + free(syslog_facility); + free(syslog_priority); +} + +static int process_event(Server *s, struct epoll_event *ev) { + assert(s); + + if (ev->events != EPOLLIN) { + log_info("Got invalid event from epoll."); + return -EIO; + } + + if (ev->data.fd == s->signal_fd) { + struct signalfd_siginfo sfsi; + ssize_t n; + + n = read(s->signal_fd, &sfsi, sizeof(sfsi)); + if (n != sizeof(sfsi)) { + + if (n >= 0) + return -EIO; + + if (errno == EINTR || errno == EAGAIN) + return 0; + + return -errno; + } + + log_debug("Received SIG%s", signal_to_string(sfsi.ssi_signo)); + return 0; + + } else { + for (;;) { + char buf[LINE_MAX+1]; + struct msghdr msghdr; + struct iovec iovec; + struct ucred *ucred = NULL; + struct timeval *tv = NULL; + struct cmsghdr *cmsg; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) + + CMSG_SPACE(sizeof(struct timeval))]; + } control; + ssize_t n; + char *e; + + zero(iovec); + iovec.iov_base = buf; + iovec.iov_len = sizeof(buf)-1; + + zero(control); + zero(msghdr); + msghdr.msg_iov = &iovec; + msghdr.msg_iovlen = 1; + msghdr.msg_control = &control; + msghdr.msg_controllen = sizeof(control); + + n = recvmsg(ev->data.fd, &msghdr, MSG_DONTWAIT); + if (n < 0) { + + if (errno == EINTR || errno == EAGAIN) + return 1; + + log_error("recvmsg() failed: %m"); + return -errno; + } + + for (cmsg = CMSG_FIRSTHDR(&msghdr); cmsg; cmsg = CMSG_NXTHDR(&msghdr, cmsg)) { + + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_CREDENTIALS && + cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) + ucred = (struct ucred*) CMSG_DATA(cmsg); + else if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SO_TIMESTAMP && + cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval))) + tv = (struct timeval*) CMSG_DATA(cmsg); + } + + e = memchr(buf, '\n', n); + if (e) + *e = 0; + else + buf[n] = 0; + + process_message(s, strstrip(buf), ucred, tv); + } + } + + return 1; +} + + +static int open_system_journal(JournalFile **f) { + int r; + + r = journal_file_open(NULL, "/var/log/journal/system.journal", O_RDWR|O_CREAT, 0644, f); + if (r == -ENOENT) { + mkdir_p("/run/log/journal", 0755); + + r = journal_file_open(NULL, "/run/log/journal/system.journal", O_RDWR|O_CREAT, 0644, f); + } + + return r; +} + +static int server_init(Server *s) { + int n, one, r; + struct epoll_event ev; + sigset_t mask; + + assert(s); + + zero(*s); + s->syslog_fd = s->signal_fd = -1; + + s->epoll_fd = epoll_create1(EPOLL_CLOEXEC); + if (s->epoll_fd < 0) { + log_error("Failed to create epoll object: %m"); + return -errno; + } + + n = sd_listen_fds(true); + if (n < 0) { + log_error("Failed to read listening file descriptors from environment: %s", strerror(-n)); + return n; + } + + if (n > 1) { + log_error("Too many file descriptors passed."); + return -EINVAL; + } + + if (n == 1) + s->syslog_fd = SD_LISTEN_FDS_START; + else { + union sockaddr_union sa; + + s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); + if (s->syslog_fd < 0) { + log_error("socket() failed: %m"); + return -errno; + } + + zero(sa); + sa.un.sun_family = AF_UNIX; + strncpy(sa.un.sun_path, "/run/systemd/syslog", sizeof(sa.un.sun_path)); + + unlink(sa.un.sun_path); + + r = bind(s->syslog_fd, &sa.sa, sizeof(sa.un)); + if (r < 0) { + log_error("bind() failed: %m"); + return -errno; + } + + chmod(sa.un.sun_path, 0666); + } + + one = 1; + r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); + if (r < 0) { + log_error("SO_PASSCRED failed: %m"); + return -errno; + } + + one = 1; + r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)); + if (r < 0) { + log_error("SO_TIMESTAMP failed: %m"); + return -errno; + } + + zero(ev); + ev.events = EPOLLIN; + ev.data.fd = s->syslog_fd; + if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->syslog_fd, &ev) < 0) { + log_error("Failed to add server fd to epoll object: %m"); + return -errno; + } + + s->user_journals = hashmap_new(trivial_hash_func, trivial_compare_func); + if (!s->user_journals) { + log_error("Out of memory."); + return -ENOMEM; + } + + r = open_system_journal(&s->system_journal); + if (r < 0) { + log_error("Failed to open journal: %s", strerror(-r)); + return r; + } + + assert_se(sigemptyset(&mask) == 0); + sigset_add_many(&mask, SIGINT, SIGTERM, -1); + assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0); + + s->signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); + if (s->signal_fd < 0) { + log_error("signalfd(): %m"); + return -errno; + } + + zero(ev); + ev.events = EPOLLIN; + ev.data.fd = s->signal_fd; + + if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->signal_fd, &ev) < 0) { + log_error("epoll_ctl(): %m"); + return -errno; + } + + return 0; +} + +static void server_done(Server *s) { + JournalFile *f; + assert(s); + + if (s->system_journal) + journal_file_close(s->system_journal); + + while ((f = hashmap_steal_first(s->user_journals))) + journal_file_close(f); + + hashmap_free(s->user_journals); + + if (s->epoll_fd >= 0) + close_nointr_nofail(s->epoll_fd); + + if (s->signal_fd >= 0) + close_nointr_nofail(s->signal_fd); + + if (s->syslog_fd >= 0) + close_nointr_nofail(s->syslog_fd); +} + +int main(int argc, char *argv[]) { + Server server; + int r; + + /* if (getppid() != 1) { */ + /* log_error("This program should be invoked by init only."); */ + /* return EXIT_FAILURE; */ + /* } */ + + if (argc > 1) { + log_error("This program does not take arguments."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + r = server_init(&server); + if (r < 0) + goto finish; + + log_debug("systemd-journald running as pid %lu", (unsigned long) getpid()); + + sd_notify(false, + "READY=1\n" + "STATUS=Processing messages..."); + + for (;;) { + struct epoll_event event; + + r = epoll_wait(server.epoll_fd, &event, 1, -1); + if (r < 0) { + + if (errno == EINTR) + continue; + + log_error("epoll_wait() failed: %m"); + r = -errno; + goto finish; + } else if (r == 0) + break; + + r = process_event(&server, &event); + if (r < 0) + goto finish; + else if (r == 0) + break; + } + +finish: + sd_notify(false, + "STATUS=Shutting down..."); + + server_done(&server); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/journal/lookup3.c b/src/journal/lookup3.c new file mode 100644 index 0000000000..b90093a5e2 --- /dev/null +++ b/src/journal/lookup3.c @@ -0,0 +1,1003 @@ +/* Slightly modified by Lennart Poettering, to avoid name clashes, and + * unexport a few functions. */ + +#include "lookup3.h" + +/* +------------------------------------------------------------------------------- +lookup3.c, by Bob Jenkins, May 2006, Public Domain. + +These are functions for producing 32-bit hashes for hash table lookup. +hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() +are externally useful functions. Routines to test the hash are included +if SELF_TEST is defined. You can use this free for any purpose. It's in +the public domain. It has no warranty. + +You probably want to use hashlittle(). hashlittle() and hashbig() +hash byte arrays. hashlittle() is is faster than hashbig() on +little-endian machines. Intel and AMD are little-endian machines. +On second thought, you probably want hashlittle2(), which is identical to +hashlittle() except it returns two 32-bit hashes for the price of one. +You could implement hashbig2() if you wanted but I haven't bothered here. + +If you want to find a hash of, say, exactly 7 integers, do + a = i1; b = i2; c = i3; + mix(a,b,c); + a += i4; b += i5; c += i6; + mix(a,b,c); + a += i7; + final(a,b,c); +then use c as the hash value. If you have a variable length array of +4-byte integers to hash, use hashword(). If you have a byte array (like +a character string), use hashlittle(). If you have several byte arrays, or +a mix of things, see the comments above hashlittle(). + +Why is this so big? I read 12 bytes at a time into 3 4-byte integers, +then mix those integers. This is fast (you can do a lot more thorough +mixing with 12*3 instructions on 3 integers than you can with 3 instructions +on 1 byte), but shoehorning those bytes into integers efficiently is messy. +------------------------------------------------------------------------------- +*/ +/* #define SELF_TEST 1 */ + +#include /* defines printf for tests */ +#include /* defines time_t for timings in the test */ +#include /* defines uint32_t etc */ +#include /* attempt to define endianness */ +#ifdef linux +# include /* attempt to define endianness */ +#endif + +/* + * My best guess at if you are big-endian or little-endian. This may + * need adjustment. + */ +#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ + __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(i386) || defined(__i386__) || defined(__i486__) || \ + defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL)) +# define HASH_LITTLE_ENDIAN 1 +# define HASH_BIG_ENDIAN 0 +#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ + __BYTE_ORDER == __BIG_ENDIAN) || \ + (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel)) +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 1 +#else +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 0 +#endif + +#define hashsize(n) ((uint32_t)1<<(n)) +#define hashmask(n) (hashsize(n)-1) +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + +/* +------------------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. + +This is reversible, so any information in (a,b,c) before mix() is +still in (a,b,c) after mix(). + +If four pairs of (a,b,c) inputs are run through mix(), or through +mix() in reverse, there are at least 32 bits of the output that +are sometimes the same for one pair and different for another pair. +This was tested for: +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that +satisfy this are + 4 6 8 16 19 4 + 9 15 3 18 27 15 + 14 9 3 7 17 3 +Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing +for "differ" defined as + with a one-bit base and a two-bit delta. I +used http://burtleburtle.net/bob/hash/avalanche.html to choose +the operations, constants, and arrangements of the variables. + +This does not achieve avalanche. There are input bits of (a,b,c) +that fail to affect some output bits of (a,b,c), especially of a. The +most thoroughly mixed value is c, but it doesn't really even achieve +avalanche in c. + +This allows some parallelism. Read-after-writes are good at doubling +the number of bits affected, so the goal of mixing pulls in the opposite +direction as the goal of parallelism. I did what I could. Rotates +seem to cost as much as shifts on every machine I could lay my hands +on, and rotates are much kinder to the top and bottom bits, so I used +rotates. +------------------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ +{ \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} + +/* +------------------------------------------------------------------------------- +final -- final mixing of 3 32-bit values (a,b,c) into c + +Pairs of (a,b,c) values differing in only a few bits will usually +produce values of c that look totally different. This was tested for +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +These constants passed: + 14 11 25 16 4 14 24 + 12 14 25 16 4 14 24 +and these came close: + 4 8 15 26 3 22 24 + 10 8 15 26 3 22 24 + 11 8 15 26 3 22 24 +------------------------------------------------------------------------------- +*/ +#define final(a,b,c) \ +{ \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} + +/* +-------------------------------------------------------------------- + This works on all machines. To be useful, it requires + -- that the key be an array of uint32_t's, and + -- that the length be the number of uint32_t's in the key + + The function hashword() is identical to hashlittle() on little-endian + machines, and identical to hashbig() on big-endian machines, + except that the length has to be measured in uint32_ts rather than in + bytes. hashlittle() is more complicated than hashword() only because + hashlittle() has to dance around fitting the key bytes into registers. +-------------------------------------------------------------------- +*/ +uint32_t jenkins_hashword( +const uint32_t *k, /* the key, an array of uint32_t values */ +size_t length, /* the length of the key, in uint32_ts */ +uint32_t initval) /* the previous hash, or an arbitrary value */ +{ + uint32_t a,b,c; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + (((uint32_t)length)<<2) + initval; + + /*------------------------------------------------- handle most of the key */ + while (length > 3) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 3; + k += 3; + } + + /*------------------------------------------- handle the last 3 uint32_t's */ + switch(length) /* all the case statements fall through */ + { + case 3 : c+=k[2]; + case 2 : b+=k[1]; + case 1 : a+=k[0]; + final(a,b,c); + case 0: /* case 0: nothing left to add */ + break; + } + /*------------------------------------------------------ report the result */ + return c; +} + + +/* +-------------------------------------------------------------------- +hashword2() -- same as hashword(), but take two seeds and return two +32-bit values. pc and pb must both be nonnull, and *pc and *pb must +both be initialized with seeds. If you pass in (*pb)==0, the output +(*pc) will be the same as the return value from hashword(). +-------------------------------------------------------------------- +*/ +void jenkins_hashword2 ( +const uint32_t *k, /* the key, an array of uint32_t values */ +size_t length, /* the length of the key, in uint32_ts */ +uint32_t *pc, /* IN: seed OUT: primary hash value */ +uint32_t *pb) /* IN: more seed OUT: secondary hash value */ +{ + uint32_t a,b,c; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)(length<<2)) + *pc; + c += *pb; + + /*------------------------------------------------- handle most of the key */ + while (length > 3) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 3; + k += 3; + } + + /*------------------------------------------- handle the last 3 uint32_t's */ + switch(length) /* all the case statements fall through */ + { + case 3 : c+=k[2]; + case 2 : b+=k[1]; + case 1 : a+=k[0]; + final(a,b,c); + case 0: /* case 0: nothing left to add */ + break; + } + /*------------------------------------------------------ report the result */ + *pc=c; *pb=b; +} + + +/* +------------------------------------------------------------------------------- +hashlittle() -- hash a variable-length key into a 32-bit value + k : the key (the unaligned variable-length array of bytes) + length : the length of the key, counting by bytes + initval : can be any 4-byte value +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Two keys differing by one or two bits will have +totally different hash values. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (uint8_t **)k, do it like this: + for (i=0, h=0; i 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : return c; + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : return c; /* zero length requires no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((uint32_t)k[11])<<24; + case 11: c+=((uint32_t)k[10])<<16; + case 10: c+=((uint32_t)k[9])<<8; + case 9 : c+=k[8]; + case 8 : b+=((uint32_t)k[7])<<24; + case 7 : b+=((uint32_t)k[6])<<16; + case 6 : b+=((uint32_t)k[5])<<8; + case 5 : b+=k[4]; + case 4 : a+=((uint32_t)k[3])<<24; + case 3 : a+=((uint32_t)k[2])<<16; + case 2 : a+=((uint32_t)k[1])<<8; + case 1 : a+=k[0]; + break; + case 0 : return c; + } + } + + final(a,b,c); + return c; +} + + +/* + * hashlittle2: return 2 32-bit hash values + * + * This is identical to hashlittle(), except it returns two 32-bit hash + * values instead of just one. This is good enough for hash table + * lookup with 2^^64 buckets, or if you want a second hash if you're not + * happy with the first, or if you want a probably-unique 64-bit ID for + * the key. *pc is better mixed than *pb, so use *pc first. If you want + * a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)". + */ +void jenkins_hashlittle2( + const void *key, /* the key to hash */ + size_t length, /* length of the key */ + uint32_t *pc, /* IN: primary initval, OUT: primary hash */ + uint32_t *pb) /* IN: secondary initval, OUT: secondary hash */ +{ + uint32_t a,b,c; /* internal state */ + union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + *pc; + c += *pb; + + u.ptr = key; + if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) { + const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((uint32_t)k[11])<<24; + case 11: c+=((uint32_t)k[10])<<16; + case 10: c+=((uint32_t)k[9])<<8; + case 9 : c+=k[8]; + case 8 : b+=((uint32_t)k[7])<<24; + case 7 : b+=((uint32_t)k[6])<<16; + case 6 : b+=((uint32_t)k[5])<<8; + case 5 : b+=k[4]; + case 4 : a+=((uint32_t)k[3])<<24; + case 3 : a+=((uint32_t)k[2])<<16; + case 2 : a+=((uint32_t)k[1])<<8; + case 1 : a+=k[0]; + break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + } + + final(a,b,c); + *pc=c; *pb=b; +} + + + +/* + * hashbig(): + * This is the same as hashword() on big-endian machines. It is different + * from hashlittle() on all machines. hashbig() takes advantage of + * big-endian byte ordering. + */ +uint32_t jenkins_hashbig( const void *key, size_t length, uint32_t initval) +{ + uint32_t a,b,c; + union { const void *ptr; size_t i; } u; /* to cast key to (size_t) happily */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + initval; + + u.ptr = key; + if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) { + const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]<<8" actually reads beyond the end of the string, but + * then shifts out the part it's not allowed to read. Because the + * string is aligned, the illegal read is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff00; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff0000; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff000000; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff00; a+=k[0]; break; + case 6 : b+=k[1]&0xffff0000; a+=k[0]; break; + case 5 : b+=k[1]&0xff000000; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff00; break; + case 2 : a+=k[0]&0xffff0000; break; + case 1 : a+=k[0]&0xff000000; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch(length) /* all the case statements fall through */ + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<8; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<16; /* fall through */ + case 9 : c+=((uint32_t)k8[8])<<24; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<8; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<16; /* fall through */ + case 5 : b+=((uint32_t)k8[4])<<24; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<8; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<16; /* fall through */ + case 1 : a+=((uint32_t)k8[0])<<24; break; + case 0 : return c; + } + +#endif /* !VALGRIND */ + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += ((uint32_t)k[0])<<24; + a += ((uint32_t)k[1])<<16; + a += ((uint32_t)k[2])<<8; + a += ((uint32_t)k[3]); + b += ((uint32_t)k[4])<<24; + b += ((uint32_t)k[5])<<16; + b += ((uint32_t)k[6])<<8; + b += ((uint32_t)k[7]); + c += ((uint32_t)k[8])<<24; + c += ((uint32_t)k[9])<<16; + c += ((uint32_t)k[10])<<8; + c += ((uint32_t)k[11]); + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=k[11]; + case 11: c+=((uint32_t)k[10])<<8; + case 10: c+=((uint32_t)k[9])<<16; + case 9 : c+=((uint32_t)k[8])<<24; + case 8 : b+=k[7]; + case 7 : b+=((uint32_t)k[6])<<8; + case 6 : b+=((uint32_t)k[5])<<16; + case 5 : b+=((uint32_t)k[4])<<24; + case 4 : a+=k[3]; + case 3 : a+=((uint32_t)k[2])<<8; + case 2 : a+=((uint32_t)k[1])<<16; + case 1 : a+=((uint32_t)k[0])<<24; + break; + case 0 : return c; + } + } + + final(a,b,c); + return c; +} + + +#ifdef SELF_TEST + +/* used for timings */ +void driver1() +{ + uint8_t buf[256]; + uint32_t i; + uint32_t h=0; + time_t a,z; + + time(&a); + for (i=0; i<256; ++i) buf[i] = 'x'; + for (i=0; i<1; ++i) + { + h = hashlittle(&buf[0],1,h); + } + time(&z); + if (z-a > 0) printf("time %d %.8x\n", z-a, h); +} + +/* check that every input bit changes every output bit half the time */ +#define HASHSTATE 1 +#define HASHLEN 1 +#define MAXPAIR 60 +#define MAXLEN 70 +void driver2() +{ + uint8_t qa[MAXLEN+1], qb[MAXLEN+2], *a = &qa[0], *b = &qb[1]; + uint32_t c[HASHSTATE], d[HASHSTATE], i=0, j=0, k, l, m=0, z; + uint32_t e[HASHSTATE],f[HASHSTATE],g[HASHSTATE],h[HASHSTATE]; + uint32_t x[HASHSTATE],y[HASHSTATE]; + uint32_t hlen; + + printf("No more than %d trials should ever be needed \n",MAXPAIR/2); + for (hlen=0; hlen < MAXLEN; ++hlen) + { + z=0; + for (i=0; i>(8-j)); + c[0] = hashlittle(a, hlen, m); + b[i] ^= ((k+1)<>(8-j)); + d[0] = hashlittle(b, hlen, m); + /* check every bit is 1, 0, set, and not set at least once */ + for (l=0; lz) z=k; + if (k==MAXPAIR) + { + printf("Some bit didn't change: "); + printf("%.8x %.8x %.8x %.8x %.8x %.8x ", + e[0],f[0],g[0],h[0],x[0],y[0]); + printf("i %d j %d m %d len %d\n", i, j, m, hlen); + } + if (z==MAXPAIR) goto done; + } + } + } + done: + if (z < MAXPAIR) + { + printf("Mix success %2d bytes %2d initvals ",i,m); + printf("required %d trials\n", z/2); + } + } + printf("\n"); +} + +/* Check for reading beyond the end of the buffer and alignment problems */ +void driver3() +{ + uint8_t buf[MAXLEN+20], *b; + uint32_t len; + uint8_t q[] = "This is the time for all good men to come to the aid of their country..."; + uint32_t h; + uint8_t qq[] = "xThis is the time for all good men to come to the aid of their country..."; + uint32_t i; + uint8_t qqq[] = "xxThis is the time for all good men to come to the aid of their country..."; + uint32_t j; + uint8_t qqqq[] = "xxxThis is the time for all good men to come to the aid of their country..."; + uint32_t ref,x,y; + uint8_t *p; + + printf("Endianness. These lines should all be the same (for values filled in):\n"); + printf("%.8x %.8x %.8x\n", + hashword((const uint32_t *)q, (sizeof(q)-1)/4, 13), + hashword((const uint32_t *)q, (sizeof(q)-5)/4, 13), + hashword((const uint32_t *)q, (sizeof(q)-9)/4, 13)); + p = q; + printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", + hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), + hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), + hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), + hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), + hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), + hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); + p = &qq[1]; + printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", + hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), + hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), + hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), + hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), + hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), + hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); + p = &qqq[2]; + printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", + hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), + hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), + hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), + hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), + hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), + hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); + p = &qqqq[3]; + printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", + hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), + hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), + hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), + hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), + hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), + hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); + printf("\n"); + + /* check that hashlittle2 and hashlittle produce the same results */ + i=47; j=0; + hashlittle2(q, sizeof(q), &i, &j); + if (hashlittle(q, sizeof(q), 47) != i) + printf("hashlittle2 and hashlittle mismatch\n"); + + /* check that hashword2 and hashword produce the same results */ + len = 0xdeadbeef; + i=47, j=0; + hashword2(&len, 1, &i, &j); + if (hashword(&len, 1, 47) != i) + printf("hashword2 and hashword mismatch %x %x\n", + i, hashword(&len, 1, 47)); + + /* check hashlittle doesn't read before or after the ends of the string */ + for (h=0, b=buf+1; h<8; ++h, ++b) + { + for (i=0; i +#include + +uint32_t jenkins_hashword(const uint32_t *k, size_t length, uint32_t initval); +void jenkins_hashword2(const uint32_t *k, size_t length, uint32_t *pc, uint32_t *pb); + +uint32_t jenkins_hashlittle(const void *key, size_t length, uint32_t initval); +void jenkins_hashlittle2(const void *key, size_t length, uint32_t *pc, uint32_t *pb); + +uint32_t jenkins_hashbig(const void *key, size_t length, uint32_t initval); + +static inline uint64_t hash64(const void *data, size_t length) { + uint32_t a = 0, b = 0; + + jenkins_hashlittle2(data, length, &a, &b); + + return ((uint64_t) a << 32ULL) | (uint64_t) b; +} + +#endif diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c new file mode 100644 index 0000000000..f1dd92927c --- /dev/null +++ b/src/journal/sd-journal.c @@ -0,0 +1,1333 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "sd-journal.h" +#include "journal-def.h" +#include "journal-private.h" +#include "lookup3.h" +#include "list.h" + +#define DEFAULT_ARENA_MAX_SIZE (16ULL*1024ULL*1024ULL*1024ULL) +#define DEFAULT_ARENA_MIN_SIZE (256ULL*1024ULL) +#define DEFAULT_ARENA_KEEP_FREE (1ULL*1024ULL*1024ULL) + +#define DEFAULT_HASH_TABLE_SIZE (2047ULL*16ULL) +#define DEFAULT_BISECT_TABLE_SIZE ((DEFAULT_ARENA_MAX_SIZE/(64ULL*1024ULL))*8ULL) + +#define DEFAULT_WINDOW_SIZE (128ULL*1024ULL*1024ULL) + +struct JournalFile { + sd_journal *journal; + + int fd; + char *path; + struct stat last_stat; + int prot; + bool writable; + + Header *header; + + HashItem *hash_table; + void *hash_table_window; + uint64_t hash_table_window_size; + + uint64_t *bisect_table; + void *bisect_table_window; + uint64_t bisect_table_window_size; + + void *window; + uint64_t window_offset; + uint64_t window_size; + + Object *current; + uint64_t current_offset; + + LIST_FIELDS(JournalFile, files); +}; + +struct sd_journal { + LIST_HEAD(JournalFile, files); +}; + +static const char signature[] = { 'L', 'P', 'K', 'S', 'H', 'H', 'R', 'H' }; + +#define ALIGN64(x) (((x) + 7ULL) & ~7ULL) + +void journal_file_close(JournalFile *f) { + assert(f); + + if (f->journal) + LIST_REMOVE(JournalFile, files, f->journal->files, f); + + if (f->fd >= 0) + close_nointr_nofail(f->fd); + + if (f->header) + munmap(f->header, PAGE_ALIGN(sizeof(Header))); + + if (f->hash_table_window) + munmap(f->hash_table_window, f->hash_table_window_size); + + if (f->bisect_table_window) + munmap(f->bisect_table_window, f->bisect_table_window_size); + + if (f->window) + munmap(f->window, f->window_size); + + free(f->path); + free(f); +} + +static int journal_file_init_header(JournalFile *f) { + Header h; + ssize_t k; + int r; + + assert(f); + + zero(h); + memcpy(h.signature, signature, 8); + h.arena_offset = htole64(ALIGN64(sizeof(h))); + h.arena_max_size = htole64(DEFAULT_ARENA_MAX_SIZE); + h.arena_min_size = htole64(DEFAULT_ARENA_MIN_SIZE); + h.arena_keep_free = htole64(DEFAULT_ARENA_KEEP_FREE); + + r = sd_id128_randomize(&h.file_id); + if (r < 0) + return r; + + k = pwrite(f->fd, &h, sizeof(h), 0); + if (k < 0) + return -errno; + + if (k != sizeof(h)) + return -EIO; + + return 0; +} + +static int journal_file_refresh_header(JournalFile *f) { + int r; + + assert(f); + + r = sd_id128_get_machine(&f->header->machine_id); + if (r < 0) + return r; + + r = sd_id128_get_boot(&f->header->boot_id); + if (r < 0) + return r; + + f->header->state = htole32(STATE_ONLINE); + return 0; +} + +static int journal_file_verify_header(JournalFile *f) { + assert(f); + + if (memcmp(f->header, signature, 8)) + return -EBADMSG; + + if (f->header->incompatible_flags != 0) + return -EPROTONOSUPPORT; + + if ((uint64_t) f->last_stat.st_size < (le64toh(f->header->arena_offset) + le64toh(f->header->arena_size))) + return -ENODATA; + + if (f->writable) { + uint32_t state; + sd_id128_t machine_id; + int r; + + r = sd_id128_get_machine(&machine_id); + if (r < 0) + return r; + + if (!sd_id128_equal(machine_id, f->header->machine_id)) + return -EHOSTDOWN; + + state = le32toh(f->header->state); + + if (state == STATE_ONLINE) + log_debug("Journal file %s is already online. Assuming unclean closing. Ignoring.", f->path); + else if (state == STATE_ARCHIVED) + return -ESHUTDOWN; + else if (state != STATE_OFFLINE) + log_debug("Journal file %s has unknown state %u. Ignoring.", f->path, state); + } + + return 0; +} + +static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size) { + uint64_t asize; + uint64_t old_size, new_size; + + assert(f); + + if (offset < le64toh(f->header->arena_offset)) + return -EINVAL; + + new_size = PAGE_ALIGN(offset + size); + + /* We assume that this file is not sparse, and we know that + * for sure, since we alway call posix_fallocate() + * ourselves */ + + old_size = + le64toh(f->header->arena_offset) + + le64toh(f->header->arena_size); + + if (old_size >= new_size) + return 0; + + asize = new_size - le64toh(f->header->arena_offset); + + if (asize > le64toh(f->header->arena_min_size)) { + struct statvfs svfs; + + if (fstatvfs(f->fd, &svfs) >= 0) { + uint64_t available; + + available = svfs.f_bfree * svfs.f_bsize; + + if (available >= f->header->arena_keep_free) + available -= f->header->arena_keep_free; + else + available = 0; + + if (new_size - old_size > available) + return -E2BIG; + } + } + + if (asize > le64toh(f->header->arena_max_size)) + return -E2BIG; + + if (posix_fallocate(f->fd, 0, new_size) < 0) + return -errno; + + if (fstat(f->fd, &f->last_stat) < 0) + return -errno; + + f->header->arena_size = htole64(asize); + + return 0; +} + +static int journal_file_map( + JournalFile *f, + uint64_t offset, + uint64_t size, + void **_window, + uint64_t *_woffset, + uint64_t *_wsize, + void **ret) { + + uint64_t woffset, wsize; + void *window; + + assert(f); + assert(size > 0); + assert(ret); + + woffset = offset & ~((uint64_t) page_size() - 1ULL); + wsize = size + (offset - woffset); + wsize = PAGE_ALIGN(wsize); + + window = mmap(NULL, wsize, f->prot, MAP_SHARED, f->fd, woffset); + if (window == MAP_FAILED) + return -errno; + + if (_window) + *_window = window; + + if (_woffset) + *_woffset = woffset; + + if (_wsize) + *_wsize = wsize; + + *ret = (uint8_t*) window + (offset - woffset); + + return 0; +} + +static int journal_file_move_to(JournalFile *f, uint64_t offset, uint64_t size, void **ret) { + void *p; + uint64_t delta; + int r; + + assert(f); + assert(ret); + + if (_likely_(f->window && + f->window_offset <= offset && + f->window_offset+f->window_size >= offset + size)) { + + *ret = (uint8_t*) f->window + (offset - f->window_offset); + return 0; + } + + if (f->window) { + if (munmap(f->window, f->window_size) < 0) + return -errno; + + f->window = NULL; + f->window_size = f->window_offset = 0; + } + + if (size < DEFAULT_WINDOW_SIZE) { + /* If the default window size is larger then what was + * asked for extend the mapping a bit in the hope to + * minimize needed remappings later on. We add half + * the window space before and half behind the + * requested mapping */ + + delta = PAGE_ALIGN((DEFAULT_WINDOW_SIZE - size) / 2); + + if (offset < delta) + delta = offset; + + offset -= delta; + size += (DEFAULT_WINDOW_SIZE - delta); + } else + delta = 0; + + r = journal_file_map(f, + offset, size, + &f->window, &f->window_offset, &f->window_size, + & p); + + if (r < 0) + return r; + + *ret = (uint8_t*) p + delta; + return 0; +} + +static bool verify_hash(Object *o) { + uint64_t t; + + assert(o); + + t = le64toh(o->object.type); + if (t == OBJECT_DATA) { + uint64_t s, h1, h2; + + s = le64toh(o->object.size); + + h1 = le64toh(o->data.hash); + h2 = hash64(o->data.payload, s - offsetof(Object, data.payload)); + + return h1 == h2; + } + + return true; +} + +int journal_file_move_to_object(JournalFile *f, uint64_t offset, Object **ret) { + int r; + void *t; + Object *o; + uint64_t s; + + assert(f); + assert(ret); + + r = journal_file_move_to(f, offset, sizeof(ObjectHeader), &t); + if (r < 0) + return r; + + o = (Object*) t; + s = le64toh(o->object.size); + + if (s < sizeof(ObjectHeader)) + return -EBADMSG; + + if (s > sizeof(ObjectHeader)) { + r = journal_file_move_to(f, offset, s, &t); + if (r < 0) + return r; + + o = (Object*) t; + } + + if (!verify_hash(o)) + return -EBADMSG; + + *ret = o; + return 0; +} + +static uint64_t journal_file_seqnum(JournalFile *f) { + uint64_t r; + + assert(f); + + r = le64toh(f->header->seqnum) + 1; + f->header->seqnum = htole64(r); + + return r; +} + +static int journal_file_append_object(JournalFile *f, uint64_t size, Object **ret, uint64_t *offset) { + int r; + uint64_t p; + Object *tail, *o; + void *t; + + assert(f); + assert(size >= sizeof(ObjectHeader)); + assert(offset); + assert(ret); + + p = le64toh(f->header->tail_object_offset); + + if (p == 0) + p = le64toh(f->header->arena_offset); + else { + r = journal_file_move_to_object(f, p, &tail); + if (r < 0) + return r; + + p += ALIGN64(le64toh(tail->object.size)); + } + + r = journal_file_allocate(f, p, size); + if (r < 0) + return r; + + r = journal_file_move_to(f, p, size, &t); + if (r < 0) + return r; + + o = (Object*) t; + + zero(o->object); + o->object.type = htole64(OBJECT_UNUSED); + zero(o->object.reserved); + o->object.size = htole64(size); + + f->header->tail_object_offset = htole64(p); + if (f->header->head_object_offset == 0) + f->header->head_object_offset = htole64(p); + + f->header->n_objects = htole64(le64toh(f->header->n_objects) + 1); + + *ret = o; + *offset = p; + + return 0; +} + +static int journal_file_setup_hash_table(JournalFile *f) { + uint64_t s, p; + Object *o; + int r; + + assert(f); + + s = DEFAULT_HASH_TABLE_SIZE; + r = journal_file_append_object(f, offsetof(Object, hash_table.table) + s, &o, &p); + if (r < 0) + return r; + + o->object.type = htole64(OBJECT_HASH_TABLE); + memset(o->hash_table.table, 0, s); + + f->header->hash_table_offset = htole64(p + offsetof(Object, hash_table.table)); + f->header->hash_table_size = htole64(s); + + return 0; +} + +static int journal_file_setup_bisect_table(JournalFile *f) { + uint64_t s, p; + Object *o; + int r; + + assert(f); + + s = DEFAULT_BISECT_TABLE_SIZE; + r = journal_file_append_object(f, offsetof(Object, bisect_table.table) + s, &o, &p); + if (r < 0) + return r; + + o->object.type = htole64(OBJECT_BISECT_TABLE); + memset(o->bisect_table.table, 0, s); + + f->header->bisect_table_offset = htole64(p + offsetof(Object, bisect_table.table)); + f->header->bisect_table_size = htole64(s); + + return 0; +} + +static int journal_file_map_hash_table(JournalFile *f) { + uint64_t s, p; + void *t; + int r; + + assert(f); + + p = le64toh(f->header->hash_table_offset); + s = le64toh(f->header->hash_table_size); + + r = journal_file_map(f, + p, s, + &f->hash_table_window, NULL, &f->hash_table_window_size, + &t); + if (r < 0) + return r; + + f->hash_table = t; + return 0; +} + +static int journal_file_map_bisect_table(JournalFile *f) { + uint64_t s, p; + void *t; + int r; + + assert(f); + + p = le64toh(f->header->bisect_table_offset); + s = le64toh(f->header->bisect_table_size); + + r = journal_file_map(f, + p, s, + &f->bisect_table_window, NULL, &f->bisect_table_window_size, + &t); + + if (r < 0) + return r; + + f->bisect_table = t; + return 0; +} + +static int journal_file_link_data(JournalFile *f, Object *o, uint64_t offset, uint64_t hash_index) { + uint64_t p; + int r; + + assert(f); + assert(o); + assert(offset > 0); + assert(o->object.type == htole64(OBJECT_DATA)); + + o->data.head_entry_offset = o->data.tail_entry_offset = 0; + o->data.next_hash_offset = 0; + + p = le64toh(f->hash_table[hash_index].tail_hash_offset); + if (p == 0) { + /* Only entry in the hash table is easy */ + + o->data.prev_hash_offset = 0; + f->hash_table[hash_index].head_hash_offset = htole64(offset); + } else { + o->data.prev_hash_offset = htole64(p); + + /* Temporarily move back to the previous data object, + * to patch in pointer */ + + r = journal_file_move_to_object(f, p, &o); + if (r < 0) + return r; + + o->data.next_hash_offset = offset; + + r = journal_file_move_to_object(f, offset, &o); + if (r < 0) + return r; + } + + f->hash_table[hash_index].tail_hash_offset = htole64(offset); + + return 0; +} + +static int journal_file_append_data(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset) { + uint64_t hash, h, p, np; + uint64_t osize; + Object *o; + int r; + + assert(f); + assert(data || size == 0); + + osize = offsetof(Object, data.payload) + size; + + hash = hash64(data, size); + h = hash % (le64toh(f->header->hash_table_size) / sizeof(HashItem)); + p = le64toh(f->hash_table[h].head_hash_offset); + + while (p != 0) { + /* Look for this data object in the hash table */ + + r = journal_file_move_to_object(f, p, &o); + if (r < 0) + return r; + + if (le64toh(o->object.type) != OBJECT_DATA) + return -EBADMSG; + + if (le64toh(o->object.size) == osize && + memcmp(o->data.payload, data, size) == 0) { + + if (le64toh(o->data.hash) != hash) + return -EBADMSG; + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 0; + } + + p = le64toh(o->data.next_hash_offset); + } + + r = journal_file_append_object(f, osize, &o, &np); + if (r < 0) + return r; + + o->object.type = htole64(OBJECT_DATA); + o->data.hash = htole64(hash); + memcpy(o->data.payload, data, size); + + r = journal_file_link_data(f, o, np, h); + if (r < 0) + return r; + + if (ret) + *ret = o; + + if (offset) + *offset = np; + + return 0; +} + +uint64_t journal_file_entry_n_items(Object *o) { + assert(o); + assert(o->object.type == htole64(OBJECT_ENTRY)); + + return (le64toh(o->object.size) - offsetof(Object, entry.items)) / sizeof(EntryItem); +} + +static int journal_file_link_entry_item(JournalFile *f, Object *o, uint64_t offset, uint64_t i) { + uint64_t p, q; + int r; + assert(f); + assert(o); + assert(offset > 0); + + p = le64toh(o->entry.items[i].object_offset); + if (p == 0) + return -EINVAL; + + o->entry.items[i].next_entry_offset = 0; + + /* Move to the data object */ + r = journal_file_move_to_object(f, p, &o); + if (r < 0) + return r; + + if (o->object.type != htole64(OBJECT_DATA)) + return -EBADMSG; + + q = le64toh(o->data.tail_entry_offset); + o->data.tail_entry_offset = htole64(offset); + + if (q == 0) + o->data.head_entry_offset = htole64(offset); + else { + uint64_t n, j; + + /* Move to previous entry */ + r = journal_file_move_to_object(f, q, &o); + if (r < 0) + return r; + + if (o->object.type != htole64(OBJECT_ENTRY)) + return -EBADMSG; + + n = journal_file_entry_n_items(o); + for (j = 0; j < n; j++) + if (le64toh(o->entry.items[j].object_offset) == p) + break; + + if (j >= n) + return -EBADMSG; + + o->entry.items[j].next_entry_offset = offset; + } + + /* Move back to original entry */ + r = journal_file_move_to_object(f, offset, &o); + if (r < 0) + return r; + + o->entry.items[i].prev_entry_offset = q; + return 0; +} + +static int journal_file_link_entry(JournalFile *f, Object *o, uint64_t offset) { + uint64_t p, i, n, k, a, b; + int r; + + assert(f); + assert(o); + assert(offset > 0); + assert(o->object.type == htole64(OBJECT_ENTRY)); + + /* Link up the entry itself */ + p = le64toh(f->header->tail_entry_offset); + + o->entry.prev_entry_offset = f->header->tail_entry_offset; + o->entry.next_entry_offset = 0; + + if (p == 0) + f->header->head_entry_offset = htole64(offset); + else { + /* Temporarily move back to the previous entry, to + * patch in pointer */ + + r = journal_file_move_to_object(f, p, &o); + if (r < 0) + return r; + + o->entry.next_entry_offset = htole64(offset); + + r = journal_file_move_to_object(f, offset, &o); + if (r < 0) + return r; + } + + f->header->tail_entry_offset = htole64(offset); + + /* Link up the items */ + n = journal_file_entry_n_items(o); + for (i = 0; i < n; i++) { + r = journal_file_link_entry_item(f, o, offset, i); + if (r < 0) + return r; + } + + /* Link up the entry in the bisect table */ + n = le64toh(f->header->bisect_table_size) / sizeof(uint64_t); + k = le64toh(f->header->arena_max_size) / n; + + a = (le64toh(f->header->last_bisect_offset) + k - 1) / k; + b = offset / k; + + for (; a <= b; a++) + f->bisect_table[a] = htole64(offset); + + f->header->last_bisect_offset = htole64(offset + le64toh(o->object.size)); + + return 0; +} + +static int journal_file_append_entry_internal(JournalFile *f, const dual_timestamp *ts, const EntryItem items[], unsigned n_items, Object **ret, uint64_t *offset) { + uint64_t np; + uint64_t osize; + Object *o; + int r; + + assert(f); + assert(items || n_items == 0); + + osize = offsetof(Object, entry.items) + (n_items * sizeof(EntryItem)); + + r = journal_file_append_object(f, osize, &o, &np); + if (r < 0) + return r; + + o->object.type = htole64(OBJECT_ENTRY); + o->entry.seqnum = htole64(journal_file_seqnum(f)); + memcpy(o->entry.items, items, n_items * sizeof(EntryItem)); + o->entry.realtime = htole64(ts->realtime); + o->entry.monotonic = htole64(ts->monotonic); + + r = journal_file_link_entry(f, o, np); + if (r < 0) + return r; + + if (ret) + *ret = o; + + if (offset) + *offset = np; + + return 0; +} + +int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const struct iovec iovec[], unsigned n_iovec, Object **ret, uint64_t *offset) { + unsigned i; + EntryItem *items; + int r; + + assert(f); + + items = new(EntryItem, n_iovec); + if (!items) + return -ENOMEM; + + for (i = 0; i < n_iovec; i++) { + uint64_t p; + + r = journal_file_append_data(f, iovec[i].iov_base, iovec[i].iov_len, NULL, &p); + if (r < 0) + goto finish; + + items[i].object_offset = htole64(p); + } + + r = journal_file_append_entry_internal(f, ts, items, n_iovec, ret, offset); + +finish: + free(items); + + return r; +} + +int journal_file_move_to_entry(JournalFile *f, uint64_t seqnum, Object **ret, uint64_t *offset) { + Object *o; + uint64_t lower, upper, p, n, k; + int r; + + assert(f); + + n = le64toh(f->header->bisect_table_size) / sizeof(uint64_t); + k = le64toh(f->header->arena_max_size) / n; + + lower = 0; + upper = le64toh(f->header->last_bisect_offset)/k+1; + + while (lower < upper) { + k = (upper + lower) / 2; + p = le64toh(f->bisect_table[k]); + + if (p == 0) { + upper = k; + continue; + } + + r = journal_file_move_to_object(f, p, &o); + if (r < 0) + return r; + + if (o->object.type != htole64(OBJECT_ENTRY)) + return -EBADMSG; + + if (o->entry.seqnum == seqnum) { + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 1; + } else if (seqnum < o->entry.seqnum) + upper = k; + else if (seqnum > o->entry.seqnum) + lower = k+1; + } + + assert(lower == upper); + + if (lower <= 0) + return 0; + + /* The object we are looking for is between + * bisect_table[lower-1] and bisect_table[lower] */ + + p = le64toh(f->bisect_table[lower-1]); + + for (;;) { + r = journal_file_move_to_object(f, p, &o); + if (r < 0) + return r; + + if (o->entry.seqnum == seqnum) { + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 1; + + } if (seqnum < o->entry.seqnum) + return 0; + + if (o->entry.next_entry_offset == 0) + return 0; + + p = le64toh(o->entry.next_entry_offset); + } + + return 0; +} + +int journal_file_next_entry(JournalFile *f, Object *o, Object **ret, uint64_t *offset) { + uint64_t np; + int r; + + assert(f); + + if (!o) + np = le64toh(f->header->head_entry_offset); + else { + if (le64toh(o->object.type) != OBJECT_ENTRY) + return -EINVAL; + + np = le64toh(o->entry.next_entry_offset); + } + + if (np == 0) + return 0; + + r = journal_file_move_to_object(f, np, &o); + if (r < 0) + return r; + + if (le64toh(o->object.type) != OBJECT_ENTRY) + return -EBADMSG; + + if (ret) + *ret = o; + + if (offset) + *offset = np; + + return 1; +} + +int journal_file_prev_entry(JournalFile *f, Object *o, Object **ret, uint64_t *offset) { + uint64_t np; + int r; + + assert(f); + + if (!o) + np = le64toh(f->header->tail_entry_offset); + else { + if (le64toh(o->object.type) != OBJECT_ENTRY) + return -EINVAL; + + np = le64toh(o->entry.prev_entry_offset); + } + + if (np == 0) + return 0; + + r = journal_file_move_to_object(f, np, &o); + if (r < 0) + return r; + + if (le64toh(o->object.type) != OBJECT_ENTRY) + return -EBADMSG; + + if (ret) + *ret = o; + + if (offset) + *offset = np; + + return 1; +} + +int journal_file_find_first_entry(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset) { + uint64_t p, osize, hash, h; + int r; + + assert(f); + assert(data || size == 0); + + osize = offsetof(Object, data.payload) + size; + + hash = hash64(data, size); + h = hash % (le64toh(f->header->hash_table_size) / sizeof(HashItem)); + p = le64toh(f->hash_table[h].head_hash_offset); + + while (p != 0) { + Object *o; + + r = journal_file_move_to_object(f, p, &o); + if (r < 0) + return r; + + if (le64toh(o->object.type) != OBJECT_DATA) + return -EBADMSG; + + if (le64toh(o->object.size) == osize && + memcmp(o->data.payload, data, size) == 0) { + + if (le64toh(o->data.hash) != hash) + return -EBADMSG; + + if (o->data.head_entry_offset == 0) + return 0; + + p = le64toh(o->data.head_entry_offset); + r = journal_file_move_to_object(f, p, &o); + if (r < 0) + return r; + + if (le64toh(o->object.type) != OBJECT_ENTRY) + return -EBADMSG; + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 1; + } + + p = le64toh(o->data.next_hash_offset); + } + + return 0; +} + +int journal_file_find_last_entry(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset) { + uint64_t p, osize, hash, h; + int r; + + assert(f); + assert(data || size == 0); + + osize = offsetof(Object, data.payload) + size; + + hash = hash64(data, size); + h = hash % (le64toh(f->header->hash_table_size) / sizeof(HashItem)); + p = le64toh(f->hash_table[h].tail_hash_offset); + + while (p != 0) { + Object *o; + + r = journal_file_move_to_object(f, p, &o); + if (r < 0) + return r; + + if (le64toh(o->object.type) != OBJECT_DATA) + return -EBADMSG; + + if (le64toh(o->object.size) == osize && + memcmp(o->data.payload, data, size) == 0) { + + if (le64toh(o->data.hash) != hash) + return -EBADMSG; + + if (o->data.tail_entry_offset == 0) + return 0; + + p = le64toh(o->data.tail_entry_offset); + r = journal_file_move_to_object(f, p, &o); + if (r < 0) + return r; + + if (le64toh(o->object.type) != OBJECT_ENTRY) + return -EBADMSG; + + if (ret) + *ret = o; + + if (offset) + *offset = p; + + return 1; + } + + p = le64toh(o->data.prev_hash_offset); + } + + return 0; +} + +void journal_file_dump(JournalFile *f) { + char a[33], b[33], c[33]; + Object *o; + int r; + uint64_t p; + + assert(f); + + printf("File ID: %s\n" + "Machine ID: %s\n" + "Boot ID: %s\n" + "Arena size: %llu\n", + sd_id128_to_string(f->header->file_id, a), + sd_id128_to_string(f->header->machine_id, b), + sd_id128_to_string(f->header->boot_id, c), + (unsigned long long) le64toh(f->header->arena_size)); + + p = le64toh(f->header->head_object_offset); + while (p != 0) { + r = journal_file_move_to_object(f, p, &o); + if (r < 0) + goto fail; + + switch (o->object.type) { + + case OBJECT_UNUSED: + printf("Type: OBJECT_UNUSED\n"); + break; + + case OBJECT_DATA: + printf("Type: OBJECT_DATA\n"); + break; + + case OBJECT_ENTRY: + printf("Type: OBJECT_ENTRY %llu\n", (unsigned long long) le64toh(o->entry.seqnum)); + break; + + case OBJECT_HASH_TABLE: + printf("Type: OBJECT_HASH_TABLE\n"); + break; + + case OBJECT_BISECT_TABLE: + printf("Type: OBJECT_BISECT_TABLE\n"); + break; + } + + if (p == le64toh(f->header->tail_object_offset)) + p = 0; + else + p = p + ALIGN64(le64toh(o->object.size)); + } + + return; +fail: + log_error("File corrupt"); +} + +int journal_file_open( + sd_journal *j, + const char *fname, + int flags, + mode_t mode, + JournalFile **ret) { + + JournalFile *f; + int r; + bool newly_created = false; + + assert(fname); + + if ((flags & O_ACCMODE) != O_RDONLY && + (flags & O_ACCMODE) != O_RDWR) + return -EINVAL; + + f = new0(JournalFile, 1); + if (!f) + return -ENOMEM; + + f->writable = (flags & O_ACCMODE) != O_RDONLY; + f->prot = prot_from_flags(flags); + + f->fd = open(fname, flags|O_CLOEXEC, mode); + if (f->fd < 0) { + r = -errno; + goto fail; + } + + f->path = strdup(fname); + if (!f->path) { + r = -ENOMEM; + goto fail; + } + + if (fstat(f->fd, &f->last_stat) < 0) { + r = -errno; + goto fail; + } + + if (f->last_stat.st_size == 0 && f->writable) { + newly_created = true; + + r = journal_file_init_header(f); + if (r < 0) + goto fail; + + if (fstat(f->fd, &f->last_stat) < 0) { + r = -errno; + goto fail; + } + } + + if (f->last_stat.st_size < (off_t) sizeof(Header)) { + r = -EIO; + goto fail; + } + + f->header = mmap(NULL, PAGE_ALIGN(sizeof(Header)), prot_from_flags(flags), MAP_SHARED, f->fd, 0); + if (f->header == MAP_FAILED) { + f->header = NULL; + r = -errno; + goto fail; + } + + if (!newly_created) { + r = journal_file_verify_header(f); + if (r < 0) + goto fail; + } + + if (f->writable) { + r = journal_file_refresh_header(f); + if (r < 0) + goto fail; + } + + if (newly_created) { + + r = journal_file_setup_hash_table(f); + if (r < 0) + goto fail; + + r = journal_file_setup_bisect_table(f); + if (r < 0) + goto fail; + } + + r = journal_file_map_hash_table(f); + if (r < 0) + goto fail; + + r = journal_file_map_bisect_table(f); + if (r < 0) + goto fail; + + if (j) { + LIST_PREPEND(JournalFile, files, j->files, f); + f->journal = j; + } + + if (ret) + *ret = f; + + return 0; + +fail: + journal_file_close(f); + + return r; +} + +int sd_journal_open(sd_journal **ret) { + sd_journal *j; + char *fn; + const char *p; + int r = 0; + const char search_paths[] = + "/run/log/journal\0" + "/var/log/journal\0"; + + assert(ret); + + j = new0(sd_journal, 1); + if (!j) + return -ENOMEM; + + NULSTR_FOREACH(p, search_paths) { + DIR *d; + + d = opendir(p); + if (!d) { + if (errno != ENOENT && r == 0) + r = -errno; + + continue; + } + + for (;;) { + struct dirent buf, *de; + int k; + + k = readdir_r(d, &buf, &de); + if (k != 0) { + if (r == 0) + r = -k; + + break; + } + + if (!de) + break; + + if (!dirent_is_file_with_suffix(de, ".journal")) + continue; + + fn = join(p, "/", de->d_name, NULL); + if (!fn) { + r = -ENOMEM; + closedir(d); + goto fail; + } + + k = journal_file_open(j, fn, O_RDONLY, 0, NULL); + if (k < 0 && r == 0) + r = -k; + + free(fn); + } + } + + if (!j->files) { + if (r >= 0) + r = -ENOENT; + + goto fail; + } + + *ret = j; + return 0; + +fail: + sd_journal_close(j); + + return r; +}; + +void sd_journal_close(sd_journal *j) { + assert(j); + + while (j->files) + journal_file_close(j->files); + + free(j); +} diff --git a/src/journal/sd-journal.h b/src/journal/sd-journal.h new file mode 100644 index 0000000000..8170dea87c --- /dev/null +++ b/src/journal/sd-journal.h @@ -0,0 +1,74 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foojournalhfoo +#define foojournalhfoo + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "sd-id128.h" + +/* TODO: + * + * - implement rotation + * - check LE/BE conversion for 8bit, 16bit, 32bit values + * - implement parallel traversal + * - implement audit gateway + * - implement native gateway + * - extend hash table/bisect table as we go + */ + +typedef struct sd_journal sd_journal; + +int sd_journal_open(sd_journal **ret); +void sd_journal_close(sd_journal *j); + +int sd_journal_previous(sd_journal *j); +int sd_journal_next(sd_journal *j); + +void* sd_journal_get(sd_journal *j, const char *field, size_t *size); +uint64_t sd_journal_get_seqnum(sd_journal *j); +uint64_t sd_journal_get_realtime_usec(sd_journal *j); +uint64_t sd_journal_get_monotonic_usec(sd_journal *j); + +int sd_journal_add_match(sd_journal *j, const char *item, size_t *size); + +int sd_journal_seek_head(sd_journal *j); +int sd_journal_seek_tail(sd_journal *j); + +int sd_journal_seek_seqnum(sd_journal *j, uint64_t seqnum); +int sd_journal_seek_monotonic_usec(sd_journal *j, uint64_t usec); +int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec); + +uint64_t sd_journal_get_max_size(sd_journal *j); +uint64_t sd_journal_get_min_size(sd_journal *j); +uint64_t sd_journal_get_keep_free(sd_journal *j); + +int sd_journal_set_max_size(sd_journal *j, uint64_t size); +int sd_journal_set_min_size(sd_journal *j, uint64_t size); +int sd_journal_set_keep_free(sd_journal *j, uint64_t size); + +sd_id128_t sd_journal_get_file_id(sd_journal *j); +sd_id128_t sd_journal_get_machine_id(sd_journal *j); +sd_id128_t sd_journal_get_boot_id(sd_journal *j); + +#endif diff --git a/src/journal/test-journal.c b/src/journal/test-journal.c new file mode 100644 index 0000000000..92bef5f3ef --- /dev/null +++ b/src/journal/test-journal.c @@ -0,0 +1,93 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include + +#include "journal-private.h" +#include "log.h" + +int main(int argc, char *argv[]) { + dual_timestamp ts; + JournalFile *f; + struct iovec iovec; + static const char test[] = "test", test2[] = "test2"; + Object *o; + + log_set_max_level(LOG_DEBUG); + + assert_se(journal_file_open(NULL, "test", O_RDWR|O_CREAT, 0666, &f) == 0); + + dual_timestamp_get(&ts); + + iovec.iov_base = (void*) test; + iovec.iov_len = strlen(test); + assert_se(journal_file_append_entry(f, &ts, &iovec, 1, NULL, NULL) == 0); + + iovec.iov_base = (void*) test2; + iovec.iov_len = strlen(test2); + assert_se(journal_file_append_entry(f, &ts, &iovec, 1, NULL, NULL) == 0); + + iovec.iov_base = (void*) test; + iovec.iov_len = strlen(test); + assert_se(journal_file_append_entry(f, &ts, &iovec, 1, NULL, NULL) == 0); + + journal_file_dump(f); + + assert(journal_file_next_entry(f, NULL, &o, NULL) == 1); + assert(le64toh(o->entry.seqnum) == 1); + + assert(journal_file_next_entry(f, o, &o, NULL) == 1); + assert(le64toh(o->entry.seqnum) == 2); + + assert(journal_file_next_entry(f, o, &o, NULL) == 1); + assert(le64toh(o->entry.seqnum) == 3); + + assert(journal_file_next_entry(f, o, &o, NULL) == 0); + + assert(journal_file_find_first_entry(f, test, strlen(test), &o, NULL) == 1); + assert(le64toh(o->entry.seqnum) == 1); + + assert(journal_file_find_last_entry(f, test, strlen(test), &o, NULL) == 1); + assert(le64toh(o->entry.seqnum) == 3); + + assert(journal_file_find_last_entry(f, test2, strlen(test2), &o, NULL) == 1); + assert(le64toh(o->entry.seqnum) == 2); + + assert(journal_file_find_first_entry(f, test2, strlen(test2), &o, NULL) == 1); + assert(le64toh(o->entry.seqnum) == 2); + + assert(journal_file_find_first_entry(f, "quux", 4, &o, NULL) == 0); + + assert(journal_file_move_to_entry(f, 1, &o, NULL) == 1); + assert(le64toh(o->entry.seqnum) == 1); + + assert(journal_file_move_to_entry(f, 3, &o, NULL) == 1); + assert(le64toh(o->entry.seqnum) == 3); + + assert(journal_file_move_to_entry(f, 2, &o, NULL) == 1); + assert(le64toh(o->entry.seqnum) == 2); + + assert(journal_file_move_to_entry(f, 10, &o, NULL) == 0); + + journal_file_close(f); + + return 0; +} diff --git a/src/journal/wjournal.c b/src/journal/wjournal.c new file mode 100644 index 0000000000..3122aa054e --- /dev/null +++ b/src/journal/wjournal.c @@ -0,0 +1,57 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include "wjournal.h" +#include "journal-def.h" + +struct WJournal { + int fd; + + Header *header; + HashItem *hash_table; + uint64_t *bisect_table; +}; + +int wjournal_open(const char *fn, WJournal **ret) { + assert(fn); + assert(ret); +} + +void wjournal_close(WJournal *j) { + assert(j); + + if (j->fd >= 0) + close_nointr_nofail(j->fd); + + if (j->header) { + munmap(j->header, PAGE_ALIGN(sizeof(Header))); + + } + + free(j); +} + +int wjournal_write_object_begin(WJournal *j, uint64_t type, uint64_t size, Object **ret); +int wjournal_write_object_finish(WJournal *j, Object *ret); + +int wjournal_write_field(WJournal *j, const char *buffer, uint64_t size, Object **ret); +int wjournal_write_entry(WJournal *j, const Field *fields, unsigned n_fields, Object **ret); +int wjournal_write_eof(WJournal *j); diff --git a/src/journal/wjournal.h b/src/journal/wjournal.h new file mode 100644 index 0000000000..b0250d0fe2 --- /dev/null +++ b/src/journal/wjournal.h @@ -0,0 +1,39 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foojournalhfoo +#define foojournalhfoo + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include + +typedef struct WJournal WJournal; + +int wjournal_open(const char *fn, WJournal **ret); +void wjournal_close(WJournal *j); + +int wjournal_write_object_begin(WJournal *j, uint64_t type, uint64_t size, Object **ret); +int wjournal_write_object_finish(WJournal *j, Object *ret); + +int wjournal_write_field(WJournal *j, const char *buffer, uint64_t size, Object **ret); +int wjournal_write_entry(WJournal *j, const Field *fields, unsigned n_fields, Object **ret); +int wjournal_write_eof(WJournal *j); + +#endif diff --git a/src/kmsg-syslogd.c b/src/kmsg-syslogd.c index 0901a0e49b..70cc0730ee 100644 --- a/src/kmsg-syslogd.c +++ b/src/kmsg-syslogd.c @@ -65,45 +65,53 @@ static void server_done(Server *s) { fdset_free(s->syslog_fds); } -static int server_init(Server *s, unsigned n_sockets) { - int r; - unsigned i; +static int server_init(Server *s) { + int i, r, n; struct epoll_event ev; sigset_t mask; assert(s); - assert(n_sockets > 0); zero(*s); - s->kmsg_fd = s->signal_fd = -1; - if ((s->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) { - r = -errno; - log_error("Failed to create epoll object: %s", strerror(errno)); - goto fail; + s->epoll_fd = epoll_create1(EPOLL_CLOEXEC); + if (s->epoll_fd < 0) { + log_error("Failed to create epoll object: %m"); + return -errno; + } + + s->syslog_fds = fdset_new(); + if (!s->syslog_fds) { + log_error("Failed to allocate file descriptor set: %s", strerror(ENOMEM)); + return -ENOMEM; } - if (!(s->syslog_fds = fdset_new())) { - r = -ENOMEM; - log_error("Failed to allocate file descriptor set: %s", strerror(errno)); - goto fail; + n = sd_listen_fds(true); + if (n < 0) { + log_error("Failed to read listening file descriptors from environment: %s", strerror(-n)); + return n; + } + + if (n <= 0 || n > SERVER_FD_MAX) { + log_error("No or too many file descriptors passed."); + return -EINVAL; } - for (i = 0; i < n_sockets; i++) { + for (i = 0; i < n; i++) { int fd, one = 1; fd = SD_LISTEN_FDS_START+i; - if ((r = sd_is_socket(fd, AF_UNSPEC, SOCK_DGRAM, -1)) < 0) { + r = sd_is_socket(fd, AF_UNSPEC, SOCK_DGRAM, -1); + if (r < 0) { log_error("Failed to determine file descriptor type: %s", strerror(-r)); - goto fail; + return r; } if (!r) { log_error("Wrong file descriptor type."); - r = -EINVAL; - goto fail; + return -EINVAL; } if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) @@ -113,18 +121,19 @@ static int server_init(Server *s, unsigned n_sockets) { ev.events = EPOLLIN; ev.data.fd = fd; if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) { - r = -errno; - log_error("Failed to add server fd to epoll object: %s", strerror(errno)); - goto fail; + log_error("Failed to add server fd to epoll object: %m"); + return -errno; } - if ((r = fdset_put(s->syslog_fds, fd)) < 0) { + r = fdset_put(s->syslog_fds, fd); + if (r < 0) { log_error("Failed to store file descriptor in set: %s", strerror(-r)); - goto fail; + return r; } } - if ((s->kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC)) < 0) { + s->kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC); + if (s->kmsg_fd < 0) { log_error("Failed to open /dev/kmsg for logging: %m"); return -errno; } @@ -133,7 +142,8 @@ static int server_init(Server *s, unsigned n_sockets) { sigset_add_many(&mask, SIGINT, SIGTERM, -1); assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0); - if ((s->signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) { + s->signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); + if (s->signal_fd < 0) { log_error("signalfd(): %m"); return -errno; } @@ -148,80 +158,6 @@ static int server_init(Server *s, unsigned n_sockets) { } return 0; - -fail: - server_done(s); - return r; -} - -static void skip_date(const char **buf) { - enum { - LETTER, - SPACE, - NUMBER, - SPACE_OR_NUMBER, - COLON - } sequence[] = { - LETTER, LETTER, LETTER, - SPACE, - SPACE_OR_NUMBER, NUMBER, - SPACE, - SPACE_OR_NUMBER, NUMBER, - COLON, - SPACE_OR_NUMBER, NUMBER, - COLON, - SPACE_OR_NUMBER, NUMBER, - SPACE - }; - - const char *p; - unsigned i; - - assert(buf); - assert(*buf); - - p = *buf; - - for (i = 0; i < ELEMENTSOF(sequence); i++, p++) { - - if (!*p) - return; - - switch (sequence[i]) { - - case SPACE: - if (*p != ' ') - return; - break; - - case SPACE_OR_NUMBER: - if (*p == ' ') - break; - - /* fall through */ - - case NUMBER: - if (*p < '0' || *p > '9') - return; - - break; - - case LETTER: - if (!(*p >= 'A' && *p <= 'Z') && - !(*p >= 'a' && *p <= 'z')) - return; - - break; - - case COLON: - if (*p != ':') - return; - break; - - } - } - - *buf = p; } static int read_process(const char **buf, struct iovec *iovec) { @@ -266,28 +202,6 @@ static int read_process(const char **buf, struct iovec *iovec) { return 1; } -static void skip_pid(const char **buf) { - const char *p; - - assert(buf); - assert(*buf); - - p = *buf; - - if (*p != '[') - return; - - p++; - p += strspn(p, "0123456789"); - - if (*p != ']') - return; - - p++; - - *buf = p; -} - static int write_message(Server *s, const char *buf, struct ucred *ucred) { ssize_t k; char priority[6], pid[16]; @@ -314,14 +228,14 @@ static int write_message(Server *s, const char *buf, struct ucred *ucred) { IOVEC_SET_STRING(iovec[i++], priority); /* Second, skip date */ - skip_date(&buf); + skip_syslog_date((char**) &buf); /* Then, add process if set */ if (read_process(&buf, &iovec[i]) > 0) i++; else if (ucred && ucred->pid > 0 && - get_process_name(ucred->pid, &process) >= 0) + get_process_comm(ucred->pid, &process) >= 0) IOVEC_SET_STRING(iovec[i++], process); /* Skip the stored PID if we have a better one */ @@ -330,7 +244,7 @@ static int write_message(Server *s, const char *buf, struct ucred *ucred) { char_array_0(pid); IOVEC_SET_STRING(iovec[i++], pid); - skip_pid(&buf); + skip_syslog_pid((char**) &buf); if (*buf == ':') buf++; @@ -368,7 +282,8 @@ static int process_event(Server *s, struct epoll_event *ev) { struct signalfd_siginfo sfsi; ssize_t n; - if ((n = read(s->signal_fd, &sfsi, sizeof(sfsi))) != sizeof(sfsi)) { + n = read(s->signal_fd, &sfsi, sizeof(sfsi)); + if (n != sizeof(sfsi)) { if (n >= 0) return -EIO; @@ -407,7 +322,8 @@ static int process_event(Server *s, struct epoll_event *ev) { msghdr.msg_control = &control; msghdr.msg_controllen = sizeof(control); - if ((n = recvmsg(ev->data.fd, &msghdr, MSG_DONTWAIT)) < 0) { + n = recvmsg(ev->data.fd, &msghdr, MSG_DONTWAIT); + if (n < 0) { if (errno == EINTR || errno == EAGAIN) return 1; @@ -424,12 +340,14 @@ static int process_event(Server *s, struct epoll_event *ev) { else ucred = NULL; - if ((e = memchr(buf, '\n', n))) + e = memchr(buf, '\n', n); + if (e) *e = 0; else buf[n] = 0; - if ((k = write_message(s, strstrip(buf), ucred)) < 0) + k = write_message(s, strstrip(buf), ucred); + if (k < 0) return k; } } @@ -439,7 +357,7 @@ static int process_event(Server *s, struct epoll_event *ev) { int main(int argc, char *argv[]) { Server server; - int r = EXIT_FAILURE, n; + int r; if (getppid() != 1) { log_error("This program should be invoked by init only."); @@ -457,18 +375,9 @@ int main(int argc, char *argv[]) { umask(0022); - if ((n = sd_listen_fds(true)) < 0) { - log_error("Failed to read listening file descriptors from environment: %s", strerror(-r)); - return EXIT_FAILURE; - } - - if (n <= 0 || n > SERVER_FD_MAX) { - log_error("No or too many file descriptors passed."); - return EXIT_FAILURE; - } - - if (server_init(&server, (unsigned) n) < 0) - return EXIT_FAILURE; + r = server_init(&server); + if (r < 0) + goto finish; log_debug("systemd-kmsg-syslogd running as pid %lu", (unsigned long) getpid()); @@ -478,36 +387,33 @@ int main(int argc, char *argv[]) { for (;;) { struct epoll_event event; - int k; - if ((k = epoll_wait(server.epoll_fd, &event, 1, -1)) < 0) { + r = epoll_wait(server.epoll_fd, &event, 1, -1); + if (r < 0) { if (errno == EINTR) continue; log_error("epoll_wait() failed: %m"); - goto fail; - } - - if (k <= 0) + r = -errno; + goto finish; + } else if (r == 0) break; - if ((k = process_event(&server, &event)) < 0) - goto fail; - - if (k == 0) + r = process_event(&server, &event); + if (r < 0) + goto finish; + else if (r == 0) break; } - r = EXIT_SUCCESS; - log_debug("systemd-kmsg-syslogd stopped as pid %lu", (unsigned long) getpid()); -fail: +finish: sd_notify(false, "STATUS=Shutting down..."); server_done(&server); - return r; + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/loginctl.c b/src/loginctl.c index 89762b66b0..1be47c8dde 100644 --- a/src/loginctl.c +++ b/src/loginctl.c @@ -393,7 +393,7 @@ static void print_session_status_info(SessionStatusInfo *i) { printf("\t Leader: %u", (unsigned) i->leader); - get_process_name(i->leader, &t); + get_process_comm(i->leader, &t); if (t) { printf(" (%s)", t); free(t); diff --git a/src/machine-id-setup.c b/src/machine-id-setup.c index 519521fe67..9b25b10438 100644 --- a/src/machine-id-setup.c +++ b/src/machine-id-setup.c @@ -31,21 +31,12 @@ #include "macro.h" #include "util.h" #include "log.h" - -static void make_v4_uuid(unsigned char *id) { - /* Stolen from generate_random_uuid() of drivers/char/random.c - * in the kernel sources */ - - /* Set UUID version to 4 --- truly random generation */ - id[6] = (id[6] & 0x0F) | 0x40; - - /* Set the UUID variant to DCE */ - id[8] = (id[8] & 0x3F) | 0x80; -} +#include "sd-id128.h" static int generate(char id[34]) { - int fd; - unsigned char buf[16], *p; + int fd, r; + unsigned char *p; + sd_id128_t buf; char *q; ssize_t k; @@ -68,26 +59,13 @@ static int generate(char id[34]) { } /* If that didn't work, generate a random machine id */ - fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) { - log_error("Failed to open /dev/urandom: %m"); - return -errno; - } - - k = loop_read(fd, buf, sizeof(buf), false); - close_nointr_nofail(fd); - - if (k != sizeof(buf)) { - log_error("Failed to read /dev/urandom: %s", strerror(k < 0 ? -k : EIO)); - return k < 0 ? (int) k : -EIO; + r = sd_id128_randomize(&buf); + if (r < 0) { + log_error("Failed to open /dev/urandom: %s", strerror(-r)); + return r; } - /* Turn this into a valid v4 UUID, to be nice. Note that we - * only guarantee this for newly generated UUIDs, not for - * pre-existing ones.*/ - make_v4_uuid(buf); - - for (p = buf, q = id; p < buf + sizeof(buf); p++, q += 2) { + for (p = buf.bytes, q = id; p < buf.bytes + sizeof(buf); p++, q += 2) { q[0] = hexchar(*p >> 4); q[1] = hexchar(*p & 15); } diff --git a/src/manager.c b/src/manager.c index e626347dec..ac5bbef1a8 100644 --- a/src/manager.c +++ b/src/manager.c @@ -2024,7 +2024,7 @@ static int manager_dispatch_sigchld(Manager *m) { if (si.si_code == CLD_EXITED || si.si_code == CLD_KILLED || si.si_code == CLD_DUMPED) { char *name = NULL; - get_process_name(si.si_pid, &name); + get_process_comm(si.si_pid, &name); log_debug("Got SIGCHLD for process %lu (%s)", (unsigned long) si.si_pid, strna(name)); free(name); } @@ -2109,7 +2109,7 @@ static int manager_process_signal_fd(Manager *m) { if (sfsi.ssi_pid > 0) { char *p = NULL; - get_process_name(sfsi.ssi_pid, &p); + get_process_comm(sfsi.ssi_pid, &p); log_debug("Received SIG%s from PID %lu (%s).", signal_to_string(sfsi.ssi_signo), diff --git a/src/pam-module.c b/src/pam-module.c index dd05f93d42..78f9b30d5b 100644 --- a/src/pam-module.c +++ b/src/pam-module.c @@ -163,42 +163,24 @@ static int get_user_data( const char *username = NULL; struct passwd *pw = NULL; + uid_t uid; int r; - bool have_loginuid = false; - char *s; assert(handle); assert(ret_username); assert(ret_pw); - if (have_effective_cap(CAP_AUDIT_CONTROL) > 0) { - /* Only use audit login uid if we are executed with - * sufficient capabilities so that pam_loginuid could - * do its job. If we are lacking the CAP_AUDIT_CONTROL - * capabality we most likely are being run in a - * container and /proc/self/loginuid is useless since - * it probably contains a uid of the host system. */ - - if (read_one_line_file("/proc/self/loginuid", &s) >= 0) { - uid_t uid; - - r = parse_uid(s, &uid); - free(s); - - if (r >= 0 && uid != (uint32_t) -1) { - have_loginuid = true; - pw = pam_modutil_getpwuid(handle, uid); - } - } - } - - if (!have_loginuid) { - if ((r = pam_get_user(handle, &username, NULL)) != PAM_SUCCESS) { + r = audit_loginuid_from_pid(0, &uid); + if (r >= 0) + pw = pam_modutil_getpwuid(handle, uid); + else { + r = pam_get_user(handle, &username, NULL); + if (r != PAM_SUCCESS) { pam_syslog(handle, LOG_ERR, "Failed to get user name."); return r; } - if (!username || !*username) { + if (isempty(username)) { pam_syslog(handle, LOG_ERR, "User name not valid."); return PAM_AUTH_ERR; } diff --git a/src/sd-id128.c b/src/sd-id128.c new file mode 100644 index 0000000000..f5e0432a3f --- /dev/null +++ b/src/sd-id128.c @@ -0,0 +1,210 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "sd-id128.h" +#include "util.h" +#include "macro.h" + +char *sd_id128_to_string(sd_id128_t id, char s[33]) { + unsigned n; + + assert(s); + + for (n = 0; n < 16; n++) { + s[n*2] = hexchar(id.bytes[n] >> 4); + s[n*2+1] = hexchar(id.bytes[n] & 0xF); + } + + s[32] = 0; + + return s; +} + +int sd_id128_from_string(const char s[33], sd_id128_t *ret) { + unsigned n; + sd_id128_t t; + + assert(s); + assert(ret); + + for (n = 0; n < 16; n++) { + int a, b; + + a = unhexchar(s[n*2]); + if (a < 0) + return -EINVAL; + + b = unhexchar(s[n*2+1]); + if (b < 0) + return -EINVAL; + + t.bytes[n] = (a << 4) | b; + } + + if (s[32] != 0) + return -EINVAL; + + *ret = t; + return 0; +} + +sd_id128_t sd_id128_make_v4_uuid(sd_id128_t id) { + /* Stolen from generate_random_uuid() of drivers/char/random.c + * in the kernel sources */ + + /* Set UUID version to 4 --- truly random generation */ + id.bytes[6] = (id.bytes[6] & 0x0F) | 0x40; + + /* Set the UUID variant to DCE */ + id.bytes[8] = (id.bytes[8] & 0x3F) | 0x80; + + return id; +} + +int sd_id128_get_machine(sd_id128_t *ret) { + static __thread sd_id128_t saved_machine_id; + static __thread bool saved_machine_id_valid = false; + int fd; + char buf[32]; + ssize_t k; + unsigned j; + sd_id128_t t; + + if (saved_machine_id_valid) { + *ret = saved_machine_id; + return 0; + } + + fd = open("/etc/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + return -errno; + + k = loop_read(fd, buf, 32, false); + close_nointr_nofail(fd); + + if (k < 0) + return (int) k; + + if (k < 32) + return -EIO; + + for (j = 0; j < 16; j++) { + int a, b; + + a = unhexchar(buf[j*2]); + b = unhexchar(buf[j*2+1]); + + if (a < 0 || b < 0) + return -EIO; + + t.bytes[j] = a << 4 | b; + } + + saved_machine_id = t; + saved_machine_id_valid = true; + + *ret = t; + return 0; +} + +int sd_id128_get_boot(sd_id128_t *ret) { + static __thread sd_id128_t saved_boot_id; + static __thread bool saved_boot_id_valid = false; + int fd; + char buf[36]; + ssize_t k; + unsigned j; + sd_id128_t t; + char *p; + + if (saved_boot_id_valid) { + *ret = saved_boot_id; + return 0; + } + + fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + return -errno; + + k = loop_read(fd, buf, 36, false); + close_nointr_nofail(fd); + + if (k < 0) + return (int) k; + + if (k < 36) + return -EIO; + + for (j = 0, p = buf; j < 16; j++) { + int a, b; + + if (*p == '-') + p++; + + a = unhexchar(p[0]); + b = unhexchar(p[1]); + + if (a < 0 || b < 0) + return -EIO; + + t.bytes[j] = a << 4 | b; + + p += 2; + } + + saved_boot_id = t; + saved_boot_id_valid = true; + + *ret = t; + return 0; +} + +int sd_id128_randomize(sd_id128_t *ret) { + int fd; + ssize_t k; + sd_id128_t t; + + assert(ret); + + fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + return -errno; + + k = loop_read(fd, &t, 16, false); + close_nointr_nofail(fd); + + if (k < 0) + return (int) k; + + if (k < 16) + return -EIO; + + /* Turn this into a valid v4 UUID, to be nice. Note that we + * only guarantee this for newly generated UUIDs, not for + * pre-existing ones.*/ + + *ret = sd_id128_make_v4_uuid(t); + return 0; +} diff --git a/src/sd-id128.h b/src/sd-id128.h new file mode 100644 index 0000000000..bfae78b97d --- /dev/null +++ b/src/sd-id128.h @@ -0,0 +1,56 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef fooid128hfoo +#define fooid128hfoo + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +typedef union sd_id128 sd_id128_t; + +union sd_id128 { + uint8_t bytes[16]; + uint64_t qwords[2]; +}; + +char *sd_id128_to_string(sd_id128_t id, char s[33]); + +int sd_id128_from_string(const char s[33], sd_id128_t *ret); + +int sd_id128_randomize(sd_id128_t *ret); + +sd_id128_t sd_id128_make_v4_uuid(sd_id128_t id); + +int sd_id128_get_machine(sd_id128_t *ret); + +int sd_id128_get_boot(sd_id128_t *ret); + +#define SD_ID128_MAKE(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \ + ((sd_id128_t) { .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \ + 0x##v8, 0x##v9, 0x##v10, 0x##v11, 0x##v12, 0x##v13, 0x##v14, 0x##v15 }}) + +static inline bool sd_id128_equal(sd_id128_t a, sd_id128_t b) { + return memcmp(&a, &b, 16) == 0; +} + +#endif diff --git a/src/stdout-syslog-bridge.c b/src/stdout-syslog-bridge.c index 9a0408819e..d50df22c88 100644 --- a/src/stdout-syslog-bridge.c +++ b/src/stdout-syslog-bridge.c @@ -649,7 +649,8 @@ int main(int argc, char *argv[]) { umask(0022); - if ((n = sd_listen_fds(true)) < 0) { + n = sd_listen_fds(true); + if (n < 0) { log_error("Failed to read listening file descriptors from environment: %s", strerror(-r)); return EXIT_FAILURE; } diff --git a/src/systemctl.c b/src/systemctl.c index 0de2444d43..a423fdbf93 100644 --- a/src/systemctl.c +++ b/src/systemctl.c @@ -2182,7 +2182,7 @@ static void print_status_info(UnitStatusInfo *i) { if (i->running) { char *t = NULL; - get_process_name(i->main_pid, &t); + get_process_comm(i->main_pid, &t); if (t) { printf(" (%s)", t); free(t); @@ -2216,7 +2216,7 @@ static void print_status_info(UnitStatusInfo *i) { printf(" Control: %u", (unsigned) i->control_pid); - get_process_name(i->control_pid, &t); + get_process_comm(i->control_pid, &t); if (t) { printf(" (%s)", t); free(t); diff --git a/src/test-id128.c b/src/test-id128.c new file mode 100644 index 0000000000..6c3928d2e9 --- /dev/null +++ b/src/test-id128.c @@ -0,0 +1,49 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include + +#include "sd-id128.h" +#include "util.h" +#include "macro.h" + +#define ID128_WALDI SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10) + +int main(int argc, char *argv[]) { + sd_id128_t id, id2; + char t[33]; + + assert_se(sd_id128_randomize(&id) == 0); + printf("random: %s\n", sd_id128_to_string(id, t)); + + assert_se(sd_id128_from_string(t, &id2) == 0); + assert_se(sd_id128_equal(id, id2)); + + assert_se(sd_id128_get_machine(&id) == 0); + printf("machine: %s\n", sd_id128_to_string(id, t)); + + assert_se(sd_id128_get_boot(&id) == 0); + printf("boot: %s\n", sd_id128_to_string(id, t)); + + printf("waldi: %s\n", sd_id128_to_string(ID128_WALDI, t)); + + return 0; +} diff --git a/src/util.c b/src/util.c index 7977ee46c5..99737e4e63 100644 --- a/src/util.c +++ b/src/util.c @@ -55,6 +55,7 @@ #include #include #include +#include #include "macro.h" #include "util.h" @@ -73,7 +74,7 @@ size_t page_size(void) { static __thread size_t pgsz = 0; long r; - if (_likely_(pgsz)) + if (_likely_(pgsz > 0)) return pgsz; assert_se((r = sysconf(_SC_PAGESIZE)) > 0); @@ -993,46 +994,51 @@ char *truncate_nl(char *s) { return s; } -int get_process_name(pid_t pid, char **name) { - char *p; +int get_process_comm(pid_t pid, char **name) { int r; - assert(pid >= 1); assert(name); - if (asprintf(&p, "/proc/%lu/comm", (unsigned long) pid) < 0) - return -ENOMEM; - - r = read_one_line_file(p, name); - free(p); + if (pid == 0) + r = read_one_line_file("/proc/self/comm", name); + else { + char *p; + if (asprintf(&p, "/proc/%lu/comm", (unsigned long) pid) < 0) + return -ENOMEM; - if (r < 0) - return r; + r = read_one_line_file(p, name); + free(p); + } - return 0; + return r; } -int get_process_cmdline(pid_t pid, size_t max_length, char **line) { - char *p, *r, *k; +int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) { + char *r, *k; int c; bool space = false; size_t left; FILE *f; - assert(pid >= 1); assert(max_length > 0); assert(line); - if (asprintf(&p, "/proc/%lu/cmdline", (unsigned long) pid) < 0) - return -ENOMEM; + if (pid == 0) + f = fopen("/proc/self/cmdline", "re"); + else { + char *p; + if (asprintf(&p, "/proc/%lu/cmdline", (unsigned long) pid) < 0) + return -ENOMEM; - f = fopen(p, "re"); - free(p); + f = fopen(p, "re"); + free(p); + } if (!f) return -errno; - if (!(r = new(char, max_length))) { + r = new(char, max_length); + if (!r) { fclose(f); return -ENOMEM; } @@ -1076,13 +1082,17 @@ int get_process_cmdline(pid_t pid, size_t max_length, char **line) { free(r); - if ((h = get_process_name(pid, &t)) < 0) + if (!comm_fallback) + return -ENOENT; + + h = get_process_comm(pid, &t); + if (h < 0) return h; - h = asprintf(&r, "[%s]", t); + r = join("[", t, "]", NULL); free(t); - if (h < 0) + if (!r) return -ENOMEM; } @@ -1090,6 +1100,25 @@ int get_process_cmdline(pid_t pid, size_t max_length, char **line) { return 0; } +int get_process_exe(pid_t pid, char **name) { + int r; + + assert(name); + + if (pid == 0) + r = readlink_malloc("/proc/self/exe", name); + else { + char *p; + if (asprintf(&p, "/proc/%lu/exe", (unsigned long) pid) < 0) + return -ENOMEM; + + r = readlink_malloc(p, name); + free(p); + } + + return r; +} + char *strnappend(const char *s, const char *suffix, size_t b) { size_t a; char *r; @@ -4267,7 +4296,7 @@ const char *default_term_for_tty(const char *tty) { return term; } -bool dirent_is_file(struct dirent *de) { +bool dirent_is_file(const struct dirent *de) { assert(de); if (ignore_file(de->d_name)) @@ -4281,6 +4310,15 @@ bool dirent_is_file(struct dirent *de) { return true; } +bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) { + assert(de); + + if (!dirent_is_file(de)) + return false; + + return endswith(de->d_name, suffix); +} + void execute_directory(const char *directory, DIR *d, char *argv[]) { DIR *_d = NULL; struct dirent *de; @@ -4453,6 +4491,98 @@ void parse_syslog_priority(char **p, int *priority) { *p += k; } +void skip_syslog_pid(char **buf) { + char *p; + + assert(buf); + assert(*buf); + + p = *buf; + + if (*p != '[') + return; + + p++; + p += strspn(p, "0123456789"); + + if (*p != ']') + return; + + p++; + + *buf = p; +} + +void skip_syslog_date(char **buf) { + enum { + LETTER, + SPACE, + NUMBER, + SPACE_OR_NUMBER, + COLON + } sequence[] = { + LETTER, LETTER, LETTER, + SPACE, + SPACE_OR_NUMBER, NUMBER, + SPACE, + SPACE_OR_NUMBER, NUMBER, + COLON, + SPACE_OR_NUMBER, NUMBER, + COLON, + SPACE_OR_NUMBER, NUMBER, + SPACE + }; + + char *p; + unsigned i; + + assert(buf); + assert(*buf); + + p = *buf; + + for (i = 0; i < ELEMENTSOF(sequence); i++, p++) { + + if (!*p) + return; + + switch (sequence[i]) { + + case SPACE: + if (*p != ' ') + return; + break; + + case SPACE_OR_NUMBER: + if (*p == ' ') + break; + + /* fall through */ + + case NUMBER: + if (*p < '0' || *p > '9') + return; + + break; + + case LETTER: + if (!(*p >= 'A' && *p <= 'Z') && + !(*p >= 'a' && *p <= 'z')) + return; + + break; + + case COLON: + if (*p != ':') + return; + break; + + } + } + + *buf = p; +} + int have_effective_cap(int value) { cap_t cap; cap_flag_value_t fv; @@ -4672,21 +4802,6 @@ int vt_disallocate(const char *name) { return 0; } - -static int file_is_conf(const struct dirent *d, const char *suffix) { - assert(d); - - if (ignore_file(d->d_name)) - return 0; - - if (d->d_type != DT_REG && - d->d_type != DT_LNK && - d->d_type != DT_UNKNOWN) - return 0; - - return endswith(d->d_name, suffix); -} - static int files_add(Hashmap *h, const char *path, const char *suffix) { DIR *dir; struct dirent buffer, *de; @@ -4712,7 +4827,7 @@ static int files_add(Hashmap *h, const char *path, const char *suffix) { if (!de) break; - if (!file_is_conf(de, suffix)) + if (!dirent_is_file_with_suffix(de, suffix)) continue; if (asprintf(&p, "%s/%s", path, de->d_name) < 0) { @@ -5063,21 +5178,27 @@ int symlink_or_copy_atomic(const char *from, const char *to) { } int audit_session_from_pid(pid_t pid, uint32_t *id) { - char *p, *s; + char *s; uint32_t u; int r; - assert(pid >= 1); assert(id); if (have_effective_cap(CAP_AUDIT_CONTROL) <= 0) return -ENOENT; - if (asprintf(&p, "/proc/%lu/sessionid", (unsigned long) pid) < 0) - return -ENOMEM; + if (pid == 0) + r = read_one_line_file("/proc/self/sessionid", &s); + else { + char *p; + + if (asprintf(&p, "/proc/%lu/sessionid", (unsigned long) pid) < 0) + return -ENOMEM; + + r = read_one_line_file(p, &s); + free(p); + } - r = read_one_line_file(p, &s); - free(p); if (r < 0) return r; @@ -5094,6 +5215,51 @@ int audit_session_from_pid(pid_t pid, uint32_t *id) { return 0; } +int audit_loginuid_from_pid(pid_t pid, uid_t *uid) { + char *s; + uid_t u; + int r; + + assert(uid); + + /* Only use audit login uid if we are executed with sufficient + * capabilities so that pam_loginuid could do its job. If we + * are lacking the CAP_AUDIT_CONTROL capabality we most likely + * are being run in a container and /proc/self/loginuid is + * useless since it probably contains a uid of the host + * system. */ + + if (have_effective_cap(CAP_AUDIT_CONTROL) <= 0) + return -ENOENT; + + if (pid == 0) + r = read_one_line_file("/proc/self/loginuid", &s); + else { + char *p; + + if (asprintf(&p, "/proc/%lu/loginuid", (unsigned long) pid) < 0) + return -ENOMEM; + + r = read_one_line_file(p, &s); + free(p); + } + + if (r < 0) + return r; + + r = parse_uid(s, &u); + free(s); + + if (r < 0) + return r; + + if (u == (uid_t) -1) + return -ENOENT; + + *uid = (uid_t) u; + return 0; +} + bool display_is_local(const char *display) { assert(display); @@ -5700,3 +5866,21 @@ int strdup_or_null(const char *a, char **b) { *b = c; return 0; } + +int prot_from_flags(int flags) { + + switch (flags & O_ACCMODE) { + + case O_RDONLY: + return PROT_READ; + + case O_WRONLY: + return PROT_WRITE; + + case O_RDWR: + return PROT_READ|PROT_WRITE; + + default: + return -EINVAL; + } +} diff --git a/src/util.h b/src/util.h index ccbe8a3efa..1a2dd5825d 100644 --- a/src/util.h +++ b/src/util.h @@ -248,8 +248,9 @@ int parent_of_path(const char *path, char **parent); int rmdir_parents(const char *path, const char *stop); -int get_process_name(pid_t pid, char **name); -int get_process_cmdline(pid_t pid, size_t max_length, char **line); +int get_process_comm(pid_t pid, char **name); +int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line); +int get_process_exe(pid_t pid, char **name); char hexchar(int x); int unhexchar(char c); @@ -274,7 +275,9 @@ bool path_equal(const char *a, const char *b); char *ascii_strlower(char *path); -bool dirent_is_file(struct dirent *de); +bool dirent_is_file(const struct dirent *de); +bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix); + bool ignore_file(const char *filename); bool chars_intersect(const char *a, const char *b); @@ -415,6 +418,8 @@ bool nulstr_contains(const char*nulstr, const char *needle); bool plymouth_running(void); void parse_syslog_priority(char **p, int *priority); +void skip_syslog_pid(char **buf); +void skip_syslog_date(char **buf); int have_effective_cap(int value); @@ -443,6 +448,7 @@ int hwclock_get_time(struct tm *tm); int hwclock_set_time(const struct tm *tm); int audit_session_from_pid(pid_t pid, uint32_t *id); +int audit_loginuid_from_pid(pid_t pid, uid_t *uid); bool display_is_local(const char *display); int socket_from_display(const char *display, char **path); @@ -506,4 +512,6 @@ extern char **saved_argv; bool kexec_loaded(void); +int prot_from_flags(int flags); + #endif diff --git a/tmpfiles.d/Makefile b/tmpfiles.d/Makefile new file mode 120000 index 0000000000..bd1047548b --- /dev/null +++ b/tmpfiles.d/Makefile @@ -0,0 +1 @@ +../src/Makefile \ No newline at end of file -- cgit v1.2.3-54-g00ecf From 4b2d99d9f4258a29f0bf8b1a78d17836e75bc378 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 30 Dec 2011 17:50:37 +0100 Subject: journal: add unit files and shared library glue --- .gitignore | 2 + Makefile.am | 108 +++++++++++++++++++++++++++++++++++--- libsystemd-id128.pc.in | 18 +++++++ libsystemd-journal.pc.in | 19 +++++++ src/libsystemd-id128.sym | 22 ++++++++ src/libsystemd-journal.sym | 45 ++++++++++++++++ units/.gitignore | 1 + units/systemd-journald.service.in | 24 +++++++++ units/systemd-journald.socket | 24 +++++++++ 9 files changed, 255 insertions(+), 8 deletions(-) create mode 100644 libsystemd-id128.pc.in create mode 100644 libsystemd-journal.pc.in create mode 100644 src/libsystemd-id128.sym create mode 100644 src/libsystemd-journal.sym create mode 100644 units/systemd-journald.service.in create mode 100644 units/systemd-journald.socket (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 265801ff5f..28b40de7db 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +/libsystemd-journal.pc +/libsystemd-id128.pc systemd-journalctl systemd-journald test-id128 diff --git a/Makefile.am b/Makefile.am index 5fe67fd1c7..fa0a217813 100644 --- a/Makefile.am +++ b/Makefile.am @@ -27,6 +27,14 @@ LIBSYSTEMD_DAEMON_CURRENT=0 LIBSYSTEMD_DAEMON_REVISION=0 LIBSYSTEMD_DAEMON_AGE=0 +LIBSYSTEMD_ID128_CURRENT=0 +LIBSYSTEMD_ID128_REVISION=0 +LIBSYSTEMD_ID128_AGE=0 + +LIBSYSTEMD_JOURNAL_CURRENT=0 +LIBSYSTEMD_JOURNAL_REVISION=0 +LIBSYSTEMD_JOURNAL_AGE=0 + # Dirs of external packages dbuspolicydir=@dbuspolicydir@ dbussessionservicedir=@dbussessionservicedir@ @@ -217,11 +225,15 @@ endif lib_LTLIBRARIES = \ libsystemd-daemon.la \ - libsystemd-login.la + libsystemd-login.la \ + libsystemd-id128.la \ + libsystemd-journal.la pkginclude_HEADERS = \ src/sd-daemon.h \ - src/sd-login.h + src/sd-login.h \ + src/sd-id128.h \ + src/journal/sd-journal.h noinst_PROGRAMS = \ test-engine \ @@ -389,7 +401,8 @@ dist_systemunit_DATA = \ units/quotaon.service \ units/systemd-ask-password-wall.path \ units/systemd-ask-password-console.path \ - units/syslog.target + units/syslog.target \ + units/systemd-journald.socket if HAVE_SYSV_COMPAT dist_systemunit_DATA += \ @@ -410,6 +423,7 @@ nodist_systemunit_DATA = \ units/systemd-stdout-syslog-bridge.service \ units/systemd-shutdownd.service \ units/systemd-logind.service \ + units/systemd-journald.service \ units/systemd-kmsg-syslogd.service \ units/systemd-modules-load.service \ units/systemd-vconsole-setup.service \ @@ -473,7 +487,8 @@ EXTRA_DIST = \ units/systemd-stdout-syslog-bridge.service.in \ units/systemd-shutdownd.service.in \ units/systemd-logind.service.in \ - units/systemd-kmsg-syslogd.service.in \ + units/systemd-journald.service.in \ + units/systemd-kmsg-syslogd.service.in \ units/systemd-modules-load.service.in \ units/systemd-vconsole-setup.service.in \ units/systemd-remount-api-vfs.service.in \ @@ -502,8 +517,12 @@ EXTRA_DIST = \ systemd.pc.in \ libsystemd-daemon.pc.in \ libsystemd-login.pc.in \ + libsystemd-id128.pc.in \ + libsystemd-journal.pc.in \ src/libsystemd-daemon.sym \ src/libsystemd-login.sym \ + src/libsystemd-id128.sym \ + src/libsystemd-journal.sym \ introspect.awk \ src/73-seat-late.rules.in \ src/99-systemd.rules.in \ @@ -594,7 +613,9 @@ pkgconfigdata_DATA = \ pkgconfiglib_DATA = \ libsystemd-daemon.pc \ - libsystemd-login.pc + libsystemd-login.pc \ + libsystemd-id128.pc \ + libsystemd-journal.pc # Passed through intltool only polkitpolicy_in_files = \ @@ -770,7 +791,14 @@ EXTRA_DIST += \ src/logind-user.h \ src/logind-acl.h \ src/dbus-loop.h \ - src/spawn-agent.h + src/spawn-agent.h \ + src/journal/journal-def.h \ + src/journal/journal-internal.h \ + src/journal/journal-file.h \ + src/journal/lookup3.h \ + src/journal/compress.h \ + src/journal/journal-rate-limit.h \ + src/acl-util.h MANPAGES = \ man/systemd.1 \ @@ -1703,6 +1731,69 @@ libsystemd-login-install-hook: libsystemd-login-uninstall-hook: rm -f $(DESTDIR)$(rootlibdir)/libsystemd-login.so* +libsystemd_id128_la_SOURCES = \ + src/sd-id128.c + +libsystemd_id128_la_CFLAGS = \ + $(AM_CFLAGS) \ + -fvisibility=hidden + +libsystemd_id128_la_LDFLAGS = \ + -shared \ + -version-info $(LIBSYSTEMD_ID128_CURRENT):$(LIBSYSTEMD_ID128_REVISION):$(LIBSYSTEMD_ID128_AGE) \ + -Wl,--version-script=$(top_srcdir)/src/libsystemd-id128.sym + +libsystemd_id128_la_LIBADD = \ + libsystemd-basic.la + +# move lib from $(libdir) to $(rootlibdir) and update devel link, if needed +libsystemd-id128-install-hook: + if test "$(libdir)" != "$(rootlibdir)"; then \ + mkdir -p $(DESTDIR)$(rootlibdir) && \ + so_img_name=$$(readlink $(DESTDIR)$(libdir)/libsystemd-id128.so) && \ + so_img_rel_target_prefix=$$(echo $(libdir) | sed 's,\(^/\|\)[^/][^/]*,..,g') && \ + ln -sf $$so_img_rel_target_prefix$(rootlibdir)/$$so_img_name $(DESTDIR)$(libdir)/libsystemd-id128.so && \ + mv $(DESTDIR)$(libdir)/libsystemd-id128.so.* $(DESTDIR)$(rootlibdir); \ + fi + +libsystemd-id128-uninstall-hook: + rm -f $(DESTDIR)$(rootlibdir)/libsystemd-id128.so* + +libsystemd_journal_la_SOURCES = \ + src/journal/sd-journal.c \ + src/journal/journal-file.c \ + src/journal/compress.c \ + src/journal/lookup3.c \ + src/journal/journal-send.c + +libsystemd_journal_la_CFLAGS = \ + $(AM_CFLAGS) \ + $(XZ_CFLAGS) \ + -fvisibility=hidden + +libsystemd_journal_la_LDFLAGS = \ + -shared \ + -version-info $(LIBSYSTEMD_JOURNAL_CURRENT):$(LIBSYSTEMD_JOURNAL_REVISION):$(LIBSYSTEMD_JOURNAL_AGE) \ + -Wl,--version-script=$(top_srcdir)/src/libsystemd-journal.sym + +libsystemd_journal_la_LIBADD = \ + libsystemd-basic.la \ + libsystemd-id128.la \ + $(XZ_LIBS) + +# move lib from $(libdir) to $(rootlibdir) and update devel link, if needed +libsystemd-journal-install-hook: + if test "$(libdir)" != "$(rootlibdir)"; then \ + mkdir -p $(DESTDIR)$(rootlibdir) && \ + so_img_name=$$(readlink $(DESTDIR)$(libdir)/libsystemd-journal.so) && \ + so_img_rel_target_prefix=$$(echo $(libdir) | sed 's,\(^/\|\)[^/][^/]*,..,g') && \ + ln -sf $$so_img_rel_target_prefix$(rootlibdir)/$$so_img_name $(DESTDIR)$(libdir)/libsystemd-journal.so && \ + mv $(DESTDIR)$(libdir)/libsystemd-journal.so.* $(DESTDIR)$(rootlibdir); \ + fi + +libsystemd-journal-uninstall-hook: + rm -f $(DESTDIR)$(rootlibdir)/libsystemd-journal.so* + SED_PROCESS = \ $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \ $(SED) -e 's,@rootlibexecdir\@,$(rootlibexecdir),g' \ @@ -1919,11 +2010,12 @@ endif rm -f user && \ $(LN_S) $(pkgsysconfdir)/user user ) ( cd $(DESTDIR)$(systemunitdir)/sockets.target.wants && \ - rm -f systemd-initctl.socket systemd-stdout-syslog-bridge.socket systemd-shutdownd.socket syslog.socket && \ + rm -f systemd-initctl.socket systemd-stdout-syslog-bridge.socket systemd-shutdownd.socket syslog.socket systemd-journald.socket && \ $(LN_S) ../systemd-stdout-syslog-bridge.socket systemd-stdout-syslog-bridge.socket && \ $(LN_S) ../systemd-initctl.socket systemd-initctl.socket && \ $(LN_S) ../systemd-shutdownd.socket systemd-shutdownd.socket && \ - $(LN_S) ../syslog.socket syslog.socket ) + $(LN_S) ../syslog.socket syslog.socket && \ + $(LN_S) ../systemd-journald.socket ) ( cd $(DESTDIR)$(systemunitdir)/runlevel1.target.wants && \ rm -f systemd-update-utmp-runlevel.service && \ $(LN_S) ../systemd-update-utmp-runlevel.service systemd-update-utmp-runlevel.service ) diff --git a/libsystemd-id128.pc.in b/libsystemd-id128.pc.in new file mode 100644 index 0000000000..4d984fdff5 --- /dev/null +++ b/libsystemd-id128.pc.in @@ -0,0 +1,18 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: systemd +Description: systemd 128 Bit ID Utility Library +URL: @PACKAGE_URL@ +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lsystemd-id128 +Cflags: -I${includedir} diff --git a/libsystemd-journal.pc.in b/libsystemd-journal.pc.in new file mode 100644 index 0000000000..13cc8208df --- /dev/null +++ b/libsystemd-journal.pc.in @@ -0,0 +1,19 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: systemd +Description: systemd Journal Utility Library +URL: @PACKAGE_URL@ +Version: @PACKAGE_VERSION@ +Requires: libsystemd-id128 = @PACKAGE_VERSION@ +Libs: -L${libdir} -lsystemd-journal +Cflags: -I${includedir} diff --git a/src/libsystemd-id128.sym b/src/libsystemd-id128.sym new file mode 100644 index 0000000000..c4d1cf5d48 --- /dev/null +++ b/src/libsystemd-id128.sym @@ -0,0 +1,22 @@ +/*** + This file is part of systemd. + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. +***/ + +/* Original symbols from systemd v38 */ + +LIBSYSTEMD_ID128_38 { +global: + sd_id128_to_string; + sd_id128_from_string; + sd_id128_randomize; + sd_id128_make_v4_uuid; + sd_id128_get_machine; + sd_id128_get_boot; +local: + *; +}; diff --git a/src/libsystemd-journal.sym b/src/libsystemd-journal.sym new file mode 100644 index 0000000000..7653880e8f --- /dev/null +++ b/src/libsystemd-journal.sym @@ -0,0 +1,45 @@ +/*** + This file is part of systemd. + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. +***/ + +/* Original symbols from systemd v38 */ + +LIBSYSTEMD_JOURNAL_38 { +global: + sd_journal_print; + sd_journal_printv; + sd_journal_send; + sd_journal_sendv; + sd_journal_stream_fd; + sd_journal_open; + sd_journal_close; + sd_journal_previous; + sd_journal_next; + sd_journal_previous_skip; + sd_journal_next_skip; + sd_journal_get_realtime_usec; + sd_journal_get_monotonic_usec; + sd_journal_get_data; + sd_journal_enumerate_data; + sd_journal_restart_data; + sd_journal_add_match; + sd_journal_flush_matches; + sd_journal_seek_head; + sd_journal_seek_tail; + sd_journal_seek_monotonic_usec; + sd_journal_seek_realtime_usec; + sd_journal_seek_cursor; + sd_journal_get_cursor; + sd_journal_query_unique; + sd_journal_enumerate_unique; + sd_journal_restart_unique; + sd_journal_get_fd; + sd_journal_process; +local: + *; +}; diff --git a/units/.gitignore b/units/.gitignore index cc92c73022..94412d52e7 100644 --- a/units/.gitignore +++ b/units/.gitignore @@ -1,3 +1,4 @@ +/systemd-journald.service user@.service systemd-logind.service systemd-localed.service diff --git a/units/systemd-journald.service.in b/units/systemd-journald.service.in new file mode 100644 index 0000000000..2cfc68482e --- /dev/null +++ b/units/systemd-journald.service.in @@ -0,0 +1,24 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# See systemd.special(7) for details + +[Unit] +Description=Journal Service +DefaultDependencies=no +Requires=systemd-journald.socket +After=systemd-journald.socket + +[Service] +ExecStart=@rootlibexecdir@/systemd-journald +NotifyAccess=all +StandardOutput=null +#CapabilityBoundingSet=CAP_SYS_ADMIN CAP_SETUID CAP_SETGID + +# Increase the default a bit in order to allow many simultaneous +# services being run since we keep one fd open per service. +LimitNOFILE=16384 diff --git a/units/systemd-journald.socket b/units/systemd-journald.socket new file mode 100644 index 0000000000..b439bc1fdc --- /dev/null +++ b/units/systemd-journald.socket @@ -0,0 +1,24 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# See systemd.special(7) for details + +[Unit] +Description=Journal Socket +DefaultDependencies=no +Before=sockets.target syslog.target + +# Mount and swap units need this. If this socket unit is removed by an +# isolate request the mount and and swap units would be removed too, +# hence let's exclude this from isolate requests. +IgnoreOnIsolate=yes + +[Socket] +ListenStream=/run/systemd/stdout +ListenDatagram=/run/systemd/native +ListenDatagram=/dev/log +SocketMode=0666 -- cgit v1.2.3-54-g00ecf From fc5e60ee0c64343d1bd08c343275fc1ceff445aa Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 31 Dec 2011 03:02:57 +0100 Subject: git: update gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 28b40de7db..20e3ec1f24 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/systemd-rc-local-generator /libsystemd-journal.pc /libsystemd-id128.pc systemd-journalctl -- cgit v1.2.3-54-g00ecf From 4668191d0298339c70fba485f46233f79fcedc30 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Sat, 31 Dec 2011 06:20:34 +0100 Subject: timedated: move sources to subdirectory --- .gitignore | 4 +- Makefile.am | 88 +-- po/POTFILES.in | 2 +- po/POTFILES.skip | 2 +- src/org.freedesktop.timedate1.conf | 27 - src/org.freedesktop.timedate1.policy.in | 61 -- src/org.freedesktop.timedate1.service | 12 - src/timedate/org.freedesktop.timedate1.conf | 27 + src/timedate/org.freedesktop.timedate1.policy.in | 61 ++ src/timedate/org.freedesktop.timedate1.service | 12 + src/timedate/timedated.c | 911 +++++++++++++++++++++++ src/timedated.c | 911 ----------------------- 12 files changed, 1055 insertions(+), 1063 deletions(-) delete mode 100644 src/org.freedesktop.timedate1.conf delete mode 100644 src/org.freedesktop.timedate1.policy.in delete mode 100644 src/org.freedesktop.timedate1.service create mode 100644 src/timedate/org.freedesktop.timedate1.conf create mode 100644 src/timedate/org.freedesktop.timedate1.policy.in create mode 100644 src/timedate/org.freedesktop.timedate1.service create mode 100644 src/timedate/timedated.c delete mode 100644 src/timedated.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 20e3ec1f24..087ee2394a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,15 +8,15 @@ test-journal test-install org.freedesktop.hostname1.xml org.freedesktop.locale1.xml -org.freedesktop.timedate1.xml libsystemd-daemon.pc libsystemd-login.pc test-login systemd-loginctl systemd-localed systemd-timedated -systemd-uaccess +org.freedesktop.timedate1.xml systemd-logind +systemd-uaccess systemd-hostnamed systemd-binfmt systemd-getty-generator diff --git a/Makefile.am b/Makefile.am index 4a338145f4..3440e68ea4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -207,11 +207,6 @@ rootlibexec_PROGRAMS += \ systemd-hostnamed endif -if ENABLE_TIMEDATED -rootlibexec_PROGRAMS += \ - systemd-timedated -endif - if ENABLE_LOCALED rootlibexec_PROGRAMS += \ systemd-localed @@ -269,11 +264,6 @@ dist_dbuspolicy_DATA += \ src/org.freedesktop.hostname1.conf endif -if ENABLE_TIMEDATED -dist_dbuspolicy_DATA += \ - src/org.freedesktop.timedate1.conf -endif - if ENABLE_LOCALED dist_dbuspolicy_DATA += \ src/org.freedesktop.locale1.conf @@ -287,11 +277,6 @@ dist_dbussystemservice_DATA += \ src/org.freedesktop.hostname1.service endif -if ENABLE_TIMEDATED -dist_dbussystemservice_DATA += \ - src/org.freedesktop.timedate1.service -endif - if ENABLE_LOCALED dist_dbussystemservice_DATA += \ src/org.freedesktop.locale1.service @@ -324,11 +309,6 @@ dbusinterface_DATA += \ org.freedesktop.hostname1.xml endif -if ENABLE_TIMEDATED -dbusinterface_DATA += \ - org.freedesktop.timedate1.xml -endif - if ENABLE_LOCALED dbusinterface_DATA += \ org.freedesktop.locale1.xml @@ -458,11 +438,6 @@ nodist_systemunit_DATA += \ units/systemd-hostnamed.service endif -if ENABLE_TIMEDATED -nodist_systemunit_DATA += \ - units/systemd-timedated.service -endif - if ENABLE_LOCALED nodist_systemunit_DATA += \ units/systemd-localed.service @@ -532,11 +507,6 @@ EXTRA_DIST += \ units/systemd-hostnamed.service.in endif -if ENABLE_TIMEDATED -EXTRA_DIST += \ - units/systemd-timedated.service.in -endif - if ENABLE_LOCALED EXTRA_DIST += \ units/systemd-localed.service.in @@ -623,11 +593,6 @@ polkitpolicy_in_files += \ src/org.freedesktop.hostname1.policy.in endif -if ENABLE_TIMEDATED -polkitpolicy_in_files += \ - src/org.freedesktop.timedate1.policy.in -endif - if ENABLE_LOCALED polkitpolicy_in_files += \ src/org.freedesktop.locale1.policy.in @@ -1178,8 +1143,10 @@ dist_pkgdata_DATA = \ dist_noinst_SCRIPT = \ src/generate-kbd-model-map +# ------------------------------------------------------------------------------ +if ENABLE_TIMEDATED systemd_timedated_SOURCES = \ - src/timedated.c \ + src/timedate/timedated.c \ src/dbus-common.c \ src/polkit.c @@ -1192,6 +1159,41 @@ systemd_timedated_LDADD = \ libsystemd-daemon.la \ $(DBUS_LIBS) +rootlibexec_PROGRAMS += \ + systemd-timedated + +dist_dbussystemservice_DATA += \ + src/timedate/org.freedesktop.timedate1.service + +dist_dbuspolicy_DATA += \ + src/timedate/org.freedesktop.timedate1.conf + +nodist_systemunit_DATA += \ + units/systemd-timedated.service + +polkitpolicy_in_files += \ + src/timedate/org.freedesktop.timedate1.policy.in + +org.freedesktop.timedate1.xml: systemd-timedated + $(AM_V_GEN)$(LIBTOOL) --mode=execute $(OBJCOPY) -O binary -j introspect.timedate1 $< $@.tmp && \ + $(STRINGS) $@.tmp | $(AWK) -f $(srcdir)/introspect.awk | \ + $(DBUS_PREPROCESS) -o $@ - && rm $@.tmp + +dbusinterface_DATA += \ + org.freedesktop.timedate1.xml + +timedated-install-data-hook: + ( cd $(DESTDIR)$(systemunitdir) && \ + rm -f dbus-org.freedesktop.timedate1.service && \ + $(LN_S) systemd-timedated.service dbus-org.freedesktop.timedate1.service ) + +INSTALL_DATA_HOOKS += \ + timedated-install-data-hook + +EXTRA_DIST += \ + units/systemd-timedated.service.in +endif + # ------------------------------------------------------------------------------ if ENABLE_LOGIND systemd_logind_SOURCES = \ @@ -1328,7 +1330,7 @@ pkgconfiglib_DATA += \ polkitpolicy_in_files += \ src/login/org.freedesktop.login1.policy.in -logind-install-data: +logind-install-data-hook: ( cd $(DESTDIR)$(systemunitdir) && \ rm -f dbus-org.freedesktop.login1.service && \ $(LN_S) systemd-logind.service dbus-org.freedesktop.login1.service) @@ -1337,7 +1339,7 @@ logind-install-data: $(LN_S) ../systemd-logind.service systemd-logind.service ) INSTALL_DATA_HOOKS += \ - logind-install-data + logind-install-data-hook systemd_uaccess_SOURCES = \ src/login/uaccess.c @@ -2038,11 +2040,6 @@ org.freedesktop.locale1.xml: systemd-localed $(STRINGS) $@.tmp | $(AWK) -f $(srcdir)/introspect.awk | \ $(DBUS_PREPROCESS) -o $@ - && rm $@.tmp -org.freedesktop.timedate1.xml: systemd-timedated - $(AM_V_GEN)$(LIBTOOL) --mode=execute $(OBJCOPY) -O binary -j introspect.timedate1 $< $@.tmp && \ - $(STRINGS) $@.tmp | $(AWK) -f $(srcdir)/introspect.awk | \ - $(DBUS_PREPROCESS) -o $@ - && rm $@.tmp - CLEANFILES += \ $(dbusinterface_DATA) @@ -2199,11 +2196,6 @@ if ENABLE_HOSTNAMED rm -f dbus-org.freedesktop.hostname1.service && \ $(LN_S) systemd-hostnamed.service dbus-org.freedesktop.hostname1.service ) endif -if ENABLE_TIMEDATED - ( cd $(DESTDIR)$(systemunitdir) && \ - rm -f dbus-org.freedesktop.timedate1.service && \ - $(LN_S) systemd-timedated.service dbus-org.freedesktop.timedate1.service ) -endif if ENABLE_LOCALED ( cd $(DESTDIR)$(systemunitdir) && \ rm -f dbus-org.freedesktop.locale1.service && \ diff --git a/po/POTFILES.in b/po/POTFILES.in index 1da4dc1d0c..1a9090348a 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,4 +1,4 @@ src/org.freedesktop.hostname1.policy.in src/org.freedesktop.locale1.policy.in src/login/org.freedesktop.login1.policy.in -src/org.freedesktop.timedate1.policy.in +src/timedate/org.freedesktop.timedate1.policy.in diff --git a/po/POTFILES.skip b/po/POTFILES.skip index 192f3b6cc6..17c87f9560 100644 --- a/po/POTFILES.skip +++ b/po/POTFILES.skip @@ -14,6 +14,6 @@ src/dbus-unit.c src/hostnamed.c src/localed.c src/org.freedesktop.systemd1.policy.in.in -src/timedated.c +src/timedate/timedated.c units/systemd-readahead-done.service.in units/user@.service.in diff --git a/src/org.freedesktop.timedate1.conf b/src/org.freedesktop.timedate1.conf deleted file mode 100644 index c9c221b644..0000000000 --- a/src/org.freedesktop.timedate1.conf +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/src/org.freedesktop.timedate1.policy.in b/src/org.freedesktop.timedate1.policy.in deleted file mode 100644 index 3d9c2081d6..0000000000 --- a/src/org.freedesktop.timedate1.policy.in +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - The systemd Project - http://www.freedesktop.org/wiki/Software/systemd - - - <_description>Set system time - <_message>Authentication is required to set the system time. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - - - - <_description>Set system timezone - <_message>Authentication is required to set the system timezone. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - - - - <_description>Set RTC to local timezone or UTC - <_message>Authentication is required to control whether - the RTC stores the local or UTC time. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - - - - <_description>Turn network time synchronization on or off - <_message>Authentication is required to control whether - network time synchronization shall be enabled. - - auth_admin_keep - auth_admin_keep - auth_admin_keep - - - - diff --git a/src/org.freedesktop.timedate1.service b/src/org.freedesktop.timedate1.service deleted file mode 100644 index c3120b66cb..0000000000 --- a/src/org.freedesktop.timedate1.service +++ /dev/null @@ -1,12 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. - -[D-BUS Service] -Name=org.freedesktop.timedate1 -Exec=/bin/false -User=root -SystemdService=dbus-org.freedesktop.timedate1.service diff --git a/src/timedate/org.freedesktop.timedate1.conf b/src/timedate/org.freedesktop.timedate1.conf new file mode 100644 index 0000000000..c9c221b644 --- /dev/null +++ b/src/timedate/org.freedesktop.timedate1.conf @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/timedate/org.freedesktop.timedate1.policy.in b/src/timedate/org.freedesktop.timedate1.policy.in new file mode 100644 index 0000000000..3d9c2081d6 --- /dev/null +++ b/src/timedate/org.freedesktop.timedate1.policy.in @@ -0,0 +1,61 @@ + + + + + + + + The systemd Project + http://www.freedesktop.org/wiki/Software/systemd + + + <_description>Set system time + <_message>Authentication is required to set the system time. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + + <_description>Set system timezone + <_message>Authentication is required to set the system timezone. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + + <_description>Set RTC to local timezone or UTC + <_message>Authentication is required to control whether + the RTC stores the local or UTC time. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + + <_description>Turn network time synchronization on or off + <_message>Authentication is required to control whether + network time synchronization shall be enabled. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + diff --git a/src/timedate/org.freedesktop.timedate1.service b/src/timedate/org.freedesktop.timedate1.service new file mode 100644 index 0000000000..c3120b66cb --- /dev/null +++ b/src/timedate/org.freedesktop.timedate1.service @@ -0,0 +1,12 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +[D-BUS Service] +Name=org.freedesktop.timedate1 +Exec=/bin/false +User=root +SystemdService=dbus-org.freedesktop.timedate1.service diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c new file mode 100644 index 0000000000..16f54b59d2 --- /dev/null +++ b/src/timedate/timedated.c @@ -0,0 +1,911 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include + +#include +#include +#include + +#include "util.h" +#include "strv.h" +#include "dbus-common.h" +#include "polkit.h" +#include "def.h" + +#define NULL_ADJTIME_UTC "0.0 0 0\n0\nUTC\n" +#define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n" + +#define INTERFACE \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" + +#define INTROSPECTION \ + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ + "\n" \ + INTERFACE \ + BUS_PROPERTIES_INTERFACE \ + BUS_INTROSPECTABLE_INTERFACE \ + BUS_PEER_INTERFACE \ + "\n" + +#define INTERFACES_LIST \ + BUS_GENERIC_INTERFACES_LIST \ + "org.freedesktop.timedate1\0" + +const char timedate_interface[] _introspect_("timedate1") = INTERFACE; + +static char *zone = NULL; +static bool local_rtc = false; +static int use_ntp = -1; + +static usec_t remain_until = 0; + +static void free_data(void) { + free(zone); + zone = NULL; + + local_rtc = false; +} + +static bool valid_timezone(const char *name) { + const char *p; + char *t; + bool slash = false; + int r; + struct stat st; + + assert(name); + + if (*name == '/' || *name == 0) + return false; + + for (p = name; *p; p++) { + if (!(*p >= '0' && *p <= '9') && + !(*p >= 'a' && *p <= 'z') && + !(*p >= 'A' && *p <= 'Z') && + !(*p == '-' || *p == '_' || *p == '+' || *p == '/')) + return false; + + if (*p == '/') { + + if (slash) + return false; + + slash = true; + } else + slash = false; + } + + if (slash) + return false; + + t = strappend("/usr/share/zoneinfo/", name); + if (!t) + return false; + + r = stat(t, &st); + free(t); + + if (r < 0) + return false; + + if (!S_ISREG(st.st_mode)) + return false; + + return true; +} + +static void verify_timezone(void) { + char *p, *a = NULL, *b = NULL; + size_t l, q; + int j, k; + + if (!zone) + return; + + p = strappend("/usr/share/zoneinfo/", zone); + if (!p) { + log_error("Out of memory"); + return; + } + + j = read_full_file("/etc/localtime", &a, &l); + k = read_full_file(p, &b, &q); + + free(p); + + if (j < 0 || k < 0 || l != q || memcmp(a, b, l)) { + log_warning("/etc/localtime and /etc/timezone out of sync."); + free(zone); + zone = NULL; + } + + free(a); + free(b); +} + +static int read_data(void) { + int r; + + free_data(); + + r = read_one_line_file("/etc/timezone", &zone); + if (r < 0) { + if (r != -ENOENT) + log_warning("Failed to read /etc/timezone: %s", strerror(-r)); + +#ifdef TARGET_FEDORA + r = parse_env_file("/etc/sysconfig/clock", NEWLINE, + "ZONE", &zone, + NULL); + + if (r < 0 && r != -ENOENT) + log_warning("Failed to read /etc/sysconfig/clock: %s", strerror(-r)); +#endif + } + + if (isempty(zone)) { + free(zone); + zone = NULL; + } + + verify_timezone(); + + local_rtc = hwclock_is_localtime() > 0; + + return 0; +} + +static int write_data_timezone(void) { + int r = 0; + char *p; + + if (!zone) { + if (unlink("/etc/timezone") < 0 && errno != ENOENT) + r = -errno; + + if (unlink("/etc/localtime") < 0 && errno != ENOENT) + r = -errno; + + return r; + } + + p = strappend("/usr/share/zoneinfo/", zone); + if (!p) { + log_error("Out of memory"); + return -ENOMEM; + } + + r = symlink_or_copy_atomic(p, "/etc/localtime"); + free(p); + + if (r < 0) + return r; + + r = write_one_line_file_atomic("/etc/timezone", zone); + if (r < 0) + return r; + + return 0; +} + +static int write_data_local_rtc(void) { + int r; + char *s, *w; + + r = read_full_file("/etc/adjtime", &s, NULL); + if (r < 0) { + if (r != -ENOENT) + return r; + + if (!local_rtc) + return 0; + + w = strdup(NULL_ADJTIME_LOCAL); + if (!w) + return -ENOMEM; + } else { + char *p, *e; + size_t a, b; + + p = strchr(s, '\n'); + if (!p) { + free(s); + return -EIO; + } + + p = strchr(p+1, '\n'); + if (!p) { + free(s); + return -EIO; + } + + p++; + e = strchr(p, '\n'); + if (!e) { + free(s); + return -EIO; + } + + a = p - s; + b = strlen(e); + + w = new(char, a + (local_rtc ? 5 : 3) + b + 1); + if (!w) { + free(s); + return -ENOMEM; + } + + *(char*) mempcpy(stpcpy(mempcpy(w, s, a), local_rtc ? "LOCAL" : "UTC"), e, b) = 0; + + if (streq(w, NULL_ADJTIME_UTC)) { + free(w); + + if (unlink("/etc/adjtime") < 0) { + if (errno != ENOENT) + return -errno; + } + + return 0; + } + } + + r = write_one_line_file_atomic("/etc/adjtime", w); + free(w); + + return r; +} + +static int read_ntp(DBusConnection *bus) { + DBusMessage *m = NULL, *reply = NULL; + const char *name = "ntpd.service", *s; + DBusError error; + int r; + + assert(bus); + + dbus_error_init(&error); + + m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetUnitFileState"); + + if (!m) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + if (!dbus_message_append_args(m, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID)) { + log_error("Could not append arguments to message."); + r = -ENOMEM; + goto finish; + } + + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); + if (!reply) { + log_error("Failed to issue method call: %s", bus_error_message(&error)); + r = -EIO; + goto finish; + } + + if (!dbus_message_get_args(reply, &error, + DBUS_TYPE_STRING, &s, + DBUS_TYPE_INVALID)) { + log_error("Failed to parse reply: %s", bus_error_message(&error)); + r = -EIO; + goto finish; + } + + use_ntp = + streq(s, "enabled") || + streq(s, "enabled-runtime"); + r = 0; + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); + + return r; +} + +static int start_ntp(DBusConnection *bus, DBusError *error) { + DBusMessage *m = NULL, *reply = NULL; + const char *name = "ntpd.service", *mode = "replace"; + int r; + + assert(bus); + assert(error); + + m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + use_ntp ? "StartUnit" : "StopUnit"); + if (!m) { + log_error("Could not allocate message."); + r = -ENOMEM; + goto finish; + } + + if (!dbus_message_append_args(m, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &mode, + DBUS_TYPE_INVALID)) { + log_error("Could not append arguments to message."); + r = -ENOMEM; + goto finish; + } + + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error); + if (!reply) { + log_error("Failed to issue method call: %s", bus_error_message(error)); + r = -EIO; + goto finish; + } + + r = 0; + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + return r; +} + +static int enable_ntp(DBusConnection *bus, DBusError *error) { + DBusMessage *m = NULL, *reply = NULL; + const char * const names[] = { "ntpd.service", NULL }; + int r; + DBusMessageIter iter; + dbus_bool_t f = FALSE, t = TRUE; + + assert(bus); + assert(error); + + m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + use_ntp ? "EnableUnitFiles" : "DisableUnitFiles"); + + if (!m) { + log_error("Could not allocate message."); + r = -ENOMEM; + goto finish; + } + + dbus_message_iter_init_append(m, &iter); + + r = bus_append_strv_iter(&iter, (char**) names); + if (r < 0) { + log_error("Failed to append unit files."); + goto finish; + } + /* send runtime bool */ + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &f)) { + log_error("Failed to append runtime boolean."); + r = -ENOMEM; + goto finish; + } + + if (use_ntp) { + /* send force bool */ + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &t)) { + log_error("Failed to append force boolean."); + r = -ENOMEM; + goto finish; + } + } + + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error); + if (!reply) { + log_error("Failed to issue method call: %s", bus_error_message(error)); + r = -EIO; + goto finish; + } + + dbus_message_unref(m); + m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "Reload"); + if (!m) { + log_error("Could not allocate message."); + r = -ENOMEM; + goto finish; + } + + dbus_message_unref(reply); + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error); + if (!reply) { + log_error("Failed to issue method call: %s", bus_error_message(error)); + r = -EIO; + goto finish; + } + + r = 0; + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + return r; +} + +static int property_append_ntp(DBusMessageIter *i, const char *property, void *data) { + dbus_bool_t db; + + assert(i); + assert(property); + + db = use_ntp > 0; + + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db)) + return -ENOMEM; + + return 0; +} + +static DBusHandlerResult timedate_message_handler( + DBusConnection *connection, + DBusMessage *message, + void *userdata) { + + const BusProperty properties[] = { + { "org.freedesktop.timedate1", "Timezone", bus_property_append_string, "s", zone }, + { "org.freedesktop.timedate1", "LocalRTC", bus_property_append_bool, "b", &local_rtc }, + { "org.freedesktop.timedate1", "NTP", property_append_ntp, "b", NULL }, + { NULL, NULL, NULL, NULL, NULL } + }; + + DBusMessage *reply = NULL, *changed = NULL; + DBusError error; + int r; + + assert(connection); + assert(message); + + dbus_error_init(&error); + + if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetTimezone")) { + const char *z; + dbus_bool_t interactive; + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_STRING, &z, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + if (!valid_timezone(z)) + return bus_send_error_reply(connection, message, NULL, -EINVAL); + + if (!streq_ptr(z, zone)) { + char *t; + + r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-timezone", interactive, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + t = strdup(z); + if (!t) + goto oom; + + free(zone); + zone = t; + + /* 1. Write new configuration file */ + r = write_data_timezone(); + if (r < 0) { + log_error("Failed to set timezone: %s", strerror(-r)); + return bus_send_error_reply(connection, message, NULL, r); + } + + if (local_rtc) { + struct timespec ts; + struct tm *tm; + + /* 2. Teach kernel new timezone */ + hwclock_apply_localtime_delta(NULL); + + /* 3. Sync RTC from system clock, with the new delta */ + assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0); + assert_se(tm = localtime(&ts.tv_sec)); + hwclock_set_time(tm); + } + + log_info("Changed timezone to '%s'.", zone); + + changed = bus_properties_changed_new( + "/org/freedesktop/timedate1", + "org.freedesktop.timedate1", + "Timezone\0"); + if (!changed) + goto oom; + } + + } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetLocalRTC")) { + dbus_bool_t lrtc; + dbus_bool_t fix_system; + dbus_bool_t interactive; + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_BOOLEAN, &lrtc, + DBUS_TYPE_BOOLEAN, &fix_system, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + if (lrtc != local_rtc) { + struct timespec ts; + + r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-local-rtc", interactive, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + local_rtc = lrtc; + + /* 1. Write new configuration file */ + r = write_data_local_rtc(); + if (r < 0) { + log_error("Failed to set RTC to local/UTC: %s", strerror(-r)); + return bus_send_error_reply(connection, message, NULL, r); + } + + /* 2. Teach kernel new timezone */ + if (local_rtc) + hwclock_apply_localtime_delta(NULL); + else + hwclock_reset_localtime_delta(); + + /* 3. Synchronize clocks */ + assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0); + + if (fix_system) { + struct tm tm; + + /* Sync system clock from RTC; first, + * initialize the timezone fields of + * struct tm. */ + if (local_rtc) + tm = *localtime(&ts.tv_sec); + else + tm = *gmtime(&ts.tv_sec); + + /* Override the main fields of + * struct tm, but not the timezone + * fields */ + if (hwclock_get_time(&tm) >= 0) { + + /* And set the system clock + * with this */ + if (local_rtc) + ts.tv_sec = mktime(&tm); + else + ts.tv_sec = timegm(&tm); + + clock_settime(CLOCK_REALTIME, &ts); + } + + } else { + struct tm *tm; + + /* Sync RTC from system clock */ + if (local_rtc) + tm = localtime(&ts.tv_sec); + else + tm = gmtime(&ts.tv_sec); + + hwclock_set_time(tm); + } + + log_info("RTC configured to %s time.", local_rtc ? "local" : "UTC"); + + changed = bus_properties_changed_new( + "/org/freedesktop/timedate1", + "org.freedesktop.timedate1", + "LocalRTC\0"); + if (!changed) + goto oom; + } + + } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetTime")) { + int64_t utc; + dbus_bool_t relative; + dbus_bool_t interactive; + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_INT64, &utc, + DBUS_TYPE_BOOLEAN, &relative, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + if (!relative && utc <= 0) + return bus_send_error_reply(connection, message, NULL, -EINVAL); + + if (!relative || utc != 0) { + struct timespec ts; + struct tm* tm; + + r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-time", interactive, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + if (relative) + timespec_store(&ts, now(CLOCK_REALTIME) + utc); + else + timespec_store(&ts, utc); + + /* Set system clock */ + if (clock_settime(CLOCK_REALTIME, &ts) < 0) { + log_error("Failed to set local time: %m"); + return bus_send_error_reply(connection, message, NULL, -errno); + } + + /* Sync down to RTC */ + if (local_rtc) + tm = localtime(&ts.tv_sec); + else + tm = gmtime(&ts.tv_sec); + + hwclock_set_time(tm); + + log_info("Changed local time to %s", ctime(&ts.tv_sec)); + } + } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetNTP")) { + dbus_bool_t ntp; + dbus_bool_t interactive; + + if (!dbus_message_get_args( + message, + &error, + DBUS_TYPE_BOOLEAN, &ntp, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID)) + return bus_send_error_reply(connection, message, &error, -EINVAL); + + if (ntp != !!use_ntp) { + + r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-ntp", interactive, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + use_ntp = !!ntp; + + r = enable_ntp(connection, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + r = start_ntp(connection, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + log_info("Set NTP to %s", use_ntp ? "enabled" : "disabled"); + + changed = bus_properties_changed_new( + "/org/freedesktop/timedate1", + "org.freedesktop.timedate1", + "NTP\0"); + if (!changed) + goto oom; + } + + } else + return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties); + + if (!(reply = dbus_message_new_method_return(message))) + goto oom; + + if (!dbus_connection_send(connection, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + reply = NULL; + + if (changed) { + + if (!dbus_connection_send(connection, changed, NULL)) + goto oom; + + dbus_message_unref(changed); + } + + return DBUS_HANDLER_RESULT_HANDLED; + +oom: + if (reply) + dbus_message_unref(reply); + + if (changed) + dbus_message_unref(changed); + + dbus_error_free(&error); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; +} + +static int connect_bus(DBusConnection **_bus) { + static const DBusObjectPathVTable timedate_vtable = { + .message_function = timedate_message_handler + }; + DBusError error; + DBusConnection *bus = NULL; + int r; + + assert(_bus); + + dbus_error_init(&error); + + bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); + if (!bus) { + log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error)); + r = -ECONNREFUSED; + goto fail; + } + + dbus_connection_set_exit_on_disconnect(bus, FALSE); + + if (!dbus_connection_register_object_path(bus, "/org/freedesktop/timedate1", &timedate_vtable, NULL) || + !dbus_connection_add_filter(bus, bus_exit_idle_filter, &remain_until, NULL)) { + log_error("Not enough memory"); + r = -ENOMEM; + goto fail; + } + + r = dbus_bus_request_name(bus, "org.freedesktop.timedate1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error); + if (dbus_error_is_set(&error)) { + log_error("Failed to register name on bus: %s", bus_error_message(&error)); + r = -EEXIST; + goto fail; + } + + if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + log_error("Failed to acquire name."); + r = -EEXIST; + goto fail; + } + + if (_bus) + *_bus = bus; + + return 0; + +fail: + dbus_connection_close(bus); + dbus_connection_unref(bus); + + dbus_error_free(&error); + + return r; +} + +int main(int argc, char *argv[]) { + int r; + DBusConnection *bus = NULL; + bool exiting = false; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + if (argc == 2 && streq(argv[1], "--introspect")) { + fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE + "\n", stdout); + fputs(timedate_interface, stdout); + fputs("\n", stdout); + return 0; + } + + if (argc != 1) { + log_error("This program takes no arguments."); + r = -EINVAL; + goto finish; + } + + r = read_data(); + if (r < 0) { + log_error("Failed to read timezone data: %s", strerror(-r)); + goto finish; + } + + r = connect_bus(&bus); + if (r < 0) + goto finish; + + r = read_ntp(bus); + if (r < 0) { + log_error("Failed to determine whether NTP is enabled: %s", strerror(-r)); + goto finish; + } + + remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC; + for (;;) { + + if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))) + break; + + if (!exiting && remain_until < now(CLOCK_MONOTONIC)) { + exiting = true; + bus_async_unregister_and_exit(bus, "org.freedesktop.hostname1"); + } + } + + r = 0; + +finish: + free_data(); + + if (bus) { + dbus_connection_flush(bus); + dbus_connection_close(bus); + dbus_connection_unref(bus); + } + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/timedated.c b/src/timedated.c deleted file mode 100644 index 16f54b59d2..0000000000 --- a/src/timedated.c +++ /dev/null @@ -1,911 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with systemd; If not, see . -***/ - -#include - -#include -#include -#include - -#include "util.h" -#include "strv.h" -#include "dbus-common.h" -#include "polkit.h" -#include "def.h" - -#define NULL_ADJTIME_UTC "0.0 0 0\n0\nUTC\n" -#define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n" - -#define INTERFACE \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" - -#define INTROSPECTION \ - DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ - "\n" \ - INTERFACE \ - BUS_PROPERTIES_INTERFACE \ - BUS_INTROSPECTABLE_INTERFACE \ - BUS_PEER_INTERFACE \ - "\n" - -#define INTERFACES_LIST \ - BUS_GENERIC_INTERFACES_LIST \ - "org.freedesktop.timedate1\0" - -const char timedate_interface[] _introspect_("timedate1") = INTERFACE; - -static char *zone = NULL; -static bool local_rtc = false; -static int use_ntp = -1; - -static usec_t remain_until = 0; - -static void free_data(void) { - free(zone); - zone = NULL; - - local_rtc = false; -} - -static bool valid_timezone(const char *name) { - const char *p; - char *t; - bool slash = false; - int r; - struct stat st; - - assert(name); - - if (*name == '/' || *name == 0) - return false; - - for (p = name; *p; p++) { - if (!(*p >= '0' && *p <= '9') && - !(*p >= 'a' && *p <= 'z') && - !(*p >= 'A' && *p <= 'Z') && - !(*p == '-' || *p == '_' || *p == '+' || *p == '/')) - return false; - - if (*p == '/') { - - if (slash) - return false; - - slash = true; - } else - slash = false; - } - - if (slash) - return false; - - t = strappend("/usr/share/zoneinfo/", name); - if (!t) - return false; - - r = stat(t, &st); - free(t); - - if (r < 0) - return false; - - if (!S_ISREG(st.st_mode)) - return false; - - return true; -} - -static void verify_timezone(void) { - char *p, *a = NULL, *b = NULL; - size_t l, q; - int j, k; - - if (!zone) - return; - - p = strappend("/usr/share/zoneinfo/", zone); - if (!p) { - log_error("Out of memory"); - return; - } - - j = read_full_file("/etc/localtime", &a, &l); - k = read_full_file(p, &b, &q); - - free(p); - - if (j < 0 || k < 0 || l != q || memcmp(a, b, l)) { - log_warning("/etc/localtime and /etc/timezone out of sync."); - free(zone); - zone = NULL; - } - - free(a); - free(b); -} - -static int read_data(void) { - int r; - - free_data(); - - r = read_one_line_file("/etc/timezone", &zone); - if (r < 0) { - if (r != -ENOENT) - log_warning("Failed to read /etc/timezone: %s", strerror(-r)); - -#ifdef TARGET_FEDORA - r = parse_env_file("/etc/sysconfig/clock", NEWLINE, - "ZONE", &zone, - NULL); - - if (r < 0 && r != -ENOENT) - log_warning("Failed to read /etc/sysconfig/clock: %s", strerror(-r)); -#endif - } - - if (isempty(zone)) { - free(zone); - zone = NULL; - } - - verify_timezone(); - - local_rtc = hwclock_is_localtime() > 0; - - return 0; -} - -static int write_data_timezone(void) { - int r = 0; - char *p; - - if (!zone) { - if (unlink("/etc/timezone") < 0 && errno != ENOENT) - r = -errno; - - if (unlink("/etc/localtime") < 0 && errno != ENOENT) - r = -errno; - - return r; - } - - p = strappend("/usr/share/zoneinfo/", zone); - if (!p) { - log_error("Out of memory"); - return -ENOMEM; - } - - r = symlink_or_copy_atomic(p, "/etc/localtime"); - free(p); - - if (r < 0) - return r; - - r = write_one_line_file_atomic("/etc/timezone", zone); - if (r < 0) - return r; - - return 0; -} - -static int write_data_local_rtc(void) { - int r; - char *s, *w; - - r = read_full_file("/etc/adjtime", &s, NULL); - if (r < 0) { - if (r != -ENOENT) - return r; - - if (!local_rtc) - return 0; - - w = strdup(NULL_ADJTIME_LOCAL); - if (!w) - return -ENOMEM; - } else { - char *p, *e; - size_t a, b; - - p = strchr(s, '\n'); - if (!p) { - free(s); - return -EIO; - } - - p = strchr(p+1, '\n'); - if (!p) { - free(s); - return -EIO; - } - - p++; - e = strchr(p, '\n'); - if (!e) { - free(s); - return -EIO; - } - - a = p - s; - b = strlen(e); - - w = new(char, a + (local_rtc ? 5 : 3) + b + 1); - if (!w) { - free(s); - return -ENOMEM; - } - - *(char*) mempcpy(stpcpy(mempcpy(w, s, a), local_rtc ? "LOCAL" : "UTC"), e, b) = 0; - - if (streq(w, NULL_ADJTIME_UTC)) { - free(w); - - if (unlink("/etc/adjtime") < 0) { - if (errno != ENOENT) - return -errno; - } - - return 0; - } - } - - r = write_one_line_file_atomic("/etc/adjtime", w); - free(w); - - return r; -} - -static int read_ntp(DBusConnection *bus) { - DBusMessage *m = NULL, *reply = NULL; - const char *name = "ntpd.service", *s; - DBusError error; - int r; - - assert(bus); - - dbus_error_init(&error); - - m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GetUnitFileState"); - - if (!m) { - log_error("Out of memory"); - r = -ENOMEM; - goto finish; - } - - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - - reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); - if (!reply) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } - - if (!dbus_message_get_args(reply, &error, - DBUS_TYPE_STRING, &s, - DBUS_TYPE_INVALID)) { - log_error("Failed to parse reply: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } - - use_ntp = - streq(s, "enabled") || - streq(s, "enabled-runtime"); - r = 0; - -finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - - dbus_error_free(&error); - - return r; -} - -static int start_ntp(DBusConnection *bus, DBusError *error) { - DBusMessage *m = NULL, *reply = NULL; - const char *name = "ntpd.service", *mode = "replace"; - int r; - - assert(bus); - assert(error); - - m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - use_ntp ? "StartUnit" : "StopUnit"); - if (!m) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_STRING, &mode, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - - reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error); - if (!reply) { - log_error("Failed to issue method call: %s", bus_error_message(error)); - r = -EIO; - goto finish; - } - - r = 0; - -finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - - return r; -} - -static int enable_ntp(DBusConnection *bus, DBusError *error) { - DBusMessage *m = NULL, *reply = NULL; - const char * const names[] = { "ntpd.service", NULL }; - int r; - DBusMessageIter iter; - dbus_bool_t f = FALSE, t = TRUE; - - assert(bus); - assert(error); - - m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - use_ntp ? "EnableUnitFiles" : "DisableUnitFiles"); - - if (!m) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - - dbus_message_iter_init_append(m, &iter); - - r = bus_append_strv_iter(&iter, (char**) names); - if (r < 0) { - log_error("Failed to append unit files."); - goto finish; - } - /* send runtime bool */ - if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &f)) { - log_error("Failed to append runtime boolean."); - r = -ENOMEM; - goto finish; - } - - if (use_ntp) { - /* send force bool */ - if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &t)) { - log_error("Failed to append force boolean."); - r = -ENOMEM; - goto finish; - } - } - - reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error); - if (!reply) { - log_error("Failed to issue method call: %s", bus_error_message(error)); - r = -EIO; - goto finish; - } - - dbus_message_unref(m); - m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "Reload"); - if (!m) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - - dbus_message_unref(reply); - reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error); - if (!reply) { - log_error("Failed to issue method call: %s", bus_error_message(error)); - r = -EIO; - goto finish; - } - - r = 0; - -finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - - return r; -} - -static int property_append_ntp(DBusMessageIter *i, const char *property, void *data) { - dbus_bool_t db; - - assert(i); - assert(property); - - db = use_ntp > 0; - - if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db)) - return -ENOMEM; - - return 0; -} - -static DBusHandlerResult timedate_message_handler( - DBusConnection *connection, - DBusMessage *message, - void *userdata) { - - const BusProperty properties[] = { - { "org.freedesktop.timedate1", "Timezone", bus_property_append_string, "s", zone }, - { "org.freedesktop.timedate1", "LocalRTC", bus_property_append_bool, "b", &local_rtc }, - { "org.freedesktop.timedate1", "NTP", property_append_ntp, "b", NULL }, - { NULL, NULL, NULL, NULL, NULL } - }; - - DBusMessage *reply = NULL, *changed = NULL; - DBusError error; - int r; - - assert(connection); - assert(message); - - dbus_error_init(&error); - - if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetTimezone")) { - const char *z; - dbus_bool_t interactive; - - if (!dbus_message_get_args( - message, - &error, - DBUS_TYPE_STRING, &z, - DBUS_TYPE_BOOLEAN, &interactive, - DBUS_TYPE_INVALID)) - return bus_send_error_reply(connection, message, &error, -EINVAL); - - if (!valid_timezone(z)) - return bus_send_error_reply(connection, message, NULL, -EINVAL); - - if (!streq_ptr(z, zone)) { - char *t; - - r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-timezone", interactive, &error); - if (r < 0) - return bus_send_error_reply(connection, message, &error, r); - - t = strdup(z); - if (!t) - goto oom; - - free(zone); - zone = t; - - /* 1. Write new configuration file */ - r = write_data_timezone(); - if (r < 0) { - log_error("Failed to set timezone: %s", strerror(-r)); - return bus_send_error_reply(connection, message, NULL, r); - } - - if (local_rtc) { - struct timespec ts; - struct tm *tm; - - /* 2. Teach kernel new timezone */ - hwclock_apply_localtime_delta(NULL); - - /* 3. Sync RTC from system clock, with the new delta */ - assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0); - assert_se(tm = localtime(&ts.tv_sec)); - hwclock_set_time(tm); - } - - log_info("Changed timezone to '%s'.", zone); - - changed = bus_properties_changed_new( - "/org/freedesktop/timedate1", - "org.freedesktop.timedate1", - "Timezone\0"); - if (!changed) - goto oom; - } - - } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetLocalRTC")) { - dbus_bool_t lrtc; - dbus_bool_t fix_system; - dbus_bool_t interactive; - - if (!dbus_message_get_args( - message, - &error, - DBUS_TYPE_BOOLEAN, &lrtc, - DBUS_TYPE_BOOLEAN, &fix_system, - DBUS_TYPE_BOOLEAN, &interactive, - DBUS_TYPE_INVALID)) - return bus_send_error_reply(connection, message, &error, -EINVAL); - - if (lrtc != local_rtc) { - struct timespec ts; - - r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-local-rtc", interactive, &error); - if (r < 0) - return bus_send_error_reply(connection, message, &error, r); - - local_rtc = lrtc; - - /* 1. Write new configuration file */ - r = write_data_local_rtc(); - if (r < 0) { - log_error("Failed to set RTC to local/UTC: %s", strerror(-r)); - return bus_send_error_reply(connection, message, NULL, r); - } - - /* 2. Teach kernel new timezone */ - if (local_rtc) - hwclock_apply_localtime_delta(NULL); - else - hwclock_reset_localtime_delta(); - - /* 3. Synchronize clocks */ - assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0); - - if (fix_system) { - struct tm tm; - - /* Sync system clock from RTC; first, - * initialize the timezone fields of - * struct tm. */ - if (local_rtc) - tm = *localtime(&ts.tv_sec); - else - tm = *gmtime(&ts.tv_sec); - - /* Override the main fields of - * struct tm, but not the timezone - * fields */ - if (hwclock_get_time(&tm) >= 0) { - - /* And set the system clock - * with this */ - if (local_rtc) - ts.tv_sec = mktime(&tm); - else - ts.tv_sec = timegm(&tm); - - clock_settime(CLOCK_REALTIME, &ts); - } - - } else { - struct tm *tm; - - /* Sync RTC from system clock */ - if (local_rtc) - tm = localtime(&ts.tv_sec); - else - tm = gmtime(&ts.tv_sec); - - hwclock_set_time(tm); - } - - log_info("RTC configured to %s time.", local_rtc ? "local" : "UTC"); - - changed = bus_properties_changed_new( - "/org/freedesktop/timedate1", - "org.freedesktop.timedate1", - "LocalRTC\0"); - if (!changed) - goto oom; - } - - } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetTime")) { - int64_t utc; - dbus_bool_t relative; - dbus_bool_t interactive; - - if (!dbus_message_get_args( - message, - &error, - DBUS_TYPE_INT64, &utc, - DBUS_TYPE_BOOLEAN, &relative, - DBUS_TYPE_BOOLEAN, &interactive, - DBUS_TYPE_INVALID)) - return bus_send_error_reply(connection, message, &error, -EINVAL); - - if (!relative && utc <= 0) - return bus_send_error_reply(connection, message, NULL, -EINVAL); - - if (!relative || utc != 0) { - struct timespec ts; - struct tm* tm; - - r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-time", interactive, &error); - if (r < 0) - return bus_send_error_reply(connection, message, &error, r); - - if (relative) - timespec_store(&ts, now(CLOCK_REALTIME) + utc); - else - timespec_store(&ts, utc); - - /* Set system clock */ - if (clock_settime(CLOCK_REALTIME, &ts) < 0) { - log_error("Failed to set local time: %m"); - return bus_send_error_reply(connection, message, NULL, -errno); - } - - /* Sync down to RTC */ - if (local_rtc) - tm = localtime(&ts.tv_sec); - else - tm = gmtime(&ts.tv_sec); - - hwclock_set_time(tm); - - log_info("Changed local time to %s", ctime(&ts.tv_sec)); - } - } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetNTP")) { - dbus_bool_t ntp; - dbus_bool_t interactive; - - if (!dbus_message_get_args( - message, - &error, - DBUS_TYPE_BOOLEAN, &ntp, - DBUS_TYPE_BOOLEAN, &interactive, - DBUS_TYPE_INVALID)) - return bus_send_error_reply(connection, message, &error, -EINVAL); - - if (ntp != !!use_ntp) { - - r = verify_polkit(connection, message, "org.freedesktop.timedate1.set-ntp", interactive, &error); - if (r < 0) - return bus_send_error_reply(connection, message, &error, r); - - use_ntp = !!ntp; - - r = enable_ntp(connection, &error); - if (r < 0) - return bus_send_error_reply(connection, message, &error, r); - - r = start_ntp(connection, &error); - if (r < 0) - return bus_send_error_reply(connection, message, &error, r); - - log_info("Set NTP to %s", use_ntp ? "enabled" : "disabled"); - - changed = bus_properties_changed_new( - "/org/freedesktop/timedate1", - "org.freedesktop.timedate1", - "NTP\0"); - if (!changed) - goto oom; - } - - } else - return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties); - - if (!(reply = dbus_message_new_method_return(message))) - goto oom; - - if (!dbus_connection_send(connection, reply, NULL)) - goto oom; - - dbus_message_unref(reply); - reply = NULL; - - if (changed) { - - if (!dbus_connection_send(connection, changed, NULL)) - goto oom; - - dbus_message_unref(changed); - } - - return DBUS_HANDLER_RESULT_HANDLED; - -oom: - if (reply) - dbus_message_unref(reply); - - if (changed) - dbus_message_unref(changed); - - dbus_error_free(&error); - - return DBUS_HANDLER_RESULT_NEED_MEMORY; -} - -static int connect_bus(DBusConnection **_bus) { - static const DBusObjectPathVTable timedate_vtable = { - .message_function = timedate_message_handler - }; - DBusError error; - DBusConnection *bus = NULL; - int r; - - assert(_bus); - - dbus_error_init(&error); - - bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); - if (!bus) { - log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error)); - r = -ECONNREFUSED; - goto fail; - } - - dbus_connection_set_exit_on_disconnect(bus, FALSE); - - if (!dbus_connection_register_object_path(bus, "/org/freedesktop/timedate1", &timedate_vtable, NULL) || - !dbus_connection_add_filter(bus, bus_exit_idle_filter, &remain_until, NULL)) { - log_error("Not enough memory"); - r = -ENOMEM; - goto fail; - } - - r = dbus_bus_request_name(bus, "org.freedesktop.timedate1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error); - if (dbus_error_is_set(&error)) { - log_error("Failed to register name on bus: %s", bus_error_message(&error)); - r = -EEXIST; - goto fail; - } - - if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { - log_error("Failed to acquire name."); - r = -EEXIST; - goto fail; - } - - if (_bus) - *_bus = bus; - - return 0; - -fail: - dbus_connection_close(bus); - dbus_connection_unref(bus); - - dbus_error_free(&error); - - return r; -} - -int main(int argc, char *argv[]) { - int r; - DBusConnection *bus = NULL; - bool exiting = false; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - if (argc == 2 && streq(argv[1], "--introspect")) { - fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE - "\n", stdout); - fputs(timedate_interface, stdout); - fputs("\n", stdout); - return 0; - } - - if (argc != 1) { - log_error("This program takes no arguments."); - r = -EINVAL; - goto finish; - } - - r = read_data(); - if (r < 0) { - log_error("Failed to read timezone data: %s", strerror(-r)); - goto finish; - } - - r = connect_bus(&bus); - if (r < 0) - goto finish; - - r = read_ntp(bus); - if (r < 0) { - log_error("Failed to determine whether NTP is enabled: %s", strerror(-r)); - goto finish; - } - - remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC; - for (;;) { - - if (!dbus_connection_read_write_dispatch(bus, exiting ? -1 : (int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))) - break; - - if (!exiting && remain_until < now(CLOCK_MONOTONIC)) { - exiting = true; - bus_async_unregister_and_exit(bus, "org.freedesktop.hostname1"); - } - } - - r = 0; - -finish: - free_data(); - - if (bus) { - dbus_connection_flush(bus); - dbus_connection_close(bus); - dbus_connection_unref(bus); - } - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} -- cgit v1.2.3-54-g00ecf From d4a66a7f9638d4c642356d1fb9e38ac29766a451 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Sat, 31 Dec 2011 08:53:06 +0100 Subject: libsystemd-id128: restructure Makefile.am --- .gitignore | 1 - Makefile.am | 113 ++++++++++++++++++++++++++++--------------------- src/journal/.gitignore | 1 + 3 files changed, 66 insertions(+), 49 deletions(-) create mode 100644 src/journal/.gitignore (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 087ee2394a..b0dacaced0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ /systemd-rc-local-generator -/libsystemd-journal.pc /libsystemd-id128.pc systemd-journalctl systemd-journald diff --git a/Makefile.am b/Makefile.am index 35838f8a15..dea9d2b3cf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -207,12 +207,10 @@ systemgenerator_PROGRAMS += \ endif lib_LTLIBRARIES = \ - libsystemd-daemon.la \ - libsystemd-id128.la + libsystemd-daemon.la pkginclude_HEADERS = \ - src/sd-daemon.h \ - src/sd-id128.h + src/sd-daemon.h noinst_PROGRAMS = \ test-engine \ @@ -224,8 +222,7 @@ noinst_PROGRAMS = \ test-cgroup \ test-env-replace \ test-strv \ - test-install \ - test-id128 + test-install if HAVE_PAM pamlib_LTLIBRARIES = \ @@ -413,9 +410,7 @@ EXTRA_DIST += \ units/user@.service.in \ systemd.pc.in \ libsystemd-daemon.pc.in \ - libsystemd-id128.pc.in \ src/libsystemd-daemon.sym \ - src/libsystemd-id128.sym \ introspect.awk \ src/73-seat-late.rules.in \ src/99-systemd.rules.in \ @@ -493,8 +488,7 @@ pkgconfigdata_DATA = \ systemd.pc pkgconfiglib_DATA = \ - libsystemd-daemon.pc \ - libsystemd-id128.pc + libsystemd-daemon.pc # First passed through sed, followed by intltool polkitpolicy_in_in_files = \ @@ -855,16 +849,6 @@ test_install_CFLAGS = \ test_install_LDADD = \ libsystemd-basic.la -test_id128_SOURCES = \ - src/test-id128.c \ - src/sd-id128.c - -test_id128_CFLAGS = \ - $(AM_CFLAGS) - -test_id128_LDADD = \ - libsystemd-basic.la - systemd_stdout_syslog_bridge_SOURCES = \ src/stdout-syslog-bridge.c \ src/tcpwrap.c @@ -928,6 +912,67 @@ dist_pkgdata_DATA = \ dist_noinst_SCRIPT = \ src/generate-kbd-model-map +# ------------------------------------------------------------------------------ +libsystemd_id128_la_SOURCES = \ + src/sd-id128.c + +libsystemd_id128_la_CFLAGS = \ + $(AM_CFLAGS) \ + -fvisibility=hidden + +libsystemd_id128_la_LDFLAGS = \ + -shared \ + -version-info $(LIBSYSTEMD_ID128_CURRENT):$(LIBSYSTEMD_ID128_REVISION):$(LIBSYSTEMD_ID128_AGE) \ + -Wl,--version-script=$(top_srcdir)/src/libsystemd-id128.sym + +libsystemd_id128_la_LIBADD = \ + libsystemd-basic.la + +test_id128_SOURCES = \ + src/test-id128.c \ + src/sd-id128.c + +test_id128_CFLAGS = \ + $(AM_CFLAGS) + +test_id128_LDADD = \ + libsystemd-basic.la + +noinst_PROGRAMS += \ + test-id128 + +pkginclude_HEADERS += \ + src/sd-id128.h + +lib_LTLIBRARIES += \ + libsystemd-id128.la + +pkgconfiglib_DATA += \ + libsystemd-id128.pc + +# move lib from $(libdir) to $(rootlibdir) and update devel link, if needed +libsystemd-id128-install-hook: + if test "$(libdir)" != "$(rootlibdir)"; then \ + mkdir -p $(DESTDIR)$(rootlibdir) && \ + so_img_name=$$(readlink $(DESTDIR)$(libdir)/libsystemd-id128.so) && \ + so_img_rel_target_prefix=$$(echo $(libdir) | sed 's,\(^/\|\)[^/][^/]*,..,g') && \ + ln -sf $$so_img_rel_target_prefix$(rootlibdir)/$$so_img_name $(DESTDIR)$(libdir)/libsystemd-id128.so && \ + mv $(DESTDIR)$(libdir)/libsystemd-id128.so.* $(DESTDIR)$(rootlibdir); \ + fi + +INSTALL_EXEC_HOOKS += \ + libsystemd-id128-install-hook + +libsystemd-id128-uninstall-hook: + rm -f $(DESTDIR)$(rootlibdir)/libsystemd-id128.so* + +UNINSTALL_EXEC_HOOKS += \ + libsystemd-id128-uninstall-hook + +EXTRA_DIST += \ + libsystemd-id128.pc.in \ + src/libsystemd-id128.sym + # ------------------------------------------------------------------------------ systemd_journald_SOURCES = \ src/journal/journald.c \ @@ -1887,34 +1932,6 @@ libsystemd-daemon-install-hook: libsystemd-daemon-uninstall-hook: rm -f $(DESTDIR)$(rootlibdir)/libsystemd-daemon.so* -libsystemd_id128_la_SOURCES = \ - src/sd-id128.c - -libsystemd_id128_la_CFLAGS = \ - $(AM_CFLAGS) \ - -fvisibility=hidden - -libsystemd_id128_la_LDFLAGS = \ - -shared \ - -version-info $(LIBSYSTEMD_ID128_CURRENT):$(LIBSYSTEMD_ID128_REVISION):$(LIBSYSTEMD_ID128_AGE) \ - -Wl,--version-script=$(top_srcdir)/src/libsystemd-id128.sym - -libsystemd_id128_la_LIBADD = \ - libsystemd-basic.la - -# move lib from $(libdir) to $(rootlibdir) and update devel link, if needed -libsystemd-id128-install-hook: - if test "$(libdir)" != "$(rootlibdir)"; then \ - mkdir -p $(DESTDIR)$(rootlibdir) && \ - so_img_name=$$(readlink $(DESTDIR)$(libdir)/libsystemd-id128.so) && \ - so_img_rel_target_prefix=$$(echo $(libdir) | sed 's,\(^/\|\)[^/][^/]*,..,g') && \ - ln -sf $$so_img_rel_target_prefix$(rootlibdir)/$$so_img_name $(DESTDIR)$(libdir)/libsystemd-id128.so && \ - mv $(DESTDIR)$(libdir)/libsystemd-id128.so.* $(DESTDIR)$(rootlibdir); \ - fi - -libsystemd-id128-uninstall-hook: - rm -f $(DESTDIR)$(rootlibdir)/libsystemd-id128.so* - SED_PROCESS = \ $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \ $(SED) -e 's,@rootlibexecdir\@,$(rootlibexecdir),g' \ diff --git a/src/journal/.gitignore b/src/journal/.gitignore new file mode 100644 index 0000000000..faf48e7dc3 --- /dev/null +++ b/src/journal/.gitignore @@ -0,0 +1 @@ +libsystemd-journal.pc -- cgit v1.2.3-54-g00ecf From dcd5f1020c36cddb9e250c01df057b9b5e5447b0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 5 Jan 2012 16:28:33 +0100 Subject: git: only ignore toplevel systemd file --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index b0dacaced0..8daf8f0a8e 100644 --- a/.gitignore +++ b/.gitignore @@ -61,7 +61,7 @@ test-ns test-loopback systemd-cgroups-agent systemd-initctl -systemd +/systemd test-engine test-job-type systemd-stdout-syslog-bridge -- cgit v1.2.3-54-g00ecf From 755a02c6800246e7e293897d0594fe7e7531ba59 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 13 Jan 2012 23:17:54 +0100 Subject: journal: add new system-cat tool as kind of a more powerfull BSD logger --- .gitignore | 1 + Makefile.am | 10 +++ src/journal/cat.c | 181 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 192 insertions(+) create mode 100644 src/journal/cat.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 8daf8f0a8e..c260210311 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/systemd-cat /systemd-rc-local-generator /libsystemd-id128.pc systemd-journalctl diff --git a/Makefile.am b/Makefile.am index 42da4fb753..d94de56ff1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1237,6 +1237,13 @@ systemd_journald_LDADD += \ $(XZ_LIBS) endif +systemd_cat_SOURCES = \ + src/journal/cat.c + +systemd_cat_LDADD = \ + libsystemd-basic.la \ + libsystemd-journal.la + systemd_journalctl_SOURCES = \ src/journal/journalctl.c \ src/pager.c \ @@ -1345,6 +1352,9 @@ rootlibexec_PROGRAMS += \ rootbin_PROGRAMS += \ systemd-journalctl +bin_PROGRAMS += \ + systemd-cat + dist_systemunit_DATA += \ units/systemd-journald.socket diff --git a/src/journal/cat.c b/src/journal/cat.c new file mode 100644 index 0000000000..6745f1ce0a --- /dev/null +++ b/src/journal/cat.c @@ -0,0 +1,181 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "util.h" +#include "build.h" + +static char *arg_identifier = NULL; +static char arg_priority = LOG_INFO; +static bool arg_level_prefix = true; + +static int help(void) { + + printf("%s [OPTIONS...] {COMMAND} ...\n\n" + "Execute process with stdout/stderr connected to the journal.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " -t --identifier=STRING Set syslog identifier\n" + " -p --priority=PRIORITY Set priority value (0..7)\n" + " --level-prefix=BOOL Control whether level prefix shall be parsed\n", + program_invocation_short_name); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_LEVEL_PREFIX + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version" , no_argument, NULL, ARG_VERSION }, + { "identifier", required_argument, NULL, 't' }, + { "priority", required_argument, NULL, 'p' }, + { "level-prefix", required_argument, NULL, ARG_LEVEL_PREFIX }, + { NULL, 0, NULL, 0 } + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "+ht:p:", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + puts(PACKAGE_STRING); + puts(DISTRIBUTION); + puts(SYSTEMD_FEATURES); + return 0; + + case 't': + free(arg_identifier); + if (isempty(optarg)) + arg_identifier = NULL; + else { + arg_identifier = strdup(optarg); + if (!arg_identifier) { + log_error("Out of memory."); + return -ENOMEM; + } + } + break; + + case 'p': + arg_priority = log_level_from_string(optarg); + if (arg_priority < 0) { + log_error("Failed to parse priority value."); + return arg_priority; + } + break; + + case ARG_LEVEL_PREFIX: { + int k; + + k = parse_boolean(optarg); + if (k < 0) { + log_error("Failed to parse level prefix value."); + return k; + } + arg_level_prefix = k; + break; + } + + default: + log_error("Unknown option code %c", c); + return -EINVAL; + } + } + + return 1; +} + +int main(int argc, char *argv[]) { + int r, fd = -1, saved_stderr = -1; + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + fd = sd_journal_stream_fd(arg_identifier, arg_priority, arg_level_prefix); + if (fd < 0) { + log_error("Failed to create stream fd: %s", strerror(fd)); + r = fd; + goto finish; + } + + saved_stderr = fcntl(STDERR_FILENO, F_DUPFD_CLOEXEC, 3); + + if (dup3(fd, STDOUT_FILENO, 0) < 0 || + dup3(fd, STDERR_FILENO, 0) < 0) { + log_error("Failed to duplicate fd: %s", strerror(fd)); + r = -errno; + goto finish; + } + + if (fd >= 3) + close_nointr_nofail(fd); + + fd = -1; + + if (argc <= optind) + execl("/bin/cat", "/bin/cat", NULL); + else + execvp(argv[optind], argv + optind); + + /* Let's try to restore a working stderr, so we can print the error message */ + if (saved_stderr >= 0) + dup3(saved_stderr, STDERR_FILENO, 0); + + log_error("Failed to execute process: %m"); + r = -errno; + +finish: + if (fd >= 0) + close_nointr_nofail(fd); + + if (saved_stderr >= 0) + close_nointr_nofail(saved_stderr); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} -- cgit v1.2.3-54-g00ecf From f5e04665ebf7124f3ea17dcf258793ed73a95fe1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 14 Jan 2012 00:37:35 +0100 Subject: journal: hook up coredumping with journal --- .gitignore | 1 + Makefile.am | 25 +++++++ configure.ac | 8 +++ src/.gitignore | 1 + src/journal/coredump.c | 173 ++++++++++++++++++++++++++++++++++++++++++++++ src/systemd/sd-messages.h | 2 + sysctl.d/.gitignore | 1 + sysctl.d/Makefile | 1 + sysctl.d/coredump.conf.in | 10 +++ 9 files changed, 222 insertions(+) create mode 100644 src/journal/coredump.c create mode 100644 sysctl.d/.gitignore create mode 120000 sysctl.d/Makefile create mode 100644 sysctl.d/coredump.conf.in (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index c260210311..011aecec95 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/systemd-coredump /systemd-cat /systemd-rc-local-generator /libsystemd-id128.pc diff --git a/Makefile.am b/Makefile.am index d94de56ff1..607ae7eadd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -52,6 +52,7 @@ bashcompletiondir=$(sysconfdir)/bash_completion.d pkgsysconfdir=$(sysconfdir)/systemd userunitdir=$(prefix)/lib/systemd/user tmpfilesdir=$(prefix)/lib/tmpfiles.d +sysctldir=$(prefix)/lib/sysctl.d usergeneratordir=$(pkglibexecdir)/user-generators pkgincludedir=$(includedir)/systemd @@ -1397,6 +1398,27 @@ EXTRA_DIST += \ CLEANFILES += \ src/journal/journald-gperf.c +if ENABLE_COREDUMP + +systemd_coredump_SOURCES = \ + src/journal/coredump.c + +systemd_coredump_LDADD = \ + libsystemd-basic.la \ + libsystemd-journal.la \ + libsystemd-login.la + +rootlibexec_PROGRAMS += \ + systemd-coredump + +sysctl_DATA = \ + sysctl.d/coredump.conf + +EXTRA_DIST += \ + sysctl.d/coredump.conf.in + +endif + # ------------------------------------------------------------------------------ if ENABLE_BINFMT systemd_binfmt_SOURCES = \ @@ -2074,6 +2096,9 @@ units/%: units/%.in Makefile man/%: man/%.in Makefile $(SED_PROCESS) +sysctl.d/%: sysctl.d/%.in Makefile + $(SED_PROCESS) + %.pc: %.pc.in Makefile $(SED_PROCESS) diff --git a/configure.ac b/configure.ac index 80d0156916..194caa5d5f 100644 --- a/configure.ac +++ b/configure.ac @@ -372,6 +372,13 @@ if test "x$enable_localed" != "xno"; then fi AM_CONDITIONAL(ENABLE_LOCALED, [test "$have_localed" = "yes"]) +have_coredump=no +AC_ARG_ENABLE(coredump, AS_HELP_STRING([--disable-coredump], [disable coredump hook])) +if test "x$enable_coredump" != "xno"; then + have_coredump=yes +fi +AM_CONDITIONAL(ENABLE_COREDUMP, [test "$have_coredump" = "yes"]) + have_gtk=no AC_ARG_ENABLE(gtk, AS_HELP_STRING([--disable-gtk], [disable GTK tools])) if test "x$enable_gtk" != "xno"; then @@ -646,6 +653,7 @@ AC_MSG_RESULT([ hostnamed: ${have_hostnamed} timedated: ${have_timedated} localed: ${have_localed} + coredump: ${have_coredump} plymouth: ${have_plymouth} prefix: ${prefix} rootprefix: ${with_rootprefix} diff --git a/src/.gitignore b/src/.gitignore index ff2737b761..c54c6f6514 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,3 +1,4 @@ +/*.pc load-fragment-gperf-nulstr.c load-fragment-gperf.c load-fragment-gperf.gperf diff --git a/src/journal/coredump.c b/src/journal/coredump.c new file mode 100644 index 0000000000..f160270168 --- /dev/null +++ b/src/journal/coredump.c @@ -0,0 +1,173 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include +#include + +#include "log.h" +#include "util.h" + +#define COREDUMP_MAX (64*1024) + +enum { + ARG_PID = 1, + ARG_UID, + ARG_GID, + ARG_SIGNAL, + ARG_TIMESTAMP, + ARG_COMM, + _ARG_MAX +}; + +int main(int argc, char* argv[]) { + int r, j = 0; + char *p = NULL; + ssize_t n; + pid_t pid; + struct iovec iovec[14]; + char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL, + *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL, + *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *t; + + log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); + log_parse_environment(); + log_open(); + + if (argc != _ARG_MAX) { + log_error("Invalid number of arguments passed from kernel."); + r = -EINVAL; + goto finish; + } + + r = parse_pid(argv[ARG_PID], &pid); + if (r < 0) { + log_error("Failed to parse PID."); + r = -EINVAL; + goto finish; + } + + p = malloc(9 + COREDUMP_MAX); + if (!p) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + memcpy(p, "COREDUMP=", 9); + + n = loop_read(STDIN_FILENO, p + 9, COREDUMP_MAX, false); + if (n < 0) { + log_error("Failed to read core dump data: %s", strerror(-n)); + r = (int) n; + goto finish; + } + + zero(iovec); + iovec[j].iov_base = p; + iovec[j].iov_len = 9 + n; + j++; + + core_pid = strappend("COREDUMP_PID=", argv[ARG_PID]); + if (core_pid) + IOVEC_SET_STRING(iovec[j++], core_pid); + + core_uid = strappend("COREDUMP_UID=", argv[ARG_UID]); + if (core_uid) + IOVEC_SET_STRING(iovec[j++], core_uid); + + core_gid = strappend("COREDUMP_GID=", argv[ARG_GID]); + if (core_gid) + IOVEC_SET_STRING(iovec[j++], core_gid); + + core_signal = strappend("COREDUMP_SIGNAL=", argv[ARG_SIGNAL]); + if (core_signal) + IOVEC_SET_STRING(iovec[j++], core_signal); + + core_comm = strappend("COREDUMP_COMM=", argv[ARG_COMM]); + if (core_comm) + IOVEC_SET_STRING(iovec[j++], core_comm); + + if (sd_pid_get_session(pid, &t) >= 0) { + core_session = strappend("COREDUMP_SESSION=", t); + free(t); + + if (core_session) + IOVEC_SET_STRING(iovec[j++], core_session); + } + + if (sd_pid_get_unit(pid, &t) >= 0) { + core_unit = strappend("COREDUMP_UNIT=", t); + free(t); + + if (core_unit) + IOVEC_SET_STRING(iovec[j++], core_unit); + } + + if (get_process_exe(pid, &t) >= 0) { + core_exe = strappend("COREDUMP_EXE=", t); + free(t); + + if (core_exe) + IOVEC_SET_STRING(iovec[j++], core_exe); + } + + if (get_process_cmdline(pid, LINE_MAX, false, &t) >= 0) { + core_cmdline = strappend("COREDUMP_CMDLINE=", t); + free(t); + + if (core_cmdline) + IOVEC_SET_STRING(iovec[j++], core_cmdline); + } + + core_timestamp = join("COREDUMP_TIMESTAMP=", argv[ARG_TIMESTAMP], "000000", NULL); + if (core_timestamp) + IOVEC_SET_STRING(iovec[j++], core_timestamp); + + IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1"); + IOVEC_SET_STRING(iovec[j++], "PRIORITY=2"); + + core_message = join("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") dumped core.", NULL); + if (core_message) + IOVEC_SET_STRING(iovec[j++], core_message); + + r = sd_journal_sendv(iovec, j); + if (r < 0) + log_error("Failed to send coredump: %s", strerror(-r)); + +finish: + free(p); + free(core_pid); + free(core_uid); + free(core_gid); + free(core_signal); + free(core_timestamp); + free(core_comm); + free(core_exe); + free(core_cmdline); + free(core_unit); + free(core_session); + free(core_message); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h index 5fd1aa7e31..c5ac3abd0c 100644 --- a/src/systemd/sd-messages.h +++ b/src/systemd/sd-messages.h @@ -32,6 +32,8 @@ extern "C" { #define SD_MESSAGE_JOURNAL_STOP SD_ID128_MAKE(d9,3f,b3,c9,c2,4d,45,1a,97,ce,a6,15,ce,59,c0,0b) #define SD_MESSAGE_JOURNAL_DROPPED SD_ID128_MAKE(a5,96,d6,fe,7b,fa,49,94,82,8e,72,30,9e,95,d6,1e) +#define SD_MESSAGE_COREDUMP SD_ID128_MAKE(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1) + #ifdef __cplusplus } #endif diff --git a/sysctl.d/.gitignore b/sysctl.d/.gitignore new file mode 100644 index 0000000000..7563539ab0 --- /dev/null +++ b/sysctl.d/.gitignore @@ -0,0 +1 @@ +/coredump.conf diff --git a/sysctl.d/Makefile b/sysctl.d/Makefile new file mode 120000 index 0000000000..bd1047548b --- /dev/null +++ b/sysctl.d/Makefile @@ -0,0 +1 @@ +../src/Makefile \ No newline at end of file diff --git a/sysctl.d/coredump.conf.in b/sysctl.d/coredump.conf.in new file mode 100644 index 0000000000..ab19b1e988 --- /dev/null +++ b/sysctl.d/coredump.conf.in @@ -0,0 +1,10 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# See sysctl.d(5) for details + +kernel.core_pattern=|@rootlibexecdir@/systemd-coredump %p %u %g %s %t %e -- cgit v1.2.3-54-g00ecf From 8f2d43a0121bc9a57ba8b79b33d5ac87d36ca2f2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 22 Jan 2012 18:21:15 +0100 Subject: cgtop: add new cgtop tool --- .gitignore | 1 + Makefile.am | 8 + src/cgtop.c | 729 +++++++++++++++++++++++++++++++++++++++++++++++ src/journal/journalctl.c | 2 +- src/logs-show.c | 2 +- src/util.c | 55 +++- src/util.h | 7 +- 7 files changed, 795 insertions(+), 9 deletions(-) create mode 100644 src/cgtop.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 011aecec95..d27424ba9a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/systemd-cgtop /systemd-coredump /systemd-cat /systemd-rc-local-generator diff --git a/Makefile.am b/Makefile.am index 110a2f4df3..be5e70c9f8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -169,6 +169,7 @@ rootbin_PROGRAMS = \ bin_PROGRAMS = \ systemd-cgls \ + systemd-cgtop \ systemd-stdio-bridge \ systemd-nspawn @@ -1008,6 +1009,13 @@ systemd_cgls_SOURCES = \ systemd_cgls_LDADD = \ libsystemd-basic.la +systemd_cgtop_SOURCES = \ + src/cgtop.c \ + src/cgroup-util.c + +systemd_cgtop_LDADD = \ + libsystemd-basic.la + systemd_nspawn_SOURCES = \ src/nspawn.c \ src/cgroup-util.c \ diff --git a/src/cgtop.c b/src/cgtop.c new file mode 100644 index 0000000000..4624bc4af5 --- /dev/null +++ b/src/cgtop.c @@ -0,0 +1,729 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "hashmap.h" +#include "cgroup-util.h" + +typedef struct Group { + char *path; + + bool n_tasks_valid:1; + bool cpu_valid:1; + bool memory_valid:1; + bool io_valid:1; + + unsigned n_tasks; + + unsigned cpu_iteration; + uint64_t cpu_usage; + struct timespec cpu_timestamp; + double cpu_fraction; + + uint64_t memory; + + unsigned io_iteration; + uint64_t io_input, io_output; + struct timespec io_timestamp; + uint64_t io_input_bps, io_output_bps; +} Group; + +static unsigned arg_depth = 2; +static usec_t arg_delay = 1*USEC_PER_SEC; + +static enum { + ORDER_PATH, + ORDER_TASKS, + ORDER_CPU, + ORDER_MEMORY, + ORDER_IO +} arg_order = ORDER_CPU; + +static void group_free(Group *g) { + assert(g); + + free(g->path); + free(g); +} + +static void group_hashmap_clear(Hashmap *h) { + Group *g; + + while ((g = hashmap_steal_first(h))) + group_free(g); +} + +static void group_hashmap_free(Hashmap *h) { + group_hashmap_clear(h); + hashmap_free(h); +} + +static int process(const char *controller, const char *path, Hashmap *a, Hashmap *b, unsigned iteration) { + Group *g; + int r; + FILE *f; + pid_t pid; + unsigned n; + + assert(controller); + assert(path); + assert(a); + + g = hashmap_get(a, path); + if (!g) { + g = hashmap_get(b, path); + if (!g) { + g = new0(Group, 1); + if (!g) + return -ENOMEM; + + g->path = strdup(path); + if (!g->path) { + group_free(g); + return -ENOMEM; + } + + r = hashmap_put(a, g->path, g); + if (r < 0) { + group_free(g); + return r; + } + } else { + assert_se(hashmap_move_one(a, b, path) == 0); + g->cpu_valid = g->memory_valid = g->io_valid = g->n_tasks_valid = false; + } + } + + /* Regardless which controller, let's find the maximum number + * of processes in any of it */ + + r = cg_enumerate_tasks(controller, path, &f); + if (r < 0) + return r; + + n = 0; + while (cg_read_pid(f, &pid) > 0) + n++; + fclose(f); + + if (n > 0) { + if (g->n_tasks_valid) + g->n_tasks = MAX(g->n_tasks, n); + else + g->n_tasks = n; + + g->n_tasks_valid = true; + } + + if (streq(controller, "cpuacct")) { + uint64_t new_usage; + char *p, *v; + struct timespec ts; + + r = cg_get_path(controller, path, "cpuacct.usage", &p); + if (r < 0) + return r; + + r = read_one_line_file(p, &v); + free(p); + if (r < 0) + return r; + + r = safe_atou64(v, &new_usage); + free(v); + if (r < 0) + return r; + + assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); + + if (g->cpu_iteration == iteration - 1) { + uint64_t x, y; + + x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) - + ((uint64_t) g->cpu_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->cpu_timestamp.tv_nsec); + + y = new_usage - g->cpu_usage; + + if (y > 0) { + g->cpu_fraction = (double) y / (double) x; + g->cpu_valid = true; + } + } + + g->cpu_usage = new_usage; + g->cpu_timestamp = ts; + g->cpu_iteration = iteration; + + } else if (streq(controller, "memory")) { + char *p, *v; + + r = cg_get_path(controller, path, "memory.usage_in_bytes", &p); + if (r < 0) + return r; + + r = read_one_line_file(p, &v); + free(p); + if (r < 0) + return r; + + r = safe_atou64(v, &g->memory); + free(v); + if (r < 0) + return r; + + if (g->memory > 0) + g->memory_valid = true; + + } else if (streq(controller, "blkio")) { + char *p; + uint64_t wr = 0, rd = 0; + struct timespec ts; + + r = cg_get_path(controller, path, "blkio.io_service_bytes", &p); + if (r < 0) + return r; + + f = fopen(p, "re"); + free(p); + + if (!f) + return -errno; + + for (;;) { + char line[LINE_MAX], *l; + uint64_t k, *q; + + if (!fgets(line, sizeof(line), f)) + break; + + l = strstrip(line); + l += strcspn(l, WHITESPACE); + l += strspn(l, WHITESPACE); + + if (first_word(l, "Read")) { + l += 4; + q = &rd; + } else if (first_word(l, "Write")) { + l += 5; + q = ≀ + } else + continue; + + l += strspn(l, WHITESPACE); + r = safe_atou64(l, &k); + if (r < 0) + continue; + + *q += k; + } + + fclose(f); + + assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); + + if (g->io_iteration == iteration - 1) { + uint64_t x, yr, yw; + + x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) - + ((uint64_t) g->io_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->io_timestamp.tv_nsec); + + yr = rd - g->io_input; + yw = wr - g->io_output; + + if (yr > 0 || yw > 0) { + g->io_input_bps = (yr * 1000000000ULL) / x; + g->io_output_bps = (yw * 1000000000ULL) / x; + g->io_valid = true; + + } + } + + g->io_input = rd; + g->io_output = wr; + g->io_timestamp = ts; + g->io_iteration = iteration; + } + + return 0; +} + +static int refresh_one( + const char *controller, + const char *path, + Hashmap *a, + Hashmap *b, + unsigned iteration, + unsigned depth) { + + DIR *d = NULL; + int r; + + assert(controller); + assert(path); + assert(a); + + if (depth > arg_depth) + return 0; + + r = process(controller, path, a, b, iteration); + if (r < 0) + return r; + + r = cg_enumerate_subgroups(controller, path, &d); + if (r < 0) { + if (r == ENOENT) + return 0; + + return r; + } + + for (;;) { + char *fn, *p; + + r = cg_read_subgroup(d, &fn); + if (r <= 0) + goto finish; + + p = join(path, "/", fn, NULL); + free(fn); + + if (!p) { + r = -ENOMEM; + goto finish; + } + + path_kill_slashes(p); + + r = refresh_one(controller, p, a, b, iteration, depth + 1); + free(p); + + if (r < 0) + goto finish; + } + +finish: + if (d) + closedir(d); + + return r; +} + +static int refresh(Hashmap *a, Hashmap *b, unsigned iteration) { + int r; + + assert(a); + + r = refresh_one("name=systemd", "/", a, b, iteration, 0); + if (r < 0) + return r; + + r = refresh_one("cpuacct", "/", a, b, iteration, 0); + if (r < 0) + return r; + + r = refresh_one("memory", "/", a, b, iteration, 0); + if (r < 0) + return r; + + return refresh_one("blkio", "/", a, b, iteration, 0); +} + +static int group_compare(const void*a, const void *b) { + const Group *x = *(Group**)a, *y = *(Group**)b; + + if (path_startswith(y->path, x->path)) + return -1; + if (path_startswith(x->path, y->path)) + return 1; + + if (arg_order == ORDER_CPU) { + if (x->cpu_valid && y->cpu_valid) { + + if (x->cpu_fraction > y->cpu_fraction) + return -1; + else if (x->cpu_fraction < y->cpu_fraction) + return 1; + } else if (x->cpu_valid) + return -1; + else if (y->cpu_valid) + return 1; + } + + if (arg_order == ORDER_TASKS) { + + if (x->n_tasks_valid && y->n_tasks_valid) { + if (x->n_tasks > y->n_tasks) + return -1; + else if (x->n_tasks < y->n_tasks) + return 1; + } else if (x->n_tasks_valid) + return -1; + else if (y->n_tasks_valid) + return 1; + } + + if (arg_order == ORDER_MEMORY) { + if (x->memory_valid && y->memory_valid) { + if (x->memory > y->memory) + return -1; + else if (x->memory < y->memory) + return 1; + } else if (x->memory_valid) + return -1; + else if (y->memory_valid) + return 1; + } + + if (arg_order == ORDER_IO) { + if (x->io_valid && y->io_valid) { + if (x->io_input_bps + x->io_output_bps > y->io_input_bps + y->io_output_bps) + return -1; + else if (x->io_input_bps + x->io_output_bps < y->io_input_bps + y->io_output_bps) + return 1; + } else if (x->io_valid) + return -1; + else if (y->io_valid) + return 1; + } + + return strcmp(x->path, y->path); +} + +static int display(Hashmap *a) { + Iterator i; + Group *g; + Group **array; + unsigned rows, n = 0, j; + + assert(a); + + /* Set cursor to top left corner and clear screen */ + fputs("\033[H" + "\033[2J", stdout); + + array = alloca(sizeof(Group*) * hashmap_size(a)); + + HASHMAP_FOREACH(g, a, i) + if (g->n_tasks_valid || g->cpu_valid || g->memory_valid || g->io_valid) + array[n++] = g; + + qsort(array, n, sizeof(Group*), group_compare); + + rows = fd_lines(STDOUT_FILENO); + if (rows <= 0) + rows = 25; + + printf("%s%-37s%s %s%7s%s %s%6s%s %s%8s%s %s%8s%s %s%8s%s\n\n", + arg_order == ORDER_PATH ? ANSI_HIGHLIGHT_ON : "", "Path", arg_order == ORDER_PATH ? ANSI_HIGHLIGHT_OFF : "", + arg_order == ORDER_TASKS ? ANSI_HIGHLIGHT_ON : "", "Tasks", arg_order == ORDER_TASKS ? ANSI_HIGHLIGHT_OFF : "", + arg_order == ORDER_CPU ? ANSI_HIGHLIGHT_ON : "", "%CPU", arg_order == ORDER_CPU ? ANSI_HIGHLIGHT_OFF : "", + arg_order == ORDER_MEMORY ? ANSI_HIGHLIGHT_ON : "", "Memory", arg_order == ORDER_MEMORY ? ANSI_HIGHLIGHT_OFF : "", + arg_order == ORDER_IO ? ANSI_HIGHLIGHT_ON : "", "Input/s", arg_order == ORDER_IO ? ANSI_HIGHLIGHT_OFF : "", + arg_order == ORDER_IO ? ANSI_HIGHLIGHT_ON : "", "Output/s", arg_order == ORDER_IO ? ANSI_HIGHLIGHT_OFF : ""); + + for (j = 0; j < n; j++) { + char *p; + char m[FORMAT_BYTES_MAX]; + + if (j + 5 > rows) + break; + + g = array[j]; + + p = ellipsize(g->path, 37, 33); + printf("%-37s", p ? p : g->path); + free(p); + + if (g->n_tasks_valid) + printf(" %7u", g->n_tasks); + else + fputs(" -", stdout); + + if (g->cpu_valid) + printf(" %6.1f", g->cpu_fraction*100); + else + fputs(" -", stdout); + + if (g->memory_valid) + printf(" %8s", format_bytes(m, sizeof(m), g->memory)); + else + fputs(" -", stdout); + + if (g->io_valid) { + printf(" %8s", + format_bytes(m, sizeof(m), g->io_input_bps)); + printf(" %8s", + format_bytes(m, sizeof(m), g->io_output_bps)); + } else + fputs(" - -", stdout); + + putchar('\n'); + } + + return 0; +} + +static void help(void) { + + printf("%s [OPTIONS...]\n\n" + "Show top control groups by their resource usage.\n\n" + " -h --help Show this help\n" + " -p Order by path\n" + " -t Order by number of tasks\n" + " -c Order by CPU load\n" + " -m Order by memory load\n" + " -i Order by IO load\n" + " -d --delay=DELAY Specify delay\n" + " --depth=DEPTH Maximum traversal depth\n", + program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_DEPTH = 0x100 + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "delay", required_argument, NULL, 'd' }, + { "depth", required_argument, NULL, ARG_DEPTH }, + { NULL, 0, NULL, 0 } + }; + + int c; + int r; + + assert(argc >= 1); + assert(argv); + + while ((c = getopt_long(argc, argv, "hptcmid:", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_DEPTH: + r = safe_atou(optarg, &arg_depth); + if (r < 0) { + log_error("Failed to parse depth parameter."); + return -EINVAL; + } + + break; + + case 'd': + r = parse_usec(optarg, &arg_delay); + if (r < 0 || arg_delay <= 0) { + log_error("Failed to parse delay parameter."); + return -EINVAL; + } + + break; + + case 'p': + arg_order = ORDER_PATH; + break; + + case 't': + arg_order = ORDER_TASKS; + break; + + case 'c': + arg_order = ORDER_CPU; + break; + + case 'm': + arg_order = ORDER_MEMORY; + break; + + case 'i': + arg_order = ORDER_IO; + break; + + case '?': + return -EINVAL; + + default: + log_error("Unknown option code %c", c); + return -EINVAL; + } + } + + if (optind < argc) { + log_error("Too many arguments."); + return -EINVAL; + } + + return 1; +} + +int main(int argc, char *argv[]) { + int r; + Hashmap *a = NULL, *b = NULL; + unsigned iteration = 0; + usec_t last_refresh = 0; + bool quit = false, immediate_refresh = false; + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + a = hashmap_new(string_hash_func, string_compare_func); + b = hashmap_new(string_hash_func, string_compare_func); + if (!a || !b) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + while (!quit) { + Hashmap *c; + usec_t t; + char key; + char h[FORMAT_TIMESPAN_MAX]; + + t = now(CLOCK_MONOTONIC); + + if (t >= last_refresh + arg_delay || immediate_refresh) { + + r = refresh(a, b, iteration++); + if (r < 0) + goto finish; + + group_hashmap_clear(b); + + c = a; + a = b; + b = c; + + last_refresh = t; + immediate_refresh = false; + } + + r = display(b); + if (r < 0) + goto finish; + + r = read_one_char(stdin, &key, last_refresh + arg_delay - t, NULL); + if (r == -ETIMEDOUT) + continue; + if (r < 0) { + log_error("Couldn't read key: %s", strerror(-r)); + goto finish; + } + + fputs("\r \r", stdout); + fflush(stdout); + + switch (key) { + + case ' ': + immediate_refresh = true; + break; + + case 'q': + quit = true; + break; + + case 'p': + arg_order = ORDER_PATH; + break; + + case 't': + arg_order = ORDER_TASKS; + break; + + case 'c': + arg_order = ORDER_CPU; + break; + + case 'm': + arg_order = ORDER_MEMORY; + break; + + case 'i': + arg_order = ORDER_IO; + break; + + case '+': + if (arg_delay < USEC_PER_SEC) + arg_delay += USEC_PER_MSEC*250; + else + arg_delay += USEC_PER_SEC; + + fprintf(stdout, "\nIncreased delay to %s.", format_timespan(h, sizeof(h), arg_delay)); + fflush(stdout); + sleep(1); + break; + + case '-': + if (arg_delay <= USEC_PER_MSEC*500) + arg_delay = USEC_PER_MSEC*250; + else if (arg_delay < USEC_PER_MSEC*1250) + arg_delay -= USEC_PER_MSEC*250; + else + arg_delay -= USEC_PER_SEC; + + fprintf(stdout, "\nDecreased delay to %s.", format_timespan(h, sizeof(h), arg_delay)); + fflush(stdout); + sleep(1); + break; + + case '?': + case 'h': + fprintf(stdout, + "\t<" ANSI_HIGHLIGHT_ON "P" ANSI_HIGHLIGHT_OFF "> By path; <" ANSI_HIGHLIGHT_ON "T" ANSI_HIGHLIGHT_OFF "> By tasks; <" ANSI_HIGHLIGHT_ON "C" ANSI_HIGHLIGHT_OFF "> By CPU; <" ANSI_HIGHLIGHT_ON "M" ANSI_HIGHLIGHT_OFF "> By memory; <" ANSI_HIGHLIGHT_ON "I" ANSI_HIGHLIGHT_OFF "> By I/O\n" + "\t<" ANSI_HIGHLIGHT_ON "Q" ANSI_HIGHLIGHT_OFF "> Quit; <" ANSI_HIGHLIGHT_ON "+" ANSI_HIGHLIGHT_OFF "> Increase delay; <" ANSI_HIGHLIGHT_ON "-" ANSI_HIGHLIGHT_OFF "> Decrease delay; <" ANSI_HIGHLIGHT_ON "SPACE" ANSI_HIGHLIGHT_OFF "> Refresh"); + fflush(stdout); + sleep(3); + break; + + default: + fprintf(stdout, "\nUnknown key '%c'. Ignoring.", key); + fflush(stdout); + sleep(1); + break; + } + } + + log_info("Exiting."); + + r = 0; + +finish: + group_hashmap_free(a); + group_hashmap_free(b); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 52db7a9365..8db3fc9201 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -282,7 +282,7 @@ int main(int argc, char *argv[]) { if (!arg_follow) break; - r = fd_wait_for_event(fd, POLLIN); + r = fd_wait_for_event(fd, POLLIN, (usec_t) -1); if (r < 0) { log_error("Couldn't wait for event: %s", strerror(-r)); goto finish; diff --git a/src/logs-show.c b/src/logs-show.c index 5a00133140..f023f0aaef 100644 --- a/src/logs-show.c +++ b/src/logs-show.c @@ -633,7 +633,7 @@ int show_journal_by_unit( if (!follow) break; - r = fd_wait_for_event(fd, POLLIN); + r = fd_wait_for_event(fd, POLLIN, (usec_t) -1); if (r < 0) goto finish; diff --git a/src/util.c b/src/util.c index ef6eea4b9f..1babb6aed4 100644 --- a/src/util.c +++ b/src/util.c @@ -2434,7 +2434,7 @@ fail: return r; } -int read_one_char(FILE *f, char *ret, bool *need_nl) { +int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) { struct termios old_termios, new_termios; char c; char line[LINE_MAX]; @@ -2452,6 +2452,13 @@ int read_one_char(FILE *f, char *ret, bool *need_nl) { if (tcsetattr(fileno(f), TCSADRAIN, &new_termios) >= 0) { size_t k; + if (t != (usec_t) -1) { + if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) { + tcsetattr(fileno(f), TCSADRAIN, &old_termios); + return -ETIMEDOUT; + } + } + k = fread(&c, 1, 1, f); tcsetattr(fileno(f), TCSADRAIN, &old_termios); @@ -2467,7 +2474,11 @@ int read_one_char(FILE *f, char *ret, bool *need_nl) { } } - if (!(fgets(line, sizeof(line), f))) + if (t != (usec_t) -1) + if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) + return -ETIMEDOUT; + + if (!fgets(line, sizeof(line), f)) return -EIO; truncate_nl(line); @@ -2509,7 +2520,8 @@ int ask(char *ret, const char *replies, const char *text, ...) { fflush(stdout); - if ((r = read_one_char(stdin, &c, &need_nl)) < 0) { + r = read_one_char(stdin, &c, (usec_t) -1, &need_nl); + if (r < 0) { if (r == -EBADMSG) { puts("Bad input, please try again."); @@ -4079,6 +4091,39 @@ unsigned columns(void) { return parsed_columns; } +int fd_lines(int fd) { + struct winsize ws; + zero(ws); + + if (ioctl(fd, TIOCGWINSZ, &ws) < 0) + return -errno; + + if (ws.ws_row <= 0) + return -EIO; + + return ws.ws_row; +} + +unsigned lines(void) { + static __thread int parsed_lines = 0; + const char *e; + + if (_likely_(parsed_lines > 0)) + return parsed_lines; + + e = getenv("LINES"); + if (e) + parsed_lines = atoi(e); + + if (parsed_lines <= 0) + parsed_lines = fd_lines(STDOUT_FILENO); + + if (parsed_lines <= 0) + parsed_lines = 25; + + return parsed_lines; +} + int running_in_chroot(void) { struct stat a, b; @@ -4850,7 +4895,7 @@ int pipe_eof(int fd) { return pollfd.revents & POLLHUP; } -int fd_wait_for_event(int fd, int event) { +int fd_wait_for_event(int fd, int event, usec_t t) { struct pollfd pollfd; int r; @@ -4858,7 +4903,7 @@ int fd_wait_for_event(int fd, int event) { pollfd.fd = fd; pollfd.events = event; - r = poll(&pollfd, 1, -1); + r = poll(&pollfd, 1, t == (usec_t) -1 ? -1 : (int) (t / USEC_PER_MSEC)); if (r < 0) return -errno; diff --git a/src/util.h b/src/util.h index 1ce2440f3f..202d07a854 100644 --- a/src/util.h +++ b/src/util.h @@ -327,7 +327,7 @@ bool fstype_is_network(const char *fstype); int chvt(int vt); -int read_one_char(FILE *f, char *ret, bool *need_nl); +int read_one_char(FILE *f, char *ret, usec_t timeout, bool *need_nl); int ask(char *ret, const char *replies, const char *text, ...); int reset_terminal_fd(int fd); @@ -384,6 +384,9 @@ void status_welcome(void); int fd_columns(int fd); unsigned columns(void); +int fd_lines(int fd); +unsigned lines(void); + int running_in_chroot(void); char *ellipsize(const char *s, size_t length, unsigned percent); @@ -525,7 +528,7 @@ unsigned long cap_last_cap(void); char *format_bytes(char *buf, size_t l, off_t t); -int fd_wait_for_event(int fd, int event); +int fd_wait_for_event(int fd, int event, usec_t timeout); void* memdup(const void *p, size_t l); -- cgit v1.2.3-54-g00ecf From c97e8d10fe35ab282725deb3e05542a598e46cb1 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Mon, 23 Jan 2012 04:42:11 +0100 Subject: add .vimrc --- .gitignore | 4 ---- .vimrc | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 .vimrc (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index d27424ba9a..055d59511d 100644 --- a/.gitignore +++ b/.gitignore @@ -78,7 +78,6 @@ systemadm *.8 *.html *~ -*.tar.gz *.o *.lo *.a @@ -102,8 +101,5 @@ stamp-* *.stamp /Makefile ltmain.sh -*.tar.bz2 -*.tar.gz *.tar.xz libtool -.vimrc diff --git a/.vimrc b/.vimrc new file mode 100644 index 0000000000..366fbdca4b --- /dev/null +++ b/.vimrc @@ -0,0 +1,4 @@ +" 'set exrc' in ~/.vimrc will read .vimrc from the current directory +set tabstop=8 +set shiftwidth=8 +set expandtab -- cgit v1.2.3-54-g00ecf From 36db9a8d5b36173daf1f622fb544f4c90cfddb67 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 23 Jan 2012 23:34:36 +0100 Subject: login: add multi-session X wrapper In preparation for https://bugzilla.gnome.org/show_bug.cgi?id=655380 we decided it's better to include the multi-seat X wrapper in systemd, rather than gdm. (Side effect: this makes this accessible for other DMs) This is a stop-gap for now, until X gins proper multi-seat graphics support at which point this code will go away without replacement. --- .gitignore | 1 + Makefile.am | 15 ++++ src/login/multi-seat-x.c | 187 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 203 insertions(+) create mode 100644 src/login/multi-seat-x.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 055d59511d..3da7e66518 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/systemd-multi-seat-x /systemd-cgtop /systemd-coredump /systemd-cat diff --git a/Makefile.am b/Makefile.am index 1df603ba60..d6fcd23823 100644 --- a/Makefile.am +++ b/Makefile.am @@ -99,6 +99,7 @@ AM_CPPFLAGS = \ -DUSER_GENERATOR_PATH=\"$(usergeneratordir)\" \ -DSYSTEM_SHUTDOWN_PATH=\"$(systemshutdowndir)\" \ -DSYSTEMD_KBD_MODEL_MAP=\"$(pkgdatadir)/kbd-model-map\" \ + -DX_SERVER=\"$(bindir)/X\" \ -I $(top_srcdir)/src \ -I $(top_srcdir)/src/readahead \ -I $(top_srcdir)/src/login \ @@ -1988,6 +1989,20 @@ logind-install-data-hook: INSTALL_DATA_HOOKS += \ logind-install-data-hook +systemd_multi_seat_x_SOURCES = \ + src/login/multi-seat-x.c + +systemd_multi_seat_x_CFLAGS = \ + $(AM_CFLAGS) \ + $(UDEV_CFLAGS) + +systemd_multi_seat_x_LDADD = \ + libsystemd-basic.la \ + $(UDEV_LIBS) + +rootlibexec_PROGRAMS += \ + systemd-multi-seat-x + systemd_uaccess_SOURCES = \ src/login/uaccess.c diff --git a/src/login/multi-seat-x.c b/src/login/multi-seat-x.c new file mode 100644 index 0000000000..8f3aacdd3a --- /dev/null +++ b/src/login/multi-seat-x.c @@ -0,0 +1,187 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include + +#include "util.h" + +int main(int argc, char *argv[]) { + + struct udev *udev = NULL; + struct udev_enumerate *enumerator = NULL; + struct udev_list_entry *first, *item; + int i; + const char *seat = NULL; + char **new_argv; + char *path = NULL, *device_node = NULL; + int r; + FILE *f = NULL; + + /* This binary will go away as soon as X natively supports + * display enumeration with udev in a way that covers both PCI + * and USB. */ + + /* This will simply determine the fb device id of the graphics + * device assigned to a seat and write a configuration file + * from it and then spawn the real X server. */ + + for (i = 1; i < argc; i++) + if (streq(argv[i], "-seat")) + seat = argv[i+1]; + + if (isempty(seat) || streq(seat, "seat0")) { + argv[0] = (char*) X_SERVER; + execv(X_SERVER, argv); + log_error("Failed to execute real X server: %m"); + goto fail; + } + + udev = udev_new(); + if (!udev) { + log_error("Failed to allocate udev environment."); + goto fail; + } + + enumerator = udev_enumerate_new(udev); + if (!enumerator) { + log_error("Failed to allocate udev enumerator."); + goto fail; + } + + udev_enumerate_add_match_subsystem(enumerator, "graphics"); + udev_enumerate_add_match_tag(enumerator, seat); + + r = udev_enumerate_scan_devices(enumerator); + if (r < 0) { + log_error("Failed to enumerate devices."); + goto fail; + } + + first = udev_enumerate_get_list_entry(enumerator); + udev_list_entry_foreach(item, first) { + struct udev_device *d; + const char *dn; + + d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); + if (!d) + continue; + + dn = udev_device_get_devnode(d); + + if (!dn) { + device_node = strdup(dn); + if (!device_node) { + udev_device_unref(d); + log_error("Out of memory."); + goto fail; + } + } + + udev_device_unref(d); + + if (device_node) + break; + } + + if (!device_node) { + log_error("Failed to find device node for seat %s.", seat); + goto fail; + } + + r = safe_mkdir("/run/systemd/multi-session-x", 0755, 0, 0); + if (r < 0) { + log_error("Failed to create directory: %s", strerror(-r)); + goto fail; + } + + path = strappend("/run/systemd/multi-session-x/", seat); + if (!path) { + log_error("Out of memory"); + goto fail; + } + + f = fopen(path, "we"); + if (!f) { + log_error("Failed to write configuration file: %m"); + goto fail; + } + + fprintf(f, + "Section \"Device\"\n" + " Identifier \"udev\"\n" + " Driver \"fbdev\"\n" + " Option \"fbdev\" \"%s\"\n" + "EndSection\n" + "Section \"ServerFlags\"\n" + " Option \"AutoAddDevices\" \"True\"\n" + " Option \"AllowEmptyInput\" \"True\"\n" + "EndSection\n", + device_node); + + fflush(f); + + if (ferror(f)) { + log_error("Failed to write configuration file: %m"); + goto fail; + } + + fclose(f); + f = NULL; + + new_argv = alloca(sizeof(char*) * (argc + 3 + 1)); + memcpy(new_argv, argv, sizeof(char*) * (argc + 2 + 1)); + + new_argv[0] = (char*) X_SERVER; + new_argv[argc+0] = (char*) "-config"; + new_argv[argc+1] = path; + new_argv[argc+2] = (char*) "-sharevts"; + new_argv[argc+3] = NULL; + + udev_enumerate_unref(enumerator); + enumerator = NULL; + + udev_unref(udev); + udev = NULL; + + free(device_node); + device_node = NULL; + + execv(X_SERVER, new_argv); + log_error("Failed to execute real X server: %m"); + +fail: + if (enumerator) + udev_enumerate_unref(enumerator); + + if (udev) + udev_unref(udev); + + free(path); + free(device_node); + + if (f) + fclose(f); + + return EXIT_FAILURE; +} -- cgit v1.2.3-54-g00ecf From 9cacf564513b5fd6078cd418b104083aab8b9bd8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 6 Mar 2012 01:28:32 +0100 Subject: umount: don't try to umount /usr, if we are running from it --- .gitignore | 2 ++ TODO | 4 ++-- src/umount.c | 6 +++++- 3 files changed, 9 insertions(+), 3 deletions(-) (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 3da7e66518..3b69eb46f9 100644 --- a/.gitignore +++ b/.gitignore @@ -103,4 +103,6 @@ stamp-* /Makefile ltmain.sh *.tar.xz +*.tar.gz +*.tar.bz2 libtool diff --git a/TODO b/TODO index fbc494ef41..9b6f526920 100644 --- a/TODO +++ b/TODO @@ -21,6 +21,8 @@ Bugfixes: Features: +* prefer /etc over /run? + * when dumping cgroup contents, include main/control PID of a service, explicitly * keep an eye on https://bugzilla.gnome.org/show_bug.cgi?id=670100 @@ -41,8 +43,6 @@ Features: * isolate for getty is still broken, due to logind -* don't umount /usr - * default unix qlen is too small (10). bump sysctl? add sockopt? * support units generated by a generator and placed in /run/systemd/system/; the directory is diff --git a/src/umount.c b/src/umount.c index 4e036d82a3..0a63d23a01 100644 --- a/src/umount.c +++ b/src/umount.c @@ -410,7 +410,11 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_e assert(head); LIST_FOREACH_SAFE(mount_point, m, n, *head) { - if (streq(m->path, "/")) { + if (path_equal(m->path, "/") +#ifndef HAVE_SPLIT_USR + || path_equal(m->path, "/usr") +#endif + ) { n_failed++; continue; } -- cgit v1.2.3-54-g00ecf From 169c4f65131fbc7bcb51e7d5487a715cdcd0e0eb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 26 Mar 2012 20:58:47 +0200 Subject: journalctl,loginctl: drop systemd- prefix in binary names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's make things a bit easier to type, drop the systemd- prefix for journalctl and loginctl, but provide the old names for compat. All systemd binaries are hence now prefixed with "systemd-" with the exception of the three primary user interface binaries: systemctl loginctl journalctl For those three we do provide systemd-xyz names as well, via symlinks: systemd-systemctl → systemctl systemd-loginctl → loginctl systemd-journalctl → journalctl We do this only for the *primary* user tools, in order to avoid unnecessary namespace problems. That means tools like systemd-notify stay the way they are. --- .gitignore | 4 +- Makefile.am | 56 ++++-- TODO | 4 + man/journalctl.xml | 253 +++++++++++++++++++++++ man/loginctl.xml | 458 ++++++++++++++++++++++++++++++++++++++++++ man/systemctl.xml | 7 +- man/systemd-journalctl.xml | 252 ----------------------- man/systemd-journald.conf.xml | 2 +- man/systemd-loginctl.xml | 457 ----------------------------------------- man/systemd-logind.conf.xml | 2 +- man/systemd.exec.xml | 3 +- 11 files changed, 768 insertions(+), 730 deletions(-) create mode 100644 man/journalctl.xml create mode 100644 man/loginctl.xml delete mode 100644 man/systemd-journalctl.xml delete mode 100644 man/systemd-loginctl.xml (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 3b69eb46f9..8f30c5e0d0 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ /systemd-cat /systemd-rc-local-generator /libsystemd-id128.pc -systemd-journalctl +journalctl systemd-journald test-id128 test-journal @@ -14,7 +14,7 @@ org.freedesktop.locale1.xml libsystemd-daemon.pc libsystemd-login.pc test-login -systemd-loginctl +loginctl systemd-localed systemd-timedated org.freedesktop.timedate1.xml diff --git a/Makefile.am b/Makefile.am index 32ec0e04ce..99ad9c0309 100644 --- a/Makefile.am +++ b/Makefile.am @@ -690,16 +690,22 @@ MANPAGES = \ man/systemd-cat.1 \ man/systemd-machine-id-setup.1 \ man/systemd-journald.conf.5 \ - man/systemd-journalctl.1 + man/journalctl.1 MANPAGES_ALIAS = \ man/reboot.8 \ man/poweroff.8 \ - man/init.1 + man/init.1 \ + man/systemd-systemctl.1 \ + man/systemd-loginctl.1 \ + man/systemd-journalctl.1 man/reboot.8: man/halt.8 man/poweroff.8: man/halt.8 man/init.1: man/systemd.1 +man/systemd-systemctl.1: man/systemctl.1 +man/systemd-loginctl.1: man/loginctl.1 +man/systemd-journalctl.1: man/journalctl.1 XML_FILES = \ ${patsubst %.1,%.xml,${patsubst %.3,%.xml,${patsubst %.5,%.xml,${patsubst %.7,%.xml,${patsubst %.8,%.xml,$(MANPAGES)}}}}} @@ -979,6 +985,14 @@ systemctl_LDADD = \ libsystemd-id128.la \ $(DBUS_LIBS) +systemctl-install-hook: + cd $(DESTDIR)$(rootbindir) && \ + rm -f systemd-systemctl && \ + $(LN_S) systemctl systemd-systemctl + +INSTALL_EXEC_HOOKS += \ + systemctl-install-hook + systemd_notify_SOURCES = \ src/notify.c \ src/readahead/sd-readahead.c @@ -1207,26 +1221,34 @@ systemd_cat_LDADD = \ libsystemd-basic.la \ libsystemd-journal.la -systemd_journalctl_SOURCES = \ +journalctl_SOURCES = \ src/journal/journalctl.c \ src/pager.c \ src/logs-show.c -systemd_journalctl_LDADD = \ +journalctl_LDADD = \ libsystemd-basic.la \ libsystemd-journal.la \ libsystemd-id128.la if HAVE_XZ -systemd_journalctl_SOURCES += \ +journalctl_SOURCES += \ src/journal/compress.c -systemd_journalctl_CFLAGS = \ +journalctl_CFLAGS = \ $(AM_CFLAGS) \ $(XZ_CFLAGS) -systemd_journalctl_LDADD += \ +journalctl_LDADD += \ $(XZ_LIBS) endif +journalctl-install-hook: + cd $(DESTDIR)$(rootbindir) && \ + rm -f systemd-journalctl && \ + $(LN_S) journalctl systemd-journalctl + +INSTALL_EXEC_HOOKS += \ + journalctl-install-hook + test_journal_SOURCES = \ src/journal/test-journal.c \ src/journal/sd-journal.c \ @@ -1313,7 +1335,7 @@ rootlibexec_PROGRAMS += \ systemd-journald rootbin_PROGRAMS += \ - systemd-journalctl + journalctl bin_PROGRAMS += \ systemd-cat @@ -1813,7 +1835,7 @@ rootlibexec_PROGRAMS += \ systemd-logind \ systemd-user-sessions -systemd_loginctl_SOURCES = \ +loginctl_SOURCES = \ src/login/loginctl.c \ src/login/sysfs-show.c \ src/dbus-common.c \ @@ -1821,18 +1843,26 @@ systemd_loginctl_SOURCES = \ src/cgroup-util.c \ src/pager.c -systemd_loginctl_CFLAGS = \ +loginctl_CFLAGS = \ $(AM_CFLAGS) \ $(DBUS_CFLAGS) \ $(UDEV_CFLAGS) -systemd_loginctl_LDADD = \ +loginctl_LDADD = \ libsystemd-basic.la \ $(DBUS_LIBS) \ $(UDEV_LIBS) rootbin_PROGRAMS += \ - systemd-loginctl + loginctl + +loginctl-install-hook: + cd $(DESTDIR)$(rootbindir) && \ + rm -f systemd-loginctl && \ + $(LN_S) loginctl systemd-loginctl + +INSTALL_EXEC_HOOKS += \ + loginctl-install-hook test_login_SOURCES = \ src/login/test-login.c @@ -1997,7 +2027,7 @@ nodist_udevrules_DATA += \ MANPAGES += \ man/systemd-logind.conf.5 \ man/sd-login.7 \ - man/systemd-loginctl.1 \ + man/loginctl.1 \ man/sd_login_monitor_new.3 \ man/sd_pid_get_session.3 \ man/sd_uid_get_state.3 \ diff --git a/TODO b/TODO index 176c2a742c..7f19c3adcb 100644 --- a/TODO +++ b/TODO @@ -18,6 +18,10 @@ Bugfixes: Features: +* ensure that logind sets the syslog facility to AUTH when logging + +* when a service has the same env var set twice we actually store it twice and return that in systemctl show -p... We should only show the last setting + * add man page documenting all kernel cmdline options, including stuff like fsck.mode= * show getty in container mode, not sulogin diff --git a/man/journalctl.xml b/man/journalctl.xml new file mode 100644 index 0000000000..c4d2a7e0e8 --- /dev/null +++ b/man/journalctl.xml @@ -0,0 +1,253 @@ + + + + + + + + + journalctl + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + journalctl + 1 + + + + journalctl + systemd-journalctl + Query the systemd journal + + + + + journalctl OPTIONS MATCH + + + + + Description + + journalctl may be + used to query the contents of the + systemd1 + journal. + + If called without parameter will show the full + contents of the journal, starting with the oldest + entry collected. + + If a match argument is passed the output is + filtered accordingly. A match is in the format + FIELD=VALUE, + e.g. _SYSTEMD_UNIT=httpd.service. + + Output is interleaved from all accessible + journal files, whether they are rotated or currently + being written, and regardless whether they belong to the + system itself or are accessible user journals. + + All users are granted access to their private + per-user journals. However, by default only root and + users who are members of the adm + group get access to the system journal and the + journals of other users. + + + + Options + + The following options are understood: + + + + + + + Prints a short help + text and exits. + + + + + + Prints a short version + string and exits. + + + + + + Do not pipe output into a + pager. + + + + + + + Show all fields in + full, even if they include unprintable + characters or are very + long. + + + + + + + Show only most recent + journal entries, and continously print + new entries as they are appended to + the journal. + + + + + + + Controls the number of + journal lines to show, counting from + the most recent ones. Takes a positive + integer argument. In follow mode + defaults to 10, otherwise is unset + thus not limiting how many lines are + shown. + + + + + + Show all stored output + lines, even in follow mode. Undoes the + effect of + . + + + + + + + Controls the + formatting of the journal entries that are + shown. Takes one of + short, + short-monotonic, + verbose, + export, + json, + cat. short + is the default and generates an output + that is mostly identical to the + formatting of classic syslog log + files, showing one line per journal + entry. short-monotonic + is very similar but shows monotonic + timestamps instead of wallclock + timestamps. verbose + shows the full structered entry items + with all + fiels. export + serializes the journal into a binary + (but mostly text-based) stream + suitable for backups and network + transfer. json + formats entries as JSON data + structures. cat + generates a very terse output only + showing the actual message of each + journal entry with no meta data, not + even a timestamp. + + + + + + + Suppresses any warning + message regarding inaccessable system + journals when run as normal + user. + + + + + + Instead of showing + journal contents generate a new 128 + bit ID suitable for identifying + messages. This is intended for usage + by developers who need a new + identifier for a new message they + introduce and want to make + recognizable. Will print the new ID in + three different formats which can be + copied into source code or + similar. + + + + + + + Exit status + + On success 0 is returned, a non-zero failure + code otherwise. + + + + Environment + + + + $SYSTEMD_PAGER + Pager to use when + is not given; + overrides $PAGER. Setting + this to an empty string or the value + cat is equivalent to passing + . + + + + + + See Also + + systemd1, + systemctl1, + systemd-journald.conf5 + + + + diff --git a/man/loginctl.xml b/man/loginctl.xml new file mode 100644 index 0000000000..be72cc331a --- /dev/null +++ b/man/loginctl.xml @@ -0,0 +1,458 @@ + + + + + + + + + loginctl + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + loginctl + 1 + + + + loginctl + systemd-loginctl + Control the systemd login manager + + + + + loginctl OPTIONS COMMAND NAME + + + + + Description + + loginctl may be used to + introspect and control the state of the + systemd1 + login manager. + + + + Options + + The following options are understood: + + + + + + + Prints a short help + text and exits. + + + + + + Prints a short version + string and exits. + + + + + + + When showing + session/user/ properties, limit + display to certain properties as + specified as argument. If not + specified all set properties are + shown. The argument should be a + property name, such as + Sessions. If + specified more than once all + properties with the specified names + are shown. + + + + + + + When showing + unit/job/manager properties, show all + properties regardless whether they are + set or not. + + + + + + + Do not pipe output into a + pager. + + + + + + When used with + kill-session, + choose which processes to kill. Must + be one of , or + to select whether + to kill only the leader process of the + session or all processes of the + session. If omitted defaults to + . + + + + + + + When used with + kill-session or + kill-user, choose + which signal to send to selected + processes. Must be one of the well + known signal specifiers such as + SIGTERM, SIGINT or SIGSTOP. If omitted + defaults to + . + + + + + + + Execute operation + remotely. Specify a hostname, or + username and hostname separated by @, + to connect to. This will use SSH to + talk to the remote login manager + instance. + + + + + + + Acquire privileges via + PolicyKit before executing the + operation. + + + + The following commands are understood: + + + + list-sessions + + List current sessions. + + + + session-status [ID...] + + Show terse runtime + status information about one or more + sessions. This function is intended to + generate human-readable output. If you + are looking for computer-parsable + output, use + show-session + instead. + + + + show-session [ID...] + + Show properties of one + or more sessions or the manager + itself. If no argument is specified + properties of the manager will be + shown. If a session ID is specified + properties of the session is shown. By + default, empty properties are + suppressed. Use + to show those too. To select specific + properties to show use + . This + command is intended to be used + whenever computer-parsable output is + required. Use + session-status if + you are looking for formatted + human-readable + output. + + + + activate [ID...] + + Activate one or more + sessions. This brings one or more + sessions into the foreground, if + another session is currently in the + foreground on the respective + seat. + + + + lock-session [ID...] + unlock-session [ID...] + + Activates/deactivates + the screen lock on one or more + sessions, if the session supports it. + + + + terminate-session [ID...] + + Terminates a + session. This kills all processes of + the session and deallocates all + resources attached to the + session. + + + + kill-session [ID...] + + Send a signal to one + or more processes of the session. Use + to select + which process to kill. Use + to select + the signal to send. + + + + list-users + + List currently logged + in users. + + + + user-status [USER...] + + Show terse runtime + status information about one or more + logged in users. This function is + intended to generate human-readable + output. If you are looking for + computer-parsable output, use + show-user + instead. Users may be specified by + their usernames or numeric user + IDs. + + + + show-user [USER...] + + Show properties of one + or more users or the manager + itself. If no argument is specified + properties of the manager will be + shown. If a user is specified + properties of the user is shown. By + default, empty properties are + suppressed. Use + to show those too. To select specific + properties to show use + . This + command is intended to be used + whenever computer-parsable output is + required. Use + user-status if + you are looking for formatted + human-readable + output. + + + + enable-linger [USER...] + disable-linger [USER...] + + Enable/disable user + lingering for one or more users. If + enabled for a specific user a user + manager is spawned for him/her at + boot, and kept around after + logouts. This allows users who aren't + logged in to run long-running + services. + + + + terminate-user [USER...] + + Terminates all + sessions of a user. This kills all + processes of all sessions of the user + and deallocates all runtime resources + attached to the + user. + + + + kill-user [USER...] + + Send a signal to all + processes of a user. Use + to select + the signal to send. + + + + list-seats + + List currently + available seats on the local + system. + + + + seat-status [NAME...] + + Show terse runtime + status information about one or more + seats. This function is + intended to generate human-readable + output. If you are looking for + computer-parsable output, use + show-seat + instead. + + + + show-seat [NAME...] + + Show properties of one + or more seats or the manager + itself. If no argument is specified + properties of the manager will be + shown. If a seat is specified + properties of the seat are shown. By + default, empty properties are + suppressed. Use + to show those too. To select specific + properties to show use + . This + command is intended to be used + whenever computer-parsable output is + required. Use + seat-status if you + are looking for formatted + human-readable + output. + + + + attach [NAME] [DEVICE...] + + Attach one or more + devices to a seat. The devices should + be specified via device paths in the + /sys file + system. To create a new seat attach at + least one graphics card to a + previously unused seat names. seat + names may consist only of a-z, A-Z, + 0-9, "-" and "_" and must be prefixed + with "seat". To drop assignment of a + device to a specific seat just + reassign it to a different seat, or + use + flush-devices. + + + + flush-devices + + Removes all device + assignments previously created with + attach. After this + call only automatically generated + seats will remain and all seat + hardware is assigned to + them. + + + + terminate-seat [NAME...] + + Terminates all + sessions on a seat. This kills all + processes of all sessions on a seat and + deallocates all runtime resources + attached to them. + + + + + + + Exit status + + On success 0 is returned, a non-zero failure + code otherwise. + + + + Environment + + + + $SYSTEMD_PAGER + Pager to use when + is not given; + overrides $PAGER. Setting + this to an empty string or the value + cat is equivalent to passing + . + + + + + + See Also + + systemd1, + systemctl1, + systemd-logind.conf5 + + + + diff --git a/man/systemctl.xml b/man/systemctl.xml index c467ed8bf7..ffe0164008 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -44,6 +44,7 @@ systemctl + systemd-systemctl Control the systemd system and service manager @@ -448,7 +449,7 @@ formatting of the journal entries that are shown. For the available choices see - systemd-journalctl1. Defaults + journalctl1. Defaults to short. @@ -1179,8 +1180,8 @@ systemd1, systemadm1, - systemd-journalctl1, - systemd-loginctl1, + journalctl1, + loginctl1, systemd.unit5, systemd.special7, wall1 diff --git a/man/systemd-journalctl.xml b/man/systemd-journalctl.xml deleted file mode 100644 index 1441ecaca1..0000000000 --- a/man/systemd-journalctl.xml +++ /dev/null @@ -1,252 +0,0 @@ - - - - - - - - - systemd-journalctl - systemd - - - - Developer - Lennart - Poettering - lennart@poettering.net - - - - - - systemd-journalctl - 1 - - - - systemd-journalctl - Query the systemd journal - - - - - systemd-journalctl OPTIONS MATCH - - - - - Description - - systemd-journalctl may be - used to query the contents of the - systemd1 - journal. - - If called without parameter will show the full - contents of the journal, starting with the oldest - entry collected. - - If a match argument is passed the output is - filtered accordingly. A match is in the format - FIELD=VALUE, - e.g. _SYSTEMD_UNIT=httpd.service. - - Output is interleaved from all accessible - journal files, whether they are rotated or currently - being written, and regardless whether they belong to the - system itself or are accessible user journals. - - All users are granted access to their private - per-user journals. However, by default only root and - users who are members of the adm - group get access to the system journal and the - journals of other users. - - - - Options - - The following options are understood: - - - - - - - Prints a short help - text and exits. - - - - - - Prints a short version - string and exits. - - - - - - Do not pipe output into a - pager. - - - - - - - Show all fields in - full, even if they include unprintable - characters or are very - long. - - - - - - - Show only most recent - journal entries, and continously print - new entries as they are appended to - the journal. - - - - - - - Controls the number of - journal lines to show, counting from - the most recent ones. Takes a positive - integer argument. In follow mode - defaults to 10, otherwise is unset - thus not limiting how many lines are - shown. - - - - - - Show all stored output - lines, even in follow mode. Undoes the - effect of - . - - - - - - - Controls the - formatting of the journal entries that are - shown. Takes one of - short, - short-monotonic, - verbose, - export, - json, - cat. short - is the default and generates an output - that is mostly identical to the - formatting of classic syslog log - files, showing one line per journal - entry. short-monotonic - is very similar but shows monotonic - timestamps instead of wallclock - timestamps. verbose - shows the full structered entry items - with all - fiels. export - serializes the journal into a binary - (but mostly text-based) stream - suitable for backups and network - transfer. json - formats entries as JSON data - structures. cat - generates a very terse output only - showing the actual message of each - journal entry with no meta data, not - even a timestamp. - - - - - - - Suppresses any warning - message regarding inaccessable system - journals when run as normal - user. - - - - - - Instead of showing - journal contents generate a new 128 - bit ID suitable for identifying - messages. This is intended for usage - by developers who need a new - identifier for a new message they - introduce and want to make - recognizable. Will print the new ID in - three different formats which can be - copied into source code or - similar. - - - - - - - Exit status - - On success 0 is returned, a non-zero failure - code otherwise. - - - - Environment - - - - $SYSTEMD_PAGER - Pager to use when - is not given; - overrides $PAGER. Setting - this to an empty string or the value - cat is equivalent to passing - . - - - - - - See Also - - systemd1, - systemctl1, - systemd-journald.conf5 - - - - diff --git a/man/systemd-journald.conf.xml b/man/systemd-journald.conf.xml index f3cd4db260..37dae68b36 100644 --- a/man/systemd-journald.conf.xml +++ b/man/systemd-journald.conf.xml @@ -246,7 +246,7 @@ See Also systemd1, - systemd-journalctl1, + journalctl1, systemd.conf5 diff --git a/man/systemd-loginctl.xml b/man/systemd-loginctl.xml deleted file mode 100644 index 6a282769a3..0000000000 --- a/man/systemd-loginctl.xml +++ /dev/null @@ -1,457 +0,0 @@ - - - - - - - - - systemd-loginctl - systemd - - - - Developer - Lennart - Poettering - lennart@poettering.net - - - - - - systemd-loginctl - 1 - - - - systemd-loginctl - Control the systemd login manager - - - - - systemd-loginctl OPTIONS COMMAND NAME - - - - - Description - - systemd-loginctl may be used to - introspect and control the state of the - systemd1 - login manager. - - - - Options - - The following options are understood: - - - - - - - Prints a short help - text and exits. - - - - - - Prints a short version - string and exits. - - - - - - - When showing - session/user/ properties, limit - display to certain properties as - specified as argument. If not - specified all set properties are - shown. The argument should be a - property name, such as - Sessions. If - specified more than once all - properties with the specified names - are shown. - - - - - - - When showing - unit/job/manager properties, show all - properties regardless whether they are - set or not. - - - - - - - Do not pipe output into a - pager. - - - - - - When used with - kill-session, - choose which processes to kill. Must - be one of , or - to select whether - to kill only the leader process of the - session or all processes of the - session. If omitted defaults to - . - - - - - - - When used with - kill-session or - kill-user, choose - which signal to send to selected - processes. Must be one of the well - known signal specifiers such as - SIGTERM, SIGINT or SIGSTOP. If omitted - defaults to - . - - - - - - - Execute operation - remotely. Specify a hostname, or - username and hostname separated by @, - to connect to. This will use SSH to - talk to the remote login manager - instance. - - - - - - - Acquire privileges via - PolicyKit before executing the - operation. - - - - The following commands are understood: - - - - list-sessions - - List current sessions. - - - - session-status [ID...] - - Show terse runtime - status information about one or more - sessions. This function is intended to - generate human-readable output. If you - are looking for computer-parsable - output, use - show-session - instead. - - - - show-session [ID...] - - Show properties of one - or more sessions or the manager - itself. If no argument is specified - properties of the manager will be - shown. If a session ID is specified - properties of the session is shown. By - default, empty properties are - suppressed. Use - to show those too. To select specific - properties to show use - . This - command is intended to be used - whenever computer-parsable output is - required. Use - session-status if - you are looking for formatted - human-readable - output. - - - - activate [ID...] - - Activate one or more - sessions. This brings one or more - sessions into the foreground, if - another session is currently in the - foreground on the respective - seat. - - - - lock-session [ID...] - unlock-session [ID...] - - Activates/deactivates - the screen lock on one or more - sessions, if the session supports it. - - - - terminate-session [ID...] - - Terminates a - session. This kills all processes of - the session and deallocates all - resources attached to the - session. - - - - kill-session [ID...] - - Send a signal to one - or more processes of the session. Use - to select - which process to kill. Use - to select - the signal to send. - - - - list-users - - List currently logged - in users. - - - - user-status [USER...] - - Show terse runtime - status information about one or more - logged in users. This function is - intended to generate human-readable - output. If you are looking for - computer-parsable output, use - show-user - instead. Users may be specified by - their usernames or numeric user - IDs. - - - - show-user [USER...] - - Show properties of one - or more users or the manager - itself. If no argument is specified - properties of the manager will be - shown. If a user is specified - properties of the user is shown. By - default, empty properties are - suppressed. Use - to show those too. To select specific - properties to show use - . This - command is intended to be used - whenever computer-parsable output is - required. Use - user-status if - you are looking for formatted - human-readable - output. - - - - enable-linger [USER...] - disable-linger [USER...] - - Enable/disable user - lingering for one or more users. If - enabled for a specific user a user - manager is spawned for him/her at - boot, and kept around after - logouts. This allows users who aren't - logged in to run long-running - services. - - - - terminate-user [USER...] - - Terminates all - sessions of a user. This kills all - processes of all sessions of the user - and deallocates all runtime resources - attached to the - user. - - - - kill-user [USER...] - - Send a signal to all - processes of a user. Use - to select - the signal to send. - - - - list-seats - - List currently - available seats on the local - system. - - - - seat-status [NAME...] - - Show terse runtime - status information about one or more - seats. This function is - intended to generate human-readable - output. If you are looking for - computer-parsable output, use - show-seat - instead. - - - - show-seat [NAME...] - - Show properties of one - or more seats or the manager - itself. If no argument is specified - properties of the manager will be - shown. If a seat is specified - properties of the seat are shown. By - default, empty properties are - suppressed. Use - to show those too. To select specific - properties to show use - . This - command is intended to be used - whenever computer-parsable output is - required. Use - seat-status if you - are looking for formatted - human-readable - output. - - - - attach [NAME] [DEVICE...] - - Attach one or more - devices to a seat. The devices should - be specified via device paths in the - /sys file - system. To create a new seat attach at - least one graphics card to a - previously unused seat names. seat - names may consist only of a-z, A-Z, - 0-9, "-" and "_" and must be prefixed - with "seat". To drop assignment of a - device to a specific seat just - reassign it to a different seat, or - use - flush-devices. - - - - flush-devices - - Removes all device - assignments previously created with - attach. After this - call only automatically generated - seats will remain and all seat - hardware is assigned to - them. - - - - terminate-seat [NAME...] - - Terminates all - sessions on a seat. This kills all - processes of all sessions on a seat and - deallocates all runtime resources - attached to them. - - - - - - - Exit status - - On success 0 is returned, a non-zero failure - code otherwise. - - - - Environment - - - - $SYSTEMD_PAGER - Pager to use when - is not given; - overrides $PAGER. Setting - this to an empty string or the value - cat is equivalent to passing - . - - - - - - See Also - - systemd1, - systemctl1, - systemd-logind.conf5 - - - - diff --git a/man/systemd-logind.conf.xml b/man/systemd-logind.conf.xml index 4b0281503e..27c11c2904 100644 --- a/man/systemd-logind.conf.xml +++ b/man/systemd-logind.conf.xml @@ -167,7 +167,7 @@ See Also systemd1, - systemd-loginctl1, + loginctl1, systemd.conf5 diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index ac0f89fb85..e6f49c9fd0 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -397,7 +397,7 @@ dmesg1. connects it with the journal which is accessible via - systemd-journalctl1 + journalctl1 (Note that everything that is written to syslog or kmsg is implicitly stored in the journal as well, those options @@ -1094,6 +1094,7 @@ systemd1, systemctl8, + journalctl8, systemd.unit5, systemd.service5, systemd.socket5, -- cgit v1.2.3-54-g00ecf From b070e7f3c9ed680c821bd89d42506695f2438506 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 2 Apr 2012 19:24:30 +0200 Subject: journal: implicitly add code location to all messages logged with the native interface This logic can be turned off by defining SD_JOURNAL_SUPPRESS_LOCATION before including sd-journal.h. This also saves/restores errno in all logging functions, in order to be useful as logging calls without side-effects. This also adds a couple of __unlikely__ around the early checks in the logging calls, in order to minimize the runtime impact. --- .gitignore | 1 + Makefile.am | 10 +- TODO | 2 + src/journal/journal-send.c | 238 ++++++++++++++++++++++++++++++++----- src/journal/libsystemd-journal.sym | 8 ++ src/journal/test-journal-send.c | 32 +++++ src/log.h | 2 +- src/systemd/sd-journal.h | 22 +++- 8 files changed, 283 insertions(+), 32 deletions(-) create mode 100644 src/journal/test-journal-send.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 8f30c5e0d0..f36dd8a81a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/test-journal-send /systemd-multi-seat-x /systemd-cgtop /systemd-coredump diff --git a/Makefile.am b/Makefile.am index 3357264a85..219d8ded8e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1247,6 +1247,13 @@ test_journal_LDADD += \ $(XZ_LIBS) endif +test_journal_send_SOURCES = \ + src/journal/test-journal-send.c + +test_journal_send_LDADD = \ + libsystemd-basic.la \ + libsystemd-journal.la + libsystemd_journal_la_SOURCES = \ src/journal/sd-journal.c \ src/journal/journal-file.c \ @@ -1297,7 +1304,8 @@ UNINSTALL_EXEC_HOOKS += \ libsystemd-journal-uninstall-hook noinst_PROGRAMS += \ - test-journal + test-journal \ + test-journal-send pkginclude_HEADERS += \ src/systemd/sd-journal.h \ diff --git a/TODO b/TODO index d8fffc369b..84636c48e4 100644 --- a/TODO +++ b/TODO @@ -19,6 +19,8 @@ Bugfixes: Features: * cleanup syslog 'priority' vs. 'level' wording +* journal: if mmap() fails for mapping window try to unmap a a few older maps + * add flag file for shutdownd so that clients can check whether a shutdown is queued * new env var for /var/tmp? glib? diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c index b5b4fbfa0d..32e94af91f 100644 --- a/src/journal/journal-send.c +++ b/src/journal/journal-send.c @@ -26,6 +26,8 @@ #include #include +#define SD_JOURNAL_SUPPRESS_LOCATION + #include "sd-journal.h" #include "util.h" #include "socket-util.h" @@ -94,12 +96,26 @@ _public_ int sd_journal_printv(int priority, const char *format, va_list ap) { return sd_journal_sendv(iov, 2); } -_public_ int sd_journal_send(const char *format, ...) { - int r, n = 0, i = 0, j; - va_list ap; +static int fill_iovec_sprintf(const char *format, va_list ap, int extra, struct iovec **_iov) { + int r, n = 0, i, j; struct iovec *iov = NULL; + int saved_errno; + + assert(_iov); + saved_errno = errno; + + if (extra > 0) { + n = MAX(extra * 2, extra + 4); + iov = malloc0(n * sizeof(struct iovec)); + if (!iov) { + r = -ENOMEM; + goto fail; + } + + i = extra; + } else + i = 0; - va_start(ap, format); while (format) { struct iovec *c; char *buffer; @@ -124,11 +140,39 @@ _public_ int sd_journal_send(const char *format, ...) { format = va_arg(ap, char *); } + + *_iov = iov; + + errno = saved_errno; + return i; + +fail: + for (j = 0; j < i; j++) + free(iov[j].iov_base); + + free(iov); + + errno = saved_errno; + return r; +} + +_public_ int sd_journal_send(const char *format, ...) { + int r, i, j; + va_list ap; + struct iovec *iov = NULL; + + va_start(ap, format); + i = fill_iovec_sprintf(format, ap, 0, &iov); va_end(ap); + if (_unlikely_(i < 0)) { + r = i; + goto finish; + } + r = sd_journal_sendv(iov, i); -fail: +finish: for (j = 0; j < i; j++) free(iov[j].iov_base); @@ -141,10 +185,11 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) { int fd, buffer_fd; struct iovec *w; uint64_t *l; - int i, j = 0; + int r, i, j = 0; struct msghdr mh; struct sockaddr_un sa; ssize_t k; + int saved_errno; union { struct cmsghdr cmsghdr; uint8_t buf[CMSG_SPACE(sizeof(int))]; @@ -155,27 +200,37 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) { * and where unprivileged users can create files. */ char path[] = "/dev/shm/journal.XXXXXX"; - if (!iov || n <= 0) + if (_unlikely_(!iov)) + return -EINVAL; + + if (_unlikely_(n <= 0)) return -EINVAL; + saved_errno = errno; + w = alloca(sizeof(struct iovec) * n * 5); l = alloca(sizeof(uint64_t) * n); for (i = 0; i < n; i++) { char *c, *nl; - if (!iov[i].iov_base || - iov[i].iov_len <= 1) - return -EINVAL; + if (_unlikely_(!iov[i].iov_base || iov[i].iov_len <= 1)) { + r = -EINVAL; + goto finish; + } c = memchr(iov[i].iov_base, '=', iov[i].iov_len); - if (!c || c == iov[i].iov_base) - return -EINVAL; + if (_unlikely_(!c || c == iov[i].iov_base)) { + r = -EINVAL; + goto finish; + } nl = memchr(iov[i].iov_base, '\n', iov[i].iov_len); if (nl) { - if (nl < c) - return -EINVAL; + if (_unlikely_(nl < c)) { + r = -EINVAL; + goto finish; + } /* Already includes a newline? Bummer, then * let's write the variable name, then a @@ -206,8 +261,10 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) { } fd = journal_fd(); - if (fd < 0) - return fd; + if (_unlikely_(fd < 0)) { + r = fd; + goto finish; + } zero(sa); sa.sun_family = AF_UNIX; @@ -220,29 +277,37 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) { mh.msg_iovlen = j; k = sendmsg(fd, &mh, MSG_NOSIGNAL); - if (k >= 0) - return 0; + if (k >= 0) { + r = 0; + goto finish; + } - if (errno != EMSGSIZE && errno != ENOBUFS) - return -errno; + if (errno != EMSGSIZE && errno != ENOBUFS) { + r = -errno; + goto finish; + } /* Message doesn't fit... Let's dump the data in a temporary * file and just pass a file descriptor of it to the other * side */ buffer_fd = mkostemp(path, O_CLOEXEC|O_RDWR); - if (buffer_fd < 0) - return -errno; + if (buffer_fd < 0) { + r = -errno; + goto finish; + } if (unlink(path) < 0) { close_nointr_nofail(buffer_fd); - return -errno; + r = -errno; + goto finish; } n = writev(buffer_fd, w, j); - if (n < 0) { + if (n < 0) { close_nointr_nofail(buffer_fd); - return -errno; + r = -errno; + goto finish; } mh.msg_iov = NULL; @@ -263,10 +328,17 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) { k = sendmsg(fd, &mh, MSG_NOSIGNAL); close_nointr_nofail(buffer_fd); - if (k < 0) - return -errno; + if (k < 0) { + r = -errno; + goto finish; + } + + r = 0; + +finish: + errno = saved_errno; - return 0; + return r; } _public_ int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix) { @@ -332,3 +404,113 @@ _public_ int sd_journal_stream_fd(const char *identifier, int priority, int leve return fd; } + +_public_ int sd_journal_print_with_location(int priority, const char *file, const char *line, const char *func, const char *format, ...) { + int r; + va_list ap; + + va_start(ap, format); + r = sd_journal_printv_with_location(priority, file, line, func, format, ap); + va_end(ap); + + return r; +} + +_public_ int sd_journal_printv_with_location(int priority, const char *file, const char *line, const char *func, const char *format, va_list ap) { + char buffer[8 + LINE_MAX], p[11]; + struct iovec iov[5]; + char *f; + size_t fl; + + if (priority < 0 || priority > 7) + return -EINVAL; + + if (_unlikely_(!format)) + return -EINVAL; + + snprintf(p, sizeof(p), "PRIORITY=%i", priority & LOG_PRIMASK); + char_array_0(p); + + memcpy(buffer, "MESSAGE=", 8); + vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap); + char_array_0(buffer); + + /* func is initialized from __func__ which is not a macro, but + * a static const char[], hence cannot easily be prefixed with + * CODE_FUNC=, hence let's do it manually here. */ + fl = strlen(func); + f = alloca(fl + 10); + memcpy(f, "CODE_FUNC=", 10); + memcpy(f + 10, func, fl + 1); + + zero(iov); + IOVEC_SET_STRING(iov[0], buffer); + IOVEC_SET_STRING(iov[1], p); + IOVEC_SET_STRING(iov[2], file); + IOVEC_SET_STRING(iov[3], line); + IOVEC_SET_STRING(iov[4], f); + + return sd_journal_sendv(iov, ELEMENTSOF(iov)); +} + +_public_ int sd_journal_send_with_location(const char *file, const char *line, const char *func, const char *format, ...) { + int r, i, j; + va_list ap; + struct iovec *iov = NULL; + char *f; + size_t fl; + + va_start(ap, format); + i = fill_iovec_sprintf(format, ap, 3, &iov); + va_end(ap); + + if (_unlikely_(i < 0)) { + r = i; + goto finish; + } + + fl = strlen(func); + f = alloca(fl + 10); + memcpy(f, "CODE_FUNC=", 10); + memcpy(f + 10, func, fl + 1); + + IOVEC_SET_STRING(iov[0], file); + IOVEC_SET_STRING(iov[1], line); + IOVEC_SET_STRING(iov[2], f); + + r = sd_journal_sendv(iov, i); + +finish: + for (j = 3; j < i; j++) + free(iov[j].iov_base); + + free(iov); + + return r; +} + +_public_ int sd_journal_sendv_with_location(const char *file, const char *line, const char *func, const struct iovec *iov, int n) { + struct iovec *niov; + char *f; + size_t fl; + + if (_unlikely_(!iov)) + return -EINVAL; + + if (_unlikely_(n <= 0)) + return -EINVAL; + + niov = alloca(sizeof(struct iovec) * (n + 3)); + memcpy(niov, iov, sizeof(struct iovec) * n); + + fl = strlen(func); + f = alloca(fl + 10); + memcpy(f, "CODE_FUNC=", 10); + memcpy(f + 10, func, fl + 1); + + IOVEC_SET_STRING(niov[n++], file); + IOVEC_SET_STRING(niov[n++], line); + IOVEC_SET_STRING(niov[n++], f); + + return sd_journal_sendv(niov, n); +} diff --git a/src/journal/libsystemd-journal.sym b/src/journal/libsystemd-journal.sym index 7653880e8f..cd434aea26 100644 --- a/src/journal/libsystemd-journal.sym +++ b/src/journal/libsystemd-journal.sym @@ -43,3 +43,11 @@ global: local: *; }; + +LIBSYSTEMD_JOURNAL_45 { +global: + sd_journal_print_with_location; + sd_journal_printv_with_location; + sd_journal_send_with_location; + sd_journal_sendv_with_location; +} LIBSYSTEMD_JOURNAL_38; diff --git a/src/journal/test-journal-send.c b/src/journal/test-journal-send.c new file mode 100644 index 0000000000..2f9987dcde --- /dev/null +++ b/src/journal/test-journal-send.c @@ -0,0 +1,32 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include + +int main(int argc, char *argv[]) { + sd_journal_print(LOG_INFO, "piepapo"); + + sd_journal_send("MESSAGE=foobar", + "VALUE=%i", 7, + NULL); + + return 0; +} diff --git a/src/log.h b/src/log.h index cf9f7fa569..5c6310ef14 100644 --- a/src/log.h +++ b/src/log.h @@ -93,7 +93,7 @@ int log_dump_internal( const char *func, char *buffer); -#define log_full(level, ...) log_meta(level, __FILE__, __LINE__, __func__, __VA_ARGS__) +#define log_full(level, ...) log_meta(level, __FILE__, __LINE__, __func__, __VA_ARGS__) #define log_debug(...) log_meta(LOG_DEBUG, __FILE__, __LINE__, __func__, __VA_ARGS__) #define log_info(...) log_meta(LOG_INFO, __FILE__, __LINE__, __func__, __VA_ARGS__) diff --git a/src/systemd/sd-journal.h b/src/systemd/sd-journal.h index 5a2dab1569..f27e461b03 100644 --- a/src/systemd/sd-journal.h +++ b/src/systemd/sd-journal.h @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -34,13 +35,30 @@ extern "C" { #endif /* Write to daemon */ - int sd_journal_print(int piority, const char *format, ...) __attribute__ ((format (printf, 2, 3))); int sd_journal_printv(int priority, const char *format, va_list ap); - int sd_journal_send(const char *format, ...) __attribute__((sentinel)); int sd_journal_sendv(const struct iovec *iov, int n); +/* Used by the macros below */ +int sd_journal_print_with_location(int priority, const char *file, const char *line, const char *func, const char *format, ...) __attribute__ ((format (printf, 5, 6))); +int sd_journal_printv_with_location(int priority, const char *file, const char *line, const char *func, const char *format, va_list ap); +int sd_journal_send_with_location(const char *file, const char *line, const char *func, const char *format, ...) __attribute__((sentinel)); +int sd_journal_sendv_with_location(const char *file, const char *line, const char *func, const struct iovec *iov, int n); + +/* implicitly add code location to messages sent, if this is enabled */ +#ifndef SD_JOURNAL_SUPPRESS_LOCATION + +#define _sd_XSTRINGIFY(x) #x +#define _sd_STRINGIFY(x) _sd_XSTRINGIFY(x) + +#define sd_journal_print(priority, ...) sd_journal_print_with_location(priority, "CODE_FILE=" __FILE__, "CODE_LINE=" _sd_STRINGIFY(__LINE__), __func__, __VA_ARGS__) +#define sd_journal_printv(priority, format, ap) sd_journal_printv_with_location(priority, "CODE_FILE=" __FILE__, "CODE_LINE=" _sd_STRINGIFY(__LINE__), __func__, format, ap) +#define sd_journal_send(...) sd_journal_send_with_location("CODE_FILE=" __FILE__, "CODE_LINE=" _sd_STRINGIFY(__LINE__), __func__, __VA_ARGS__) +#define sd_journal_sendv(iovec, n) sd_journal_sendv_with_location("CODE_FILE=" __FILE__, "CODE_LINE=" _sd_STRINGIFY(__LINE__), __func__, iovec, n) + +#endif + int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix); /* Browse journal stream */ -- cgit v1.2.3-54-g00ecf From 3e2147858f21943d5f4a781c60f33ac22c6096ed Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Tue, 3 Apr 2012 21:24:46 +0200 Subject: move imported udev into place --- .gitignore | 11 + Makefile.am | 758 ++- TODO | 25 + autogen.sh | 4 +- configure.ac | 157 +- m4/.gitignore | 1 + man/udev.xml | 695 +++ man/udevadm.xml | 472 ++ man/udevd.xml | 151 + rules/.gitignore | 1 + rules/42-usb-hid-pm.rules | 49 + rules/50-udev-default.rules | 107 + rules/60-persistent-alsa.rules | 14 + rules/60-persistent-input.rules | 38 + rules/60-persistent-serial.rules | 20 + rules/60-persistent-storage-tape.rules | 25 + rules/60-persistent-storage.rules | 89 + rules/75-net-description.rules | 14 + rules/75-tty-description.rules | 14 + rules/78-sound-card.rules | 89 + rules/80-drivers.rules | 12 + rules/95-udev-late.rules | 4 + rules/99-systemd.rules.in | 55 + src/99-systemd.rules.in | 55 - src/udev/.gitignore | 32 +- src/udev/COPYING | 339 -- src/udev/ChangeLog | 6387 -------------------- src/udev/INSTALL | 44 - src/udev/Makefile.am | 712 --- src/udev/NEWS | 1735 ------ src/udev/README | 101 - src/udev/TODO | 22 - src/udev/accelerometer/61-accelerometer.rules | 3 + src/udev/accelerometer/accelerometer.c | 357 ++ src/udev/ata_id/ata_id.c | 721 +++ src/udev/autogen.sh | 44 - src/udev/cdrom_id/60-cdrom_id.rules | 20 + src/udev/cdrom_id/cdrom_id.c | 1099 ++++ src/udev/collect/collect.c | 473 ++ src/udev/configure.ac | 242 - src/udev/docs/.gitignore | 18 + src/udev/docs/Makefile.am | 99 + src/udev/docs/libudev-docs.xml | 32 + src/udev/docs/libudev-sections.txt | 127 + src/udev/docs/libudev.types | 0 src/udev/docs/version.xml.in | 1 + src/udev/gudev/.gitignore | 9 + src/udev/gudev/docs/.gitignore | 17 + src/udev/gudev/docs/Makefile.am | 106 + src/udev/gudev/docs/gudev-docs.xml | 93 + src/udev/gudev/docs/gudev-sections.txt | 113 + src/udev/gudev/docs/gudev.types | 4 + src/udev/gudev/docs/version.xml.in | 1 + src/udev/gudev/gjs-example.js | 75 + src/udev/gudev/gudev-1.0.pc.in | 11 + src/udev/gudev/gudev.h | 33 + src/udev/gudev/gudevclient.c | 527 ++ src/udev/gudev/gudevclient.h | 100 + src/udev/gudev/gudevdevice.c | 963 +++ src/udev/gudev/gudevdevice.h | 128 + src/udev/gudev/gudevenumerator.c | 431 ++ src/udev/gudev/gudevenumerator.h | 107 + src/udev/gudev/gudevenums.h | 49 + src/udev/gudev/gudevenumtypes.c.template | 39 + src/udev/gudev/gudevenumtypes.h.template | 24 + src/udev/gudev/gudevmarshal.list | 1 + src/udev/gudev/gudevprivate.h | 41 + src/udev/gudev/gudevtypes.h | 51 + src/udev/gudev/seed-example-enum.js | 38 + src/udev/gudev/seed-example.js | 72 + src/udev/keymap/.gitignore | 5 + src/udev/keymap/95-keyboard-force-release.rules | 54 + src/udev/keymap/95-keymap.rules | 170 + src/udev/keymap/README.keymap.txt | 101 + src/udev/keymap/check-keymaps.sh | 38 + src/udev/keymap/findkeyboards | 68 + .../keymap/force-release-maps/common-volume-keys | 3 + src/udev/keymap/force-release-maps/dell-touchpad | 1 + src/udev/keymap/force-release-maps/hp-other | 3 + src/udev/keymap/force-release-maps/samsung-90x3a | 6 + src/udev/keymap/force-release-maps/samsung-other | 10 + src/udev/keymap/keyboard-force-release.sh.in | 22 + src/udev/keymap/keymap.c | 447 ++ src/udev/keymap/keymaps/acer | 22 + src/udev/keymap/keymaps/acer-aspire_5720 | 4 + src/udev/keymap/keymaps/acer-aspire_5920g | 5 + src/udev/keymap/keymaps/acer-aspire_6920 | 5 + src/udev/keymap/keymaps/acer-aspire_8930 | 5 + src/udev/keymap/keymaps/acer-travelmate_c300 | 5 + src/udev/keymap/keymaps/asus | 3 + src/udev/keymap/keymaps/compaq-e_evo | 4 + src/udev/keymap/keymaps/dell | 29 + src/udev/keymap/keymaps/dell-latitude-xt2 | 4 + src/udev/keymap/keymaps/everex-xt5000 | 7 + src/udev/keymap/keymaps/fujitsu-amilo_li_2732 | 3 + src/udev/keymap/keymaps/fujitsu-amilo_pa_2548 | 3 + .../keymap/keymaps/fujitsu-amilo_pro_edition_v3505 | 4 + src/udev/keymap/keymaps/fujitsu-amilo_pro_v3205 | 2 + src/udev/keymap/keymaps/fujitsu-amilo_si_1520 | 6 + src/udev/keymap/keymaps/fujitsu-esprimo_mobile_v5 | 4 + src/udev/keymap/keymaps/fujitsu-esprimo_mobile_v6 | 2 + src/udev/keymap/keymaps/genius-slimstar-320 | 35 + src/udev/keymap/keymaps/hewlett-packard | 12 + .../keymap/keymaps/hewlett-packard-2510p_2530p | 2 + .../keymaps/hewlett-packard-compaq_elitebook | 2 + src/udev/keymap/keymaps/hewlett-packard-pavilion | 3 + .../keymap/keymaps/hewlett-packard-presario-2100 | 3 + src/udev/keymap/keymaps/hewlett-packard-tablet | 6 + src/udev/keymap/keymaps/hewlett-packard-tx2 | 3 + .../keymaps/ibm-thinkpad-usb-keyboard-trackpoint | 7 + src/udev/keymap/keymaps/inventec-symphony_6.0_7.0 | 2 + src/udev/keymap/keymaps/lenovo-3000 | 5 + src/udev/keymap/keymaps/lenovo-ideapad | 8 + .../lenovo-thinkpad-usb-keyboard-trackpoint | 13 + .../keymap/keymaps/lenovo-thinkpad_x200_tablet | 6 + src/udev/keymap/keymaps/lenovo-thinkpad_x6_tablet | 8 + src/udev/keymap/keymaps/lg-x110 | 12 + src/udev/keymap/keymaps/logitech-wave | 16 + src/udev/keymap/keymaps/logitech-wave-cordless | 15 + src/udev/keymap/keymaps/logitech-wave-pro-cordless | 12 + src/udev/keymap/keymaps/maxdata-pro_7000 | 9 + src/udev/keymap/keymaps/medion-fid2060 | 2 + src/udev/keymap/keymaps/medionnb-a555 | 4 + src/udev/keymap/keymaps/micro-star | 13 + src/udev/keymap/keymaps/module-asus-w3j | 11 + src/udev/keymap/keymaps/module-ibm | 16 + src/udev/keymap/keymaps/module-lenovo | 17 + src/udev/keymap/keymaps/module-sony | 8 + src/udev/keymap/keymaps/module-sony-old | 2 + src/udev/keymap/keymaps/module-sony-vgn | 8 + src/udev/keymap/keymaps/olpc-xo | 74 + src/udev/keymap/keymaps/onkyo | 14 + src/udev/keymap/keymaps/oqo-model2 | 5 + src/udev/keymap/keymaps/samsung-90x3a | 5 + src/udev/keymap/keymaps/samsung-other | 14 + src/udev/keymap/keymaps/samsung-sq1us | 7 + src/udev/keymap/keymaps/samsung-sx20s | 4 + src/udev/keymap/keymaps/toshiba-satellite_a100 | 2 + src/udev/keymap/keymaps/toshiba-satellite_a110 | 10 + src/udev/keymap/keymaps/toshiba-satellite_m30x | 6 + src/udev/keymap/keymaps/zepto-znote | 11 + src/udev/libudev-device-private.c | 185 + src/udev/libudev-device.c | 1744 ++++++ src/udev/libudev-enumerate.c | 947 +++ src/udev/libudev-list.c | 344 ++ src/udev/libudev-monitor.c | 874 +++ src/udev/libudev-private.h | 213 + src/udev/libudev-queue-private.c | 412 ++ src/udev/libudev-queue.c | 474 ++ src/udev/libudev-selinux-private.c | 109 + src/udev/libudev-util-private.c | 242 + src/udev/libudev-util.c | 570 ++ src/udev/libudev.c | 457 ++ src/udev/libudev.h | 189 + src/udev/libudev.pc.in | 11 + src/udev/m4/.gitignore | 4 - src/udev/mtd_probe/75-probe_mtd.rules | 8 + src/udev/mtd_probe/mtd_probe.c | 51 + src/udev/mtd_probe/mtd_probe.h | 49 + src/udev/mtd_probe/probe_smartmedia.c | 97 + src/udev/rules/42-usb-hid-pm.rules | 49 - src/udev/rules/50-udev-default.rules | 107 - src/udev/rules/60-persistent-alsa.rules | 14 - src/udev/rules/60-persistent-input.rules | 38 - src/udev/rules/60-persistent-serial.rules | 20 - src/udev/rules/60-persistent-storage-tape.rules | 25 - src/udev/rules/60-persistent-storage.rules | 89 - src/udev/rules/75-net-description.rules | 14 - src/udev/rules/75-tty-description.rules | 14 - src/udev/rules/78-sound-card.rules | 89 - src/udev/rules/80-drivers.rules | 12 - src/udev/rules/95-udev-late.rules | 4 - src/udev/scsi_id/.gitignore | 1 + src/udev/scsi_id/README | 4 + src/udev/scsi_id/scsi.h | 97 + src/udev/scsi_id/scsi_id.c | 657 ++ src/udev/scsi_id/scsi_id.h | 73 + src/udev/scsi_id/scsi_serial.c | 990 +++ src/udev/src/.gitignore | 5 - src/udev/src/COPYING | 502 -- src/udev/src/accelerometer/61-accelerometer.rules | 3 - src/udev/src/accelerometer/accelerometer.c | 357 -- src/udev/src/ata_id/ata_id.c | 721 --- src/udev/src/cdrom_id/60-cdrom_id.rules | 20 - src/udev/src/cdrom_id/cdrom_id.c | 1099 ---- src/udev/src/collect/collect.c | 473 -- src/udev/src/docs/.gitignore | 17 - src/udev/src/docs/Makefile.am | 99 - src/udev/src/docs/libudev-docs.xml | 32 - src/udev/src/docs/libudev-sections.txt | 127 - src/udev/src/docs/libudev.types | 0 src/udev/src/docs/version.xml.in | 1 - src/udev/src/floppy/60-floppy.rules | 4 - src/udev/src/floppy/create_floppy_devices.c | 177 - src/udev/src/gudev/.gitignore | 9 - src/udev/src/gudev/COPYING | 502 -- src/udev/src/gudev/docs/.gitignore | 16 - src/udev/src/gudev/docs/Makefile.am | 106 - src/udev/src/gudev/docs/gudev-docs.xml | 93 - src/udev/src/gudev/docs/gudev-sections.txt | 113 - src/udev/src/gudev/docs/gudev.types | 4 - src/udev/src/gudev/docs/version.xml.in | 1 - src/udev/src/gudev/gjs-example.js | 75 - src/udev/src/gudev/gudev-1.0.pc.in | 11 - src/udev/src/gudev/gudev.h | 33 - src/udev/src/gudev/gudevclient.c | 527 -- src/udev/src/gudev/gudevclient.h | 100 - src/udev/src/gudev/gudevdevice.c | 963 --- src/udev/src/gudev/gudevdevice.h | 128 - src/udev/src/gudev/gudevenumerator.c | 431 -- src/udev/src/gudev/gudevenumerator.h | 107 - src/udev/src/gudev/gudevenums.h | 49 - src/udev/src/gudev/gudevenumtypes.c.template | 39 - src/udev/src/gudev/gudevenumtypes.h.template | 24 - src/udev/src/gudev/gudevmarshal.list | 1 - src/udev/src/gudev/gudevprivate.h | 41 - src/udev/src/gudev/gudevtypes.h | 51 - src/udev/src/gudev/seed-example-enum.js | 38 - src/udev/src/gudev/seed-example.js | 72 - src/udev/src/keymap/.gitignore | 5 - .../src/keymap/95-keyboard-force-release.rules | 54 - src/udev/src/keymap/95-keymap.rules | 170 - src/udev/src/keymap/README.keymap.txt | 101 - src/udev/src/keymap/check-keymaps.sh | 38 - src/udev/src/keymap/findkeyboards | 68 - .../keymap/force-release-maps/common-volume-keys | 3 - .../src/keymap/force-release-maps/dell-touchpad | 1 - src/udev/src/keymap/force-release-maps/hp-other | 3 - .../src/keymap/force-release-maps/samsung-90x3a | 6 - .../src/keymap/force-release-maps/samsung-other | 10 - src/udev/src/keymap/keyboard-force-release.sh.in | 22 - src/udev/src/keymap/keymap.c | 447 -- src/udev/src/keymap/keymaps/acer | 22 - src/udev/src/keymap/keymaps/acer-aspire_5720 | 4 - src/udev/src/keymap/keymaps/acer-aspire_5920g | 5 - src/udev/src/keymap/keymaps/acer-aspire_6920 | 5 - src/udev/src/keymap/keymaps/acer-aspire_8930 | 5 - src/udev/src/keymap/keymaps/acer-travelmate_c300 | 5 - src/udev/src/keymap/keymaps/asus | 3 - src/udev/src/keymap/keymaps/compaq-e_evo | 4 - src/udev/src/keymap/keymaps/dell | 29 - src/udev/src/keymap/keymaps/dell-latitude-xt2 | 4 - src/udev/src/keymap/keymaps/everex-xt5000 | 7 - src/udev/src/keymap/keymaps/fujitsu-amilo_li_2732 | 3 - src/udev/src/keymap/keymaps/fujitsu-amilo_pa_2548 | 3 - .../keymap/keymaps/fujitsu-amilo_pro_edition_v3505 | 4 - .../src/keymap/keymaps/fujitsu-amilo_pro_v3205 | 2 - src/udev/src/keymap/keymaps/fujitsu-amilo_si_1520 | 6 - .../src/keymap/keymaps/fujitsu-esprimo_mobile_v5 | 4 - .../src/keymap/keymaps/fujitsu-esprimo_mobile_v6 | 2 - src/udev/src/keymap/keymaps/genius-slimstar-320 | 35 - src/udev/src/keymap/keymaps/hewlett-packard | 12 - .../src/keymap/keymaps/hewlett-packard-2510p_2530p | 2 - .../keymaps/hewlett-packard-compaq_elitebook | 2 - .../src/keymap/keymaps/hewlett-packard-pavilion | 3 - .../keymap/keymaps/hewlett-packard-presario-2100 | 3 - src/udev/src/keymap/keymaps/hewlett-packard-tablet | 6 - src/udev/src/keymap/keymaps/hewlett-packard-tx2 | 3 - .../keymaps/ibm-thinkpad-usb-keyboard-trackpoint | 7 - .../src/keymap/keymaps/inventec-symphony_6.0_7.0 | 2 - src/udev/src/keymap/keymaps/lenovo-3000 | 5 - src/udev/src/keymap/keymaps/lenovo-ideapad | 8 - .../lenovo-thinkpad-usb-keyboard-trackpoint | 13 - .../src/keymap/keymaps/lenovo-thinkpad_x200_tablet | 6 - .../src/keymap/keymaps/lenovo-thinkpad_x6_tablet | 8 - src/udev/src/keymap/keymaps/lg-x110 | 12 - src/udev/src/keymap/keymaps/logitech-wave | 16 - src/udev/src/keymap/keymaps/logitech-wave-cordless | 15 - .../src/keymap/keymaps/logitech-wave-pro-cordless | 12 - src/udev/src/keymap/keymaps/maxdata-pro_7000 | 9 - src/udev/src/keymap/keymaps/medion-fid2060 | 2 - src/udev/src/keymap/keymaps/medionnb-a555 | 4 - src/udev/src/keymap/keymaps/micro-star | 13 - src/udev/src/keymap/keymaps/module-asus-w3j | 11 - src/udev/src/keymap/keymaps/module-ibm | 16 - src/udev/src/keymap/keymaps/module-lenovo | 17 - src/udev/src/keymap/keymaps/module-sony | 8 - src/udev/src/keymap/keymaps/module-sony-old | 2 - src/udev/src/keymap/keymaps/module-sony-vgn | 8 - src/udev/src/keymap/keymaps/olpc-xo | 74 - src/udev/src/keymap/keymaps/onkyo | 14 - src/udev/src/keymap/keymaps/oqo-model2 | 5 - src/udev/src/keymap/keymaps/samsung-90x3a | 5 - src/udev/src/keymap/keymaps/samsung-other | 14 - src/udev/src/keymap/keymaps/samsung-sq1us | 7 - src/udev/src/keymap/keymaps/samsung-sx20s | 4 - src/udev/src/keymap/keymaps/toshiba-satellite_a100 | 2 - src/udev/src/keymap/keymaps/toshiba-satellite_a110 | 10 - src/udev/src/keymap/keymaps/toshiba-satellite_m30x | 6 - src/udev/src/keymap/keymaps/zepto-znote | 11 - src/udev/src/libudev-device-private.c | 185 - src/udev/src/libudev-device.c | 1744 ------ src/udev/src/libudev-enumerate.c | 947 --- src/udev/src/libudev-list.c | 344 -- src/udev/src/libudev-monitor.c | 874 --- src/udev/src/libudev-private.h | 213 - src/udev/src/libudev-queue-private.c | 412 -- src/udev/src/libudev-queue.c | 474 -- src/udev/src/libudev-selinux-private.c | 109 - src/udev/src/libudev-util-private.c | 242 - src/udev/src/libudev-util.c | 570 -- src/udev/src/libudev.c | 457 -- src/udev/src/libudev.h | 189 - src/udev/src/libudev.pc.in | 11 - src/udev/src/mtd_probe/75-probe_mtd.rules | 8 - src/udev/src/mtd_probe/mtd_probe.c | 51 - src/udev/src/mtd_probe/mtd_probe.h | 49 - src/udev/src/mtd_probe/probe_smartmedia.c | 97 - .../rule_generator/75-cd-aliases-generator.rules | 9 - .../75-persistent-net-generator.rules | 102 - .../src/rule_generator/rule_generator.functions | 113 - src/udev/src/rule_generator/write_cd_rules | 126 - src/udev/src/rule_generator/write_net_rules | 141 - src/udev/src/scsi_id/.gitignore | 1 - src/udev/src/scsi_id/README | 4 - src/udev/src/scsi_id/scsi.h | 97 - src/udev/src/scsi_id/scsi_id.8 | 119 - src/udev/src/scsi_id/scsi_id.c | 657 -- src/udev/src/scsi_id/scsi_id.h | 73 - src/udev/src/scsi_id/scsi_serial.c | 990 --- src/udev/src/sd-daemon.c | 530 -- src/udev/src/sd-daemon.h | 282 - src/udev/src/test-libudev.c | 501 -- src/udev/src/test-udev.c | 121 - src/udev/src/udev-builtin-blkid.c | 207 - src/udev/src/udev-builtin-firmware.c | 168 - src/udev/src/udev-builtin-hwdb.c | 247 - src/udev/src/udev-builtin-input_id.c | 218 - src/udev/src/udev-builtin-kmod.c | 142 - src/udev/src/udev-builtin-path_id.c | 498 -- src/udev/src/udev-builtin-usb_id.c | 482 -- src/udev/src/udev-builtin.c | 134 - src/udev/src/udev-control.socket | 10 - src/udev/src/udev-ctrl.c | 494 -- src/udev/src/udev-event.c | 1011 ---- src/udev/src/udev-kernel.socket | 10 - src/udev/src/udev-node.c | 379 -- src/udev/src/udev-rules.c | 2767 --------- src/udev/src/udev-settle.service.in | 25 - src/udev/src/udev-trigger.service.in | 10 - src/udev/src/udev-watch.c | 170 - src/udev/src/udev.conf | 3 - src/udev/src/udev.h | 188 - src/udev/src/udev.pc.in | 5 - src/udev/src/udev.service.in | 14 - src/udev/src/udev.xml | 695 --- src/udev/src/udevadm-control.c | 175 - src/udev/src/udevadm-info.c | 568 -- src/udev/src/udevadm-monitor.c | 297 - src/udev/src/udevadm-settle.c | 235 - src/udev/src/udevadm-test-builtin.c | 128 - src/udev/src/udevadm-test.c | 173 - src/udev/src/udevadm-trigger.c | 232 - src/udev/src/udevadm.c | 165 - src/udev/src/udevadm.xml | 472 -- src/udev/src/udevd.c | 1746 ------ src/udev/src/udevd.xml | 151 - src/udev/src/v4l_id/60-persistent-v4l.rules | 20 - src/udev/src/v4l_id/v4l_id.c | 87 - src/udev/test-libudev.c | 501 ++ src/udev/test-udev.c | 121 + src/udev/test/rules-test.sh | 2 +- src/udev/udev-builtin-blkid.c | 207 + src/udev/udev-builtin-firmware.c | 168 + src/udev/udev-builtin-hwdb.c | 247 + src/udev/udev-builtin-input_id.c | 218 + src/udev/udev-builtin-kmod.c | 142 + src/udev/udev-builtin-path_id.c | 498 ++ src/udev/udev-builtin-usb_id.c | 482 ++ src/udev/udev-builtin.c | 134 + src/udev/udev-ctrl.c | 494 ++ src/udev/udev-event.c | 1011 ++++ src/udev/udev-node.c | 379 ++ src/udev/udev-rules.c | 2767 +++++++++ src/udev/udev-watch.c | 170 + src/udev/udev.conf | 3 + src/udev/udev.h | 188 + src/udev/udev.pc.in | 5 + src/udev/udevadm-control.c | 175 + src/udev/udevadm-info.c | 568 ++ src/udev/udevadm-monitor.c | 297 + src/udev/udevadm-settle.c | 235 + src/udev/udevadm-test-builtin.c | 128 + src/udev/udevadm-test.c | 173 + src/udev/udevadm-trigger.c | 232 + src/udev/udevadm.c | 165 + src/udev/udevd.c | 1746 ++++++ src/udev/v4l_id/60-persistent-v4l.rules | 20 + src/udev/v4l_id/v4l_id.c | 87 + units/.gitignore | 3 + units/udev-control.socket | 10 + units/udev-kernel.socket | 10 + units/udev-settle.service.in | 25 + units/udev-trigger.service.in | 10 + units/udev.service.in | 14 + 395 files changed, 30594 insertions(+), 42072 deletions(-) create mode 100644 man/udev.xml create mode 100644 man/udevadm.xml create mode 100644 man/udevd.xml create mode 100644 rules/.gitignore create mode 100644 rules/42-usb-hid-pm.rules create mode 100644 rules/50-udev-default.rules create mode 100644 rules/60-persistent-alsa.rules create mode 100644 rules/60-persistent-input.rules create mode 100644 rules/60-persistent-serial.rules create mode 100644 rules/60-persistent-storage-tape.rules create mode 100644 rules/60-persistent-storage.rules create mode 100644 rules/75-net-description.rules create mode 100644 rules/75-tty-description.rules create mode 100644 rules/78-sound-card.rules create mode 100644 rules/80-drivers.rules create mode 100644 rules/95-udev-late.rules create mode 100644 rules/99-systemd.rules.in delete mode 100644 src/99-systemd.rules.in delete mode 100644 src/udev/COPYING delete mode 100644 src/udev/ChangeLog delete mode 100644 src/udev/INSTALL delete mode 100644 src/udev/Makefile.am delete mode 100644 src/udev/NEWS delete mode 100644 src/udev/README delete mode 100644 src/udev/TODO create mode 100644 src/udev/accelerometer/61-accelerometer.rules create mode 100644 src/udev/accelerometer/accelerometer.c create mode 100644 src/udev/ata_id/ata_id.c delete mode 100755 src/udev/autogen.sh create mode 100644 src/udev/cdrom_id/60-cdrom_id.rules create mode 100644 src/udev/cdrom_id/cdrom_id.c create mode 100644 src/udev/collect/collect.c delete mode 100644 src/udev/configure.ac create mode 100644 src/udev/docs/.gitignore create mode 100644 src/udev/docs/Makefile.am create mode 100644 src/udev/docs/libudev-docs.xml create mode 100644 src/udev/docs/libudev-sections.txt create mode 100644 src/udev/docs/libudev.types create mode 100644 src/udev/docs/version.xml.in create mode 100644 src/udev/gudev/.gitignore create mode 100644 src/udev/gudev/docs/.gitignore create mode 100644 src/udev/gudev/docs/Makefile.am create mode 100644 src/udev/gudev/docs/gudev-docs.xml create mode 100644 src/udev/gudev/docs/gudev-sections.txt create mode 100644 src/udev/gudev/docs/gudev.types create mode 100644 src/udev/gudev/docs/version.xml.in create mode 100755 src/udev/gudev/gjs-example.js create mode 100644 src/udev/gudev/gudev-1.0.pc.in create mode 100644 src/udev/gudev/gudev.h create mode 100644 src/udev/gudev/gudevclient.c create mode 100644 src/udev/gudev/gudevclient.h create mode 100644 src/udev/gudev/gudevdevice.c create mode 100644 src/udev/gudev/gudevdevice.h create mode 100644 src/udev/gudev/gudevenumerator.c create mode 100644 src/udev/gudev/gudevenumerator.h create mode 100644 src/udev/gudev/gudevenums.h create mode 100644 src/udev/gudev/gudevenumtypes.c.template create mode 100644 src/udev/gudev/gudevenumtypes.h.template create mode 100644 src/udev/gudev/gudevmarshal.list create mode 100644 src/udev/gudev/gudevprivate.h create mode 100644 src/udev/gudev/gudevtypes.h create mode 100755 src/udev/gudev/seed-example-enum.js create mode 100755 src/udev/gudev/seed-example.js create mode 100644 src/udev/keymap/.gitignore create mode 100644 src/udev/keymap/95-keyboard-force-release.rules create mode 100644 src/udev/keymap/95-keymap.rules create mode 100644 src/udev/keymap/README.keymap.txt create mode 100755 src/udev/keymap/check-keymaps.sh create mode 100755 src/udev/keymap/findkeyboards create mode 100644 src/udev/keymap/force-release-maps/common-volume-keys create mode 100644 src/udev/keymap/force-release-maps/dell-touchpad create mode 100644 src/udev/keymap/force-release-maps/hp-other create mode 100644 src/udev/keymap/force-release-maps/samsung-90x3a create mode 100644 src/udev/keymap/force-release-maps/samsung-other create mode 100755 src/udev/keymap/keyboard-force-release.sh.in create mode 100644 src/udev/keymap/keymap.c create mode 100644 src/udev/keymap/keymaps/acer create mode 100644 src/udev/keymap/keymaps/acer-aspire_5720 create mode 100644 src/udev/keymap/keymaps/acer-aspire_5920g create mode 100644 src/udev/keymap/keymaps/acer-aspire_6920 create mode 100644 src/udev/keymap/keymaps/acer-aspire_8930 create mode 100644 src/udev/keymap/keymaps/acer-travelmate_c300 create mode 100644 src/udev/keymap/keymaps/asus create mode 100644 src/udev/keymap/keymaps/compaq-e_evo create mode 100644 src/udev/keymap/keymaps/dell create mode 100644 src/udev/keymap/keymaps/dell-latitude-xt2 create mode 100644 src/udev/keymap/keymaps/everex-xt5000 create mode 100644 src/udev/keymap/keymaps/fujitsu-amilo_li_2732 create mode 100644 src/udev/keymap/keymaps/fujitsu-amilo_pa_2548 create mode 100644 src/udev/keymap/keymaps/fujitsu-amilo_pro_edition_v3505 create mode 100644 src/udev/keymap/keymaps/fujitsu-amilo_pro_v3205 create mode 100644 src/udev/keymap/keymaps/fujitsu-amilo_si_1520 create mode 100644 src/udev/keymap/keymaps/fujitsu-esprimo_mobile_v5 create mode 100644 src/udev/keymap/keymaps/fujitsu-esprimo_mobile_v6 create mode 100644 src/udev/keymap/keymaps/genius-slimstar-320 create mode 100644 src/udev/keymap/keymaps/hewlett-packard create mode 100644 src/udev/keymap/keymaps/hewlett-packard-2510p_2530p create mode 100644 src/udev/keymap/keymaps/hewlett-packard-compaq_elitebook create mode 100644 src/udev/keymap/keymaps/hewlett-packard-pavilion create mode 100644 src/udev/keymap/keymaps/hewlett-packard-presario-2100 create mode 100644 src/udev/keymap/keymaps/hewlett-packard-tablet create mode 100644 src/udev/keymap/keymaps/hewlett-packard-tx2 create mode 100644 src/udev/keymap/keymaps/ibm-thinkpad-usb-keyboard-trackpoint create mode 100644 src/udev/keymap/keymaps/inventec-symphony_6.0_7.0 create mode 100644 src/udev/keymap/keymaps/lenovo-3000 create mode 100644 src/udev/keymap/keymaps/lenovo-ideapad create mode 100644 src/udev/keymap/keymaps/lenovo-thinkpad-usb-keyboard-trackpoint create mode 100644 src/udev/keymap/keymaps/lenovo-thinkpad_x200_tablet create mode 100644 src/udev/keymap/keymaps/lenovo-thinkpad_x6_tablet create mode 100644 src/udev/keymap/keymaps/lg-x110 create mode 100644 src/udev/keymap/keymaps/logitech-wave create mode 100644 src/udev/keymap/keymaps/logitech-wave-cordless create mode 100644 src/udev/keymap/keymaps/logitech-wave-pro-cordless create mode 100644 src/udev/keymap/keymaps/maxdata-pro_7000 create mode 100644 src/udev/keymap/keymaps/medion-fid2060 create mode 100644 src/udev/keymap/keymaps/medionnb-a555 create mode 100644 src/udev/keymap/keymaps/micro-star create mode 100644 src/udev/keymap/keymaps/module-asus-w3j create mode 100644 src/udev/keymap/keymaps/module-ibm create mode 100644 src/udev/keymap/keymaps/module-lenovo create mode 100644 src/udev/keymap/keymaps/module-sony create mode 100644 src/udev/keymap/keymaps/module-sony-old create mode 100644 src/udev/keymap/keymaps/module-sony-vgn create mode 100644 src/udev/keymap/keymaps/olpc-xo create mode 100644 src/udev/keymap/keymaps/onkyo create mode 100644 src/udev/keymap/keymaps/oqo-model2 create mode 100644 src/udev/keymap/keymaps/samsung-90x3a create mode 100644 src/udev/keymap/keymaps/samsung-other create mode 100644 src/udev/keymap/keymaps/samsung-sq1us create mode 100644 src/udev/keymap/keymaps/samsung-sx20s create mode 100644 src/udev/keymap/keymaps/toshiba-satellite_a100 create mode 100644 src/udev/keymap/keymaps/toshiba-satellite_a110 create mode 100644 src/udev/keymap/keymaps/toshiba-satellite_m30x create mode 100644 src/udev/keymap/keymaps/zepto-znote create mode 100644 src/udev/libudev-device-private.c create mode 100644 src/udev/libudev-device.c create mode 100644 src/udev/libudev-enumerate.c create mode 100644 src/udev/libudev-list.c create mode 100644 src/udev/libudev-monitor.c create mode 100644 src/udev/libudev-private.h create mode 100644 src/udev/libudev-queue-private.c create mode 100644 src/udev/libudev-queue.c create mode 100644 src/udev/libudev-selinux-private.c create mode 100644 src/udev/libudev-util-private.c create mode 100644 src/udev/libudev-util.c create mode 100644 src/udev/libudev.c create mode 100644 src/udev/libudev.h create mode 100644 src/udev/libudev.pc.in delete mode 100644 src/udev/m4/.gitignore create mode 100644 src/udev/mtd_probe/75-probe_mtd.rules create mode 100644 src/udev/mtd_probe/mtd_probe.c create mode 100644 src/udev/mtd_probe/mtd_probe.h create mode 100644 src/udev/mtd_probe/probe_smartmedia.c delete mode 100644 src/udev/rules/42-usb-hid-pm.rules delete mode 100644 src/udev/rules/50-udev-default.rules delete mode 100644 src/udev/rules/60-persistent-alsa.rules delete mode 100644 src/udev/rules/60-persistent-input.rules delete mode 100644 src/udev/rules/60-persistent-serial.rules delete mode 100644 src/udev/rules/60-persistent-storage-tape.rules delete mode 100644 src/udev/rules/60-persistent-storage.rules delete mode 100644 src/udev/rules/75-net-description.rules delete mode 100644 src/udev/rules/75-tty-description.rules delete mode 100644 src/udev/rules/78-sound-card.rules delete mode 100644 src/udev/rules/80-drivers.rules delete mode 100644 src/udev/rules/95-udev-late.rules create mode 100644 src/udev/scsi_id/.gitignore create mode 100644 src/udev/scsi_id/README create mode 100644 src/udev/scsi_id/scsi.h create mode 100644 src/udev/scsi_id/scsi_id.c create mode 100644 src/udev/scsi_id/scsi_id.h create mode 100644 src/udev/scsi_id/scsi_serial.c delete mode 100644 src/udev/src/.gitignore delete mode 100644 src/udev/src/COPYING delete mode 100644 src/udev/src/accelerometer/61-accelerometer.rules delete mode 100644 src/udev/src/accelerometer/accelerometer.c delete mode 100644 src/udev/src/ata_id/ata_id.c delete mode 100644 src/udev/src/cdrom_id/60-cdrom_id.rules delete mode 100644 src/udev/src/cdrom_id/cdrom_id.c delete mode 100644 src/udev/src/collect/collect.c delete mode 100644 src/udev/src/docs/.gitignore delete mode 100644 src/udev/src/docs/Makefile.am delete mode 100644 src/udev/src/docs/libudev-docs.xml delete mode 100644 src/udev/src/docs/libudev-sections.txt delete mode 100644 src/udev/src/docs/libudev.types delete mode 100644 src/udev/src/docs/version.xml.in delete mode 100644 src/udev/src/floppy/60-floppy.rules delete mode 100644 src/udev/src/floppy/create_floppy_devices.c delete mode 100644 src/udev/src/gudev/.gitignore delete mode 100644 src/udev/src/gudev/COPYING delete mode 100644 src/udev/src/gudev/docs/.gitignore delete mode 100644 src/udev/src/gudev/docs/Makefile.am delete mode 100644 src/udev/src/gudev/docs/gudev-docs.xml delete mode 100644 src/udev/src/gudev/docs/gudev-sections.txt delete mode 100644 src/udev/src/gudev/docs/gudev.types delete mode 100644 src/udev/src/gudev/docs/version.xml.in delete mode 100755 src/udev/src/gudev/gjs-example.js delete mode 100644 src/udev/src/gudev/gudev-1.0.pc.in delete mode 100644 src/udev/src/gudev/gudev.h delete mode 100644 src/udev/src/gudev/gudevclient.c delete mode 100644 src/udev/src/gudev/gudevclient.h delete mode 100644 src/udev/src/gudev/gudevdevice.c delete mode 100644 src/udev/src/gudev/gudevdevice.h delete mode 100644 src/udev/src/gudev/gudevenumerator.c delete mode 100644 src/udev/src/gudev/gudevenumerator.h delete mode 100644 src/udev/src/gudev/gudevenums.h delete mode 100644 src/udev/src/gudev/gudevenumtypes.c.template delete mode 100644 src/udev/src/gudev/gudevenumtypes.h.template delete mode 100644 src/udev/src/gudev/gudevmarshal.list delete mode 100644 src/udev/src/gudev/gudevprivate.h delete mode 100644 src/udev/src/gudev/gudevtypes.h delete mode 100755 src/udev/src/gudev/seed-example-enum.js delete mode 100755 src/udev/src/gudev/seed-example.js delete mode 100644 src/udev/src/keymap/.gitignore delete mode 100644 src/udev/src/keymap/95-keyboard-force-release.rules delete mode 100644 src/udev/src/keymap/95-keymap.rules delete mode 100644 src/udev/src/keymap/README.keymap.txt delete mode 100755 src/udev/src/keymap/check-keymaps.sh delete mode 100755 src/udev/src/keymap/findkeyboards delete mode 100644 src/udev/src/keymap/force-release-maps/common-volume-keys delete mode 100644 src/udev/src/keymap/force-release-maps/dell-touchpad delete mode 100644 src/udev/src/keymap/force-release-maps/hp-other delete mode 100644 src/udev/src/keymap/force-release-maps/samsung-90x3a delete mode 100644 src/udev/src/keymap/force-release-maps/samsung-other delete mode 100755 src/udev/src/keymap/keyboard-force-release.sh.in delete mode 100644 src/udev/src/keymap/keymap.c delete mode 100644 src/udev/src/keymap/keymaps/acer delete mode 100644 src/udev/src/keymap/keymaps/acer-aspire_5720 delete mode 100644 src/udev/src/keymap/keymaps/acer-aspire_5920g delete mode 100644 src/udev/src/keymap/keymaps/acer-aspire_6920 delete mode 100644 src/udev/src/keymap/keymaps/acer-aspire_8930 delete mode 100644 src/udev/src/keymap/keymaps/acer-travelmate_c300 delete mode 100644 src/udev/src/keymap/keymaps/asus delete mode 100644 src/udev/src/keymap/keymaps/compaq-e_evo delete mode 100644 src/udev/src/keymap/keymaps/dell delete mode 100644 src/udev/src/keymap/keymaps/dell-latitude-xt2 delete mode 100644 src/udev/src/keymap/keymaps/everex-xt5000 delete mode 100644 src/udev/src/keymap/keymaps/fujitsu-amilo_li_2732 delete mode 100644 src/udev/src/keymap/keymaps/fujitsu-amilo_pa_2548 delete mode 100644 src/udev/src/keymap/keymaps/fujitsu-amilo_pro_edition_v3505 delete mode 100644 src/udev/src/keymap/keymaps/fujitsu-amilo_pro_v3205 delete mode 100644 src/udev/src/keymap/keymaps/fujitsu-amilo_si_1520 delete mode 100644 src/udev/src/keymap/keymaps/fujitsu-esprimo_mobile_v5 delete mode 100644 src/udev/src/keymap/keymaps/fujitsu-esprimo_mobile_v6 delete mode 100644 src/udev/src/keymap/keymaps/genius-slimstar-320 delete mode 100644 src/udev/src/keymap/keymaps/hewlett-packard delete mode 100644 src/udev/src/keymap/keymaps/hewlett-packard-2510p_2530p delete mode 100644 src/udev/src/keymap/keymaps/hewlett-packard-compaq_elitebook delete mode 100644 src/udev/src/keymap/keymaps/hewlett-packard-pavilion delete mode 100644 src/udev/src/keymap/keymaps/hewlett-packard-presario-2100 delete mode 100644 src/udev/src/keymap/keymaps/hewlett-packard-tablet delete mode 100644 src/udev/src/keymap/keymaps/hewlett-packard-tx2 delete mode 100644 src/udev/src/keymap/keymaps/ibm-thinkpad-usb-keyboard-trackpoint delete mode 100644 src/udev/src/keymap/keymaps/inventec-symphony_6.0_7.0 delete mode 100644 src/udev/src/keymap/keymaps/lenovo-3000 delete mode 100644 src/udev/src/keymap/keymaps/lenovo-ideapad delete mode 100644 src/udev/src/keymap/keymaps/lenovo-thinkpad-usb-keyboard-trackpoint delete mode 100644 src/udev/src/keymap/keymaps/lenovo-thinkpad_x200_tablet delete mode 100644 src/udev/src/keymap/keymaps/lenovo-thinkpad_x6_tablet delete mode 100644 src/udev/src/keymap/keymaps/lg-x110 delete mode 100644 src/udev/src/keymap/keymaps/logitech-wave delete mode 100644 src/udev/src/keymap/keymaps/logitech-wave-cordless delete mode 100644 src/udev/src/keymap/keymaps/logitech-wave-pro-cordless delete mode 100644 src/udev/src/keymap/keymaps/maxdata-pro_7000 delete mode 100644 src/udev/src/keymap/keymaps/medion-fid2060 delete mode 100644 src/udev/src/keymap/keymaps/medionnb-a555 delete mode 100644 src/udev/src/keymap/keymaps/micro-star delete mode 100644 src/udev/src/keymap/keymaps/module-asus-w3j delete mode 100644 src/udev/src/keymap/keymaps/module-ibm delete mode 100644 src/udev/src/keymap/keymaps/module-lenovo delete mode 100644 src/udev/src/keymap/keymaps/module-sony delete mode 100644 src/udev/src/keymap/keymaps/module-sony-old delete mode 100644 src/udev/src/keymap/keymaps/module-sony-vgn delete mode 100644 src/udev/src/keymap/keymaps/olpc-xo delete mode 100644 src/udev/src/keymap/keymaps/onkyo delete mode 100644 src/udev/src/keymap/keymaps/oqo-model2 delete mode 100644 src/udev/src/keymap/keymaps/samsung-90x3a delete mode 100644 src/udev/src/keymap/keymaps/samsung-other delete mode 100644 src/udev/src/keymap/keymaps/samsung-sq1us delete mode 100644 src/udev/src/keymap/keymaps/samsung-sx20s delete mode 100644 src/udev/src/keymap/keymaps/toshiba-satellite_a100 delete mode 100644 src/udev/src/keymap/keymaps/toshiba-satellite_a110 delete mode 100644 src/udev/src/keymap/keymaps/toshiba-satellite_m30x delete mode 100644 src/udev/src/keymap/keymaps/zepto-znote delete mode 100644 src/udev/src/libudev-device-private.c delete mode 100644 src/udev/src/libudev-device.c delete mode 100644 src/udev/src/libudev-enumerate.c delete mode 100644 src/udev/src/libudev-list.c delete mode 100644 src/udev/src/libudev-monitor.c delete mode 100644 src/udev/src/libudev-private.h delete mode 100644 src/udev/src/libudev-queue-private.c delete mode 100644 src/udev/src/libudev-queue.c delete mode 100644 src/udev/src/libudev-selinux-private.c delete mode 100644 src/udev/src/libudev-util-private.c delete mode 100644 src/udev/src/libudev-util.c delete mode 100644 src/udev/src/libudev.c delete mode 100644 src/udev/src/libudev.h delete mode 100644 src/udev/src/libudev.pc.in delete mode 100644 src/udev/src/mtd_probe/75-probe_mtd.rules delete mode 100644 src/udev/src/mtd_probe/mtd_probe.c delete mode 100644 src/udev/src/mtd_probe/mtd_probe.h delete mode 100644 src/udev/src/mtd_probe/probe_smartmedia.c delete mode 100644 src/udev/src/rule_generator/75-cd-aliases-generator.rules delete mode 100644 src/udev/src/rule_generator/75-persistent-net-generator.rules delete mode 100644 src/udev/src/rule_generator/rule_generator.functions delete mode 100644 src/udev/src/rule_generator/write_cd_rules delete mode 100644 src/udev/src/rule_generator/write_net_rules delete mode 100644 src/udev/src/scsi_id/.gitignore delete mode 100644 src/udev/src/scsi_id/README delete mode 100644 src/udev/src/scsi_id/scsi.h delete mode 100644 src/udev/src/scsi_id/scsi_id.8 delete mode 100644 src/udev/src/scsi_id/scsi_id.c delete mode 100644 src/udev/src/scsi_id/scsi_id.h delete mode 100644 src/udev/src/scsi_id/scsi_serial.c delete mode 100644 src/udev/src/sd-daemon.c delete mode 100644 src/udev/src/sd-daemon.h delete mode 100644 src/udev/src/test-libudev.c delete mode 100644 src/udev/src/test-udev.c delete mode 100644 src/udev/src/udev-builtin-blkid.c delete mode 100644 src/udev/src/udev-builtin-firmware.c delete mode 100644 src/udev/src/udev-builtin-hwdb.c delete mode 100644 src/udev/src/udev-builtin-input_id.c delete mode 100644 src/udev/src/udev-builtin-kmod.c delete mode 100644 src/udev/src/udev-builtin-path_id.c delete mode 100644 src/udev/src/udev-builtin-usb_id.c delete mode 100644 src/udev/src/udev-builtin.c delete mode 100644 src/udev/src/udev-control.socket delete mode 100644 src/udev/src/udev-ctrl.c delete mode 100644 src/udev/src/udev-event.c delete mode 100644 src/udev/src/udev-kernel.socket delete mode 100644 src/udev/src/udev-node.c delete mode 100644 src/udev/src/udev-rules.c delete mode 100644 src/udev/src/udev-settle.service.in delete mode 100644 src/udev/src/udev-trigger.service.in delete mode 100644 src/udev/src/udev-watch.c delete mode 100644 src/udev/src/udev.conf delete mode 100644 src/udev/src/udev.h delete mode 100644 src/udev/src/udev.pc.in delete mode 100644 src/udev/src/udev.service.in delete mode 100644 src/udev/src/udev.xml delete mode 100644 src/udev/src/udevadm-control.c delete mode 100644 src/udev/src/udevadm-info.c delete mode 100644 src/udev/src/udevadm-monitor.c delete mode 100644 src/udev/src/udevadm-settle.c delete mode 100644 src/udev/src/udevadm-test-builtin.c delete mode 100644 src/udev/src/udevadm-test.c delete mode 100644 src/udev/src/udevadm-trigger.c delete mode 100644 src/udev/src/udevadm.c delete mode 100644 src/udev/src/udevadm.xml delete mode 100644 src/udev/src/udevd.c delete mode 100644 src/udev/src/udevd.xml delete mode 100644 src/udev/src/v4l_id/60-persistent-v4l.rules delete mode 100644 src/udev/src/v4l_id/v4l_id.c create mode 100644 src/udev/test-libudev.c create mode 100644 src/udev/test-udev.c create mode 100644 src/udev/udev-builtin-blkid.c create mode 100644 src/udev/udev-builtin-firmware.c create mode 100644 src/udev/udev-builtin-hwdb.c create mode 100644 src/udev/udev-builtin-input_id.c create mode 100644 src/udev/udev-builtin-kmod.c create mode 100644 src/udev/udev-builtin-path_id.c create mode 100644 src/udev/udev-builtin-usb_id.c create mode 100644 src/udev/udev-builtin.c create mode 100644 src/udev/udev-ctrl.c create mode 100644 src/udev/udev-event.c create mode 100644 src/udev/udev-node.c create mode 100644 src/udev/udev-rules.c create mode 100644 src/udev/udev-watch.c create mode 100644 src/udev/udev.conf create mode 100644 src/udev/udev.h create mode 100644 src/udev/udev.pc.in create mode 100644 src/udev/udevadm-control.c create mode 100644 src/udev/udevadm-info.c create mode 100644 src/udev/udevadm-monitor.c create mode 100644 src/udev/udevadm-settle.c create mode 100644 src/udev/udevadm-test-builtin.c create mode 100644 src/udev/udevadm-test.c create mode 100644 src/udev/udevadm-trigger.c create mode 100644 src/udev/udevadm.c create mode 100644 src/udev/udevd.c create mode 100644 src/udev/v4l_id/60-persistent-v4l.rules create mode 100644 src/udev/v4l_id/v4l_id.c create mode 100644 units/udev-control.socket create mode 100644 units/udev-kernel.socket create mode 100644 units/udev-settle.service.in create mode 100644 units/udev-trigger.service.in create mode 100644 units/udev.service.in (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index f36dd8a81a..16a6f583b5 100644 --- a/.gitignore +++ b/.gitignore @@ -107,3 +107,14 @@ ltmain.sh *.tar.gz *.tar.bz2 libtool +/accelerometer +/ata_id +/cdrom_id +/collect +/gtk-doc.make +/keymap +/mtd_probe +/scsi_id +/udevadm +/udevd +/v4l_id diff --git a/Makefile.am b/Makefile.am index 219d8ded8e..74e14d2f2a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,7 +1,7 @@ # This file is part of systemd. # -# Copyright 2011 Lennart Poettering -# Copyright 2011 Kay Sievers +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers # # systemd is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -16,9 +16,18 @@ # You should have received a copy of the GNU General Public License # along with systemd; If not, see . -ACLOCAL_AMFLAGS = -I m4 +ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} +AM_MAKEFLAGS = --no-print-directory -SUBDIRS = po +SUBDIRS = . po + +LIBUDEV_CURRENT=13 +LIBUDEV_REVISION=2 +LIBUDEV_AGE=13 + +LIBGUDEV_CURRENT=1 +LIBGUDEV_REVISION=1 +LIBGUDEV_AGE=1 LIBSYSTEMD_LOGIN_CURRENT=2 LIBSYSTEMD_LOGIN_REVISION=1 @@ -41,7 +50,6 @@ dbuspolicydir=@dbuspolicydir@ dbussessionservicedir=@dbussessionservicedir@ dbussystemservicedir=@dbussystemservicedir@ dbusinterfacedir=@dbusinterfacedir@ -udevrulesdir=@udevrulesdir@ pamlibdir=@pamlibdir@ pkgconfigdatadir=$(datadir)/pkgconfig pkgconfiglibdir=$(libdir)/pkgconfig @@ -55,6 +63,7 @@ tmpfilesdir=$(prefix)/lib/tmpfiles.d sysctldir=$(prefix)/lib/sysctl.d usergeneratordir=$(pkglibexecdir)/user-generators pkgincludedir=$(includedir)/systemd +udevlibexecdir=$(rootprefix)/lib/udev # And these are the special ones for / rootprefix=@rootprefix@ @@ -66,17 +75,28 @@ systemunitdir=$(rootprefix)/lib/systemd/system CLEANFILES = EXTRA_DIST = +BUILT_SOURCES = INSTALL_EXEC_HOOKS = UNINSTALL_EXEC_HOOKS = INSTALL_DATA_HOOKS = +DISTCHECK_HOOKS = +DISTCLEAN_LOCAL_HOOKS = pkginclude_HEADERS = lib_LTLIBRARIES = +include_HEADERS = pkgconfiglib_DATA = polkitpolicy_in_files = dist_udevrules_DATA = +nodist_udevrules_DATA = +udevhomedir = $(libexecdir)/udev +udevhome_SCRIPTS = +dist_udevhome_SCRIPTS = +dist_udevhome_DATA = +dist_man_MANS = AM_CPPFLAGS = \ -include $(top_builddir)/config.h \ + -DSYSCONFDIR=\""$(sysconfdir)"\" \ -DSYSTEM_CONFIG_FILE=\"$(pkgsysconfdir)/system.conf\" \ -DSYSTEM_CONFIG_UNIT_PATH=\"$(pkgsysconfdir)/system\" \ -DSYSTEM_DATA_UNIT_PATH=\"$(systemunitdir)\" \ @@ -99,12 +119,14 @@ AM_CPPFLAGS = \ -DUSER_GENERATOR_PATH=\"$(usergeneratordir)\" \ -DSYSTEM_SHUTDOWN_PATH=\"$(systemshutdowndir)\" \ -DSYSTEMD_KBD_MODEL_MAP=\"$(pkgdatadir)/kbd-model-map\" \ - -DX_SERVER=\"$(bindir)/X\" \ + -DX_SERVER=\"$(bindir)/X\" \ + -DUDEVLIBEXECDIR=\""$(libexecdir)/udev"\" \ -I $(top_srcdir)/src \ -I $(top_srcdir)/src/readahead \ -I $(top_srcdir)/src/login \ -I $(top_srcdir)/src/journal \ - -I $(top_srcdir)/src/systemd + -I $(top_srcdir)/src/systemd \ + -I $(top_srcdir)/src/udev AM_CFLAGS = $(WARNINGFLAGS) AM_LDFLAGS = $(GCLDFLAGS) @@ -221,9 +243,6 @@ dist_dbuspolicy_DATA = \ dist_dbussystemservice_DATA = \ src/org.freedesktop.systemd1.service -nodist_udevrules_DATA = \ - src/99-systemd.rules - dbusinterface_DATA = \ org.freedesktop.systemd1.Manager.xml \ org.freedesktop.systemd1.Job.xml \ @@ -299,7 +318,9 @@ dist_systemunit_DATA = \ units/quotaon.service \ units/systemd-ask-password-wall.path \ units/systemd-ask-password-console.path \ - units/syslog.target + units/syslog.target \ + units/udev-control.socket \ + units/udev-kernel.socket nodist_systemunit_DATA = \ units/getty@.service \ @@ -323,7 +344,10 @@ nodist_systemunit_DATA = \ units/fsck@.service \ units/fsck-root.service \ units/rescue.service \ - units/user@.service + units/user@.service \ + units/udev.service \ + units/udev-trigger.service \ + units/udev-settle.service dist_userunit_DATA = \ units/user/default.target \ @@ -356,9 +380,11 @@ EXTRA_DIST += \ units/fsck@.service.in \ units/fsck-root.service.in \ units/user@.service.in \ + units/udev.service \ + units/udev-trigger.service \ + units/udev-settle.service \ src/systemd.pc.in \ introspect.awk \ - src/99-systemd.rules.in \ man/custom-html.xsl if TARGET_FEDORA @@ -422,7 +448,7 @@ endif dist_doc_DATA = \ README \ - NEWS \ + NEWS \ LICENSE \ DISTRO_PORTING @@ -535,7 +561,6 @@ EXTRA_DIST += \ libsystemd_core_la_CFLAGS = \ $(AM_CFLAGS) \ $(DBUS_CFLAGS) \ - $(UDEV_CFLAGS) \ $(LIBWRAP_CFLAGS) \ $(PAM_CFLAGS) \ $(AUDIT_CFLAGS) \ @@ -543,8 +568,8 @@ libsystemd_core_la_CFLAGS = \ libsystemd_core_la_LIBADD = \ libsystemd-basic.la \ + libudev.la \ $(DBUS_LIBS) \ - $(UDEV_LIBS) \ $(LIBWRAP_LIBS) \ $(PAM_LIBS) \ $(AUDIT_LIBS) \ @@ -642,9 +667,9 @@ EXTRA_DIST += \ src/spawn-agent.h \ src/acl-util.h \ src/logs-show.h \ - src/utf8.h \ - src/journal/sparse-endian.h \ - src/ima-setup.h + src/utf8.h \ + src/journal/sparse-endian.h \ + src/ima-setup.h MANPAGES = \ man/systemd.1 \ @@ -687,7 +712,10 @@ MANPAGES = \ man/systemd-cat.1 \ man/systemd-machine-id-setup.1 \ man/journald.conf.5 \ - man/journalctl.1 + man/journalctl.1 \ + man/udev.7 \ + man/udevadm.8 \ + man/udevd.8 MANPAGES_ALIAS = \ man/reboot.8 \ @@ -721,8 +749,7 @@ systemd_SOURCES = \ systemd_CFLAGS = \ $(AM_CFLAGS) \ - $(DBUS_CFLAGS) \ - $(UDEV_CFLAGS) + $(DBUS_CFLAGS) systemd_LDADD = \ libsystemd-core.la @@ -843,13 +870,9 @@ systemd_shutdown_SOURCES = \ src/umount.c \ src/shutdown.c -systemd_shutdown_CFLAGS = \ - $(AM_CFLAGS) \ - $(UDEV_CFLAGS) - systemd_shutdown_LDADD = \ libsystemd-basic.la \ - $(UDEV_LIBS) + libudev.la systemd_modules_load_SOURCES = \ src/modules-load.c @@ -887,12 +910,11 @@ systemd_fsck_SOURCES = \ systemd_fsck_CFLAGS = \ $(AM_CFLAGS) \ - $(UDEV_CFLAGS) \ $(DBUS_CFLAGS) systemd_fsck_LDADD = \ libsystemd-basic.la \ - $(UDEV_LIBS) \ + libudev.la \ $(DBUS_LIBS) systemd_timestamp_SOURCES = \ @@ -904,13 +926,9 @@ systemd_timestamp_LDADD = \ systemd_ac_power_SOURCES = \ src/ac-power.c -systemd_ac_power_CFLAGS = \ - $(AM_CFLAGS) \ - $(UDEV_CFLAGS) - systemd_ac_power_LDADD = \ libsystemd-basic.la \ - $(UDEV_LIBS) + libudev.la systemd_detect_virt_SOURCES = \ src/detect-virt.c @@ -1102,6 +1120,620 @@ EXTRA_DIST += \ src/libsystemd-daemon.pc.in \ src/libsystemd-daemon.sym +# ------------------------------------------------------------------------------ +SUBDIRS += \ + src/udev/docs + +include_HEADERS += \ + src/udev/libudev.h + +lib_LTLIBRARIES += \ + libudev.la + +noinst_LTLIBRARIES += \ + libudev-private.la + +libudev_la_SOURCES =\ + src/udev/libudev-private.h \ + src/udev/libudev.c \ + src/udev/libudev-list.c \ + src/udev/libudev-util.c \ + src/udev/libudev-device.c \ + src/udev/libudev-enumerate.c \ + src/udev/libudev-monitor.c \ + src/udev/libudev-queue.c + +libudev_la_LDFLAGS = \ + $(AM_LDFLAGS) \ + -version-info $(LIBUDEV_CURRENT):$(LIBUDEV_REVISION):$(LIBUDEV_AGE) + +libudev_private_la_SOURCES =\ + $(libudev_la_SOURCES) \ + src/udev/libudev-util-private.c \ + src/udev/libudev-device-private.c \ + src/udev/libudev-queue-private.c \ + src/udev/libudev-selinux-private.c + +libudev_private_la_LIBADD = \ + $(SELINUX_LIBS) + +pkgconfiglib_DATA += \ + src/udev/libudev.pc + +EXTRA_DIST += \ + src/udev/libudev.pc.in + +CLEANFILES += \ + src/udev/libudev.pc + +# move lib from $(libdir) to $(rootlibdir) and update devel link, if needed +libudev-install-move-hook: + if test "$(libdir)" != "$(rootlibdir)"; then \ + mkdir -p $(DESTDIR)$(rootlibdir) && \ + so_img_name=$$(readlink $(DESTDIR)$(libdir)/libudev.so) && \ + so_img_rel_target_prefix=$$(echo $(libdir) | sed 's,\(^/\|\)[^/][^/]*,..,g') && \ + ln -sf $$so_img_rel_target_prefix$(rootlibdir)/$$so_img_name $(DESTDIR)$(libdir)/libudev.so && \ + mv $(DESTDIR)$(libdir)/libudev.so.* $(DESTDIR)$(rootlibdir); \ + fi + +libudev-uninstall-move-hook: + rm -f $(DESTDIR)$(rootlibdir)/libudev.so* + +INSTALL_EXEC_HOOKS += libudev-install-move-hook +UNINSTALL_EXEC_HOOKS += libudev-uninstall-move-hook + +# ------------------------------------------------------------------------------ +udev-confdirs: + -mkdir -p $(DESTDIR)$(sysconfdir)/udev/rules.d + -mkdir -p $(DESTDIR)$(libexecdir)/udev/devices + +INSTALL_DATA_HOOKS += udev-confdirs + +udevrulesdir = $(libexecdir)/udev/rules.d +dist_udevrules_DATA += \ + rules/99-systemd.rules \ + rules/42-usb-hid-pm.rules \ + rules/50-udev-default.rules \ + rules/60-persistent-storage-tape.rules \ + rules/60-persistent-serial.rules \ + rules/60-persistent-input.rules \ + rules/60-persistent-alsa.rules \ + rules/60-persistent-storage.rules \ + rules/75-net-description.rules \ + rules/75-tty-description.rules \ + rules/78-sound-card.rules \ + rules/80-drivers.rules \ + rules/95-udev-late.rules + +udevconfdir = $(sysconfdir)/udev +dist_udevconf_DATA = \ + src/udev/udev.conf + +sharepkgconfigdir = $(datadir)/pkgconfig +sharepkgconfig_DATA = \ + src/udev/udev.pc + +EXTRA_DIST += \ + rules/99-systemd.rules.in \ + src/udev/udev.pc.in + +CLEANFILES += \ + rules/99-systemd.rules \ + src/udev/udev.pc + +EXTRA_DIST += \ + units/udev.service.in \ + units/udev-trigger.service.in \ + units/udev-settle.service.in + +CLEANFILES += \ + units/udev.service \ + units/udev-trigger.service \ + units/udev-settle.service + +systemd-install-hook: + mkdir -p $(DESTDIR)$(systemunitdir)/sockets.target.wants + ln -sf ../udev-control.socket $(DESTDIR)$(systemunitdir)/sockets.target.wants/udev-control.socket + ln -sf ../udev-kernel.socket $(DESTDIR)$(systemunitdir)/sockets.target.wants/udev-kernel.socket + mkdir -p $(DESTDIR)$(systemunitdir)/basic.target.wants + ln -sf ../udev.service $(DESTDIR)$(systemunitdir)/basic.target.wants/udev.service + ln -sf ../udev-trigger.service $(DESTDIR)$(systemunitdir)/basic.target.wants/udev-trigger.service + +INSTALL_DATA_HOOKS += systemd-install-hook + +bin_PROGRAMS += \ + udevadm + +udevlibexec_PROGRAMS = \ + udevd + +udev_common_sources = \ + src/udev/udev.h \ + src/udev/udev-event.c \ + src/udev/udev-watch.c \ + src/udev/udev-node.c \ + src/udev/udev-rules.c \ + src/udev/udev-ctrl.c \ + src/udev/udev-builtin.c \ + src/udev/udev-builtin-blkid.c \ + src/udev/udev-builtin-firmware.c \ + src/udev/udev-builtin-hwdb.c \ + src/udev/udev-builtin-input_id.c \ + src/udev/udev-builtin-kmod.c \ + src/udev/udev-builtin-path_id.c \ + src/udev/udev-builtin-usb_id.c + +udev_common_CFLAGS = \ + $(BLKID_CFLAGS) \ + $(KMOD_CFLAGS) + +udev_common_LDADD = \ + libudev-private.la \ + $(BLKID_LIBS) \ + $(KMOD_LIBS) + +udev_common_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -DFIRMWARE_PATH="$(FIRMWARE_PATH)" \ + -DUSB_DATABASE=\"$(USB_DATABASE)\" -DPCI_DATABASE=\"$(PCI_DATABASE)\" + +udevd_SOURCES = \ + $(udev_common_sources) \ + src/udev/udevd.c \ + src/systemd/sd-daemon.h \ + src/sd-daemon.c + +udevd_CFLAGS = \ + $(udev_common_CFLAGS) + +udevd_LDADD = \ + $(udev_common_LDADD) + +udevd_CPPFLAGS = \ + $(udev_common_CPPFLAGS) + +udevadm_SOURCES = \ + $(udev_common_sources) \ + src/udev/udevadm.c \ + src/udev/udevadm-info.c \ + src/udev/udevadm-control.c \ + src/udev/udevadm-monitor.c \ + src/udev/udevadm-settle.c \ + src/udev/udevadm-trigger.c \ + src/udev/udevadm-test.c \ + src/udev/udevadm-test-builtin.c + +udevadm_CFLAGS = \ + $(udev_common_CFLAGS) + +udevadm_LDADD = \ + $(udev_common_LDADD) + +udevadm_CPPFLAGS = \ + $(udev_common_CPPFLAGS) + +# ------------------------------------------------------------------------------ +TESTS = \ + src/udev/test/udev-test.pl \ + src/udev/test/rules-test.sh + +check_PROGRAMS = \ + test-libudev \ + test-udev + +test_libudev_SOURCES = \ + src/udev/test-libudev.c + +test_libudev_LDADD = \ + libudev.la + +test_udev_SOURCES = \ + $(udev_common_sources) \ + src/udev/test-udev.c + +test_udev_CFLAGS = \ + $(udev_common_CFLAGS) + +test_udev_LDADD = \ + $(udev_common_LDADD) + +test_udev_CPPFLAGS = \ + $(udev_common_CPPFLAGS) + +test_udev_DEPENDENCIES = \ + src/udev/test/sys + +# packed sysfs test tree +src/udev/test/sys: + $(AM_V_GEN)mkdir -p src/udev/test && tar -C src/udev/test/ -xJf $(top_srcdir)/src/udev/test/sys.tar.xz + +test-sys-distclean: + -rm -rf src/udev/test/sys +DISTCLEAN_LOCAL_HOOKS += test-sys-distclean + +EXTRA_DIST += \ + src/udev/test/sys.tar.xz \ + $(TESTS) \ + src/udev/test/rule-syntax-check.py + +# ------------------------------------------------------------------------------ +ata_id_SOURCES = \ + src/udev/ata_id/ata_id.c + +ata_id_LDADD = \ + libudev-private.la + +udevlibexec_PROGRAMS += \ + ata_id + +# ------------------------------------------------------------------------------ +cdrom_id_SOURCES = \ + src/udev/cdrom_id/cdrom_id.c + +cdrom_id_LDADD = \ + libudev-private.la + +udevlibexec_PROGRAMS += \ + cdrom_id + +dist_udevrules_DATA += \ + src/udev/cdrom_id/60-cdrom_id.rules + +# ------------------------------------------------------------------------------ +collect_SOURCES = \ + src/udev/collect/collect.c + +collect_LDADD = \ + libudev-private.la + +udevlibexec_PROGRAMS += \ + collect + +# ------------------------------------------------------------------------------ +scsi_id_SOURCES =\ + src/udev/scsi_id/scsi_id.c \ + src/udev/scsi_id/scsi_serial.c \ + src/udev/scsi_id/scsi.h \ + src/udev/scsi_id/scsi_id.h + +scsi_id_LDADD = \ + libudev-private.la + +udevlibexec_PROGRAMS += \ + scsi_id + +EXTRA_DIST += \ + src/udev/scsi_id/README + +# ------------------------------------------------------------------------------ +v4l_id_SOURCES = \ + src/udev/v4l_id/v4l_id.c + +v4l_id_LDADD = \ + libudev-private.la + +udevlibexec_PROGRAMS += \ + v4l_id + +dist_udevrules_DATA += \ + src/udev/v4l_id/60-persistent-v4l.rules + +# ------------------------------------------------------------------------------ +accelerometer_SOURCES = \ + src/udev/accelerometer/accelerometer.c + +accelerometer_LDADD = \ + libudev-private.la -lm + +udevlibexec_PROGRAMS += \ + accelerometer + +dist_udevrules_DATA += \ + src/udev/accelerometer/61-accelerometer.rules + +# ------------------------------------------------------------------------------ +if ENABLE_GUDEV +SUBDIRS += \ + src/udev/gudev/docs + +libgudev_includedir = \ + $(includedir)/gudev-1.0/gudev + +libgudev_include_HEADERS = \ + src/udev/gudev/gudev.h \ + src/udev/gudev/gudevenums.h \ + src/udev/gudev/gudevenumtypes.h \ + src/udev/gudev/gudevtypes.h \ + src/udev/gudev/gudevclient.h \ + src/udev/gudev/gudevdevice.h \ + src/udev/gudev/gudevenumerator.h + +lib_LTLIBRARIES += libgudev-1.0.la + +pkgconfiglib_DATA += \ + src/udev/gudev/gudev-1.0.pc + +EXTRA_DIST += \ + src/udev/gudev/gudev-1.0.pc.in + +CLEANFILES += \ + src/udev/gudev/gudev-1.0.pc + +libgudev_1_0_la_SOURCES = \ + src/udev/gudev/gudevenums.h \ + src/udev/gudev/gudevenumtypes.h \ + src/udev/gudev/gudevenumtypes.h\ + src/udev/gudev/gudevtypes.h \ + src/udev/gudev/gudevclient.h \ + src/udev/gudev/gudevclient.c \ + src/udev/gudev/gudevdevice.h \ + src/udev/gudev/gudevdevice.c \ + src/udev/gudev/gudevenumerator.h \ + src/udev/gudev/gudevenumerator.c \ + src/udev/gudev/gudevprivate.h + +nodist_libgudev_1_0_la_SOURCES = \ + src/udev/gudev/gudevmarshal.h \ + src/udev/gudev/gudevmarshal.c \ + src/udev/gudev/gudevenumtypes.h \ + src/udev/gudev/gudevenumtypes.c + +BUILT_SOURCES += \ + $(nodist_libgudev_1_0_la_SOURCES) + +libgudev_1_0_la_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -I$(top_builddir)/src\ + -I$(top_srcdir)/src\ + -I$(top_builddir)/src/udev/gudev \ + -I$(top_srcdir)/src/udev/gudev \ + -D_POSIX_PTHREAD_SEMANTICS -D_REENTRANT \ + -D_GUDEV_COMPILATION \ + -DG_LOG_DOMAIN=\"GUdev\" + +libgudev_1_0_la_CFLAGS = \ + -fvisibility=default \ + $(GLIB_CFLAGS) + +libgudev_1_0_la_LIBADD = \ + libudev.la \ + $(GLIB_LIBS) + +libgudev_1_0_la_LDFLAGS = \ + -version-info $(LIBGUDEV_CURRENT):$(LIBGUDEV_REVISION):$(LIBGUDEV_AGE) \ + -export-dynamic -no-undefined \ + -export-symbols-regex '^g_udev_.*' + +EXTRA_DIST += \ + src/udev/gudev/gudevmarshal.list \ + src/udev/gudev/gudevenumtypes.h.template \ + src/udev/gudev/gudevenumtypes.c.template \ + src/udev/gudev/gjs-example.js \ + src/udev/gudev/seed-example-enum.js \ + src/udev/gudev/seed-example.js + +CLEANFILES += \ + $(nodist_libgudev_1_0_la_SOURCES) + +src/udev/gudev/gudevmarshal.h: src/udev/gudev/gudevmarshal.list + $(AM_V_GEN)glib-genmarshal $< --prefix=g_udev_marshal --header > $@ + +src/udev/gudev/gudevmarshal.c: src/udev/gudev/gudevmarshal.list + $(AM_V_GEN)echo "#include \"gudevmarshal.h\"" > $@ && \ + glib-genmarshal $< --prefix=g_udev_marshal --body >> $@ + +src/udev/gudev/gudevenumtypes.h: src/udev/gudev/gudevenumtypes.h.template src/udev/gudev/gudevenums.h + $(AM_V_GEN)glib-mkenums --template $^ > \ + $@.tmp && mv $@.tmp $@ + +src/udev/gudev/gudevenumtypes.c: src/udev/gudev/gudevenumtypes.c.template src/udev/gudev/gudevenums.h + $(AM_V_GEN)glib-mkenums --template $^ > \ + $@.tmp && mv $@.tmp $@ + +if ENABLE_INTROSPECTION +src/udev/gudev/GUdev-1.0.gir: libgudev-1.0.la $(G_IR_SCANNER) + $(AM_V_GEN)$(G_IR_SCANNER) -v \ + --warn-all \ + --namespace GUdev \ + --nsversion=1.0 \ + --include=GObject-2.0 \ + --library=gudev-1.0 \ + --library-path=$(top_builddir)/src/udev \ + --library-path=$(top_builddir)/src/udev/gudev \ + --output $@ \ + --pkg=glib-2.0 \ + --pkg=gobject-2.0 \ + --pkg-export=gudev-1.0 \ + --c-include=gudev/gudev.h \ + -I$(top_srcdir)/src/udev \ + -I$(top_builddir)/src/udev \ + -D_GUDEV_COMPILATION \ + -D_GUDEV_WORK_AROUND_DEV_T_BUG \ + $(top_srcdir)/src/udev/gudev/gudev.h \ + $(top_srcdir)/src/udev/gudev/gudevtypes.h \ + $(top_srcdir)/src/udev/gudev/gudevenums.h \ + $(or $(wildcard $(top_builddir)/src/udev/gudev/gudevenumtypes.h),$(top_srcdir)/src/udev/gudev/gudevenumtypes.h) \ + $(top_srcdir)/src/udev/gudev/gudevclient.h \ + $(top_srcdir)/src/udev/gudev/gudevdevice.h \ + $(top_srcdir)/src/udev/gudev/gudevenumerator.h \ + $(top_srcdir)/src/udev/gudev/gudevclient.c \ + $(top_srcdir)/src/udev/gudev/gudevdevice.c \ + $(top_srcdir)/src/udev/gudev/gudevenumerator.c + +src/udev/gudev/GUdev-1.0.typelib: src/udev/gudev/GUdev-1.0.gir $(G_IR_COMPILER) + $(AM_V_GEN)g-ir-compiler $< -o $@ + +girdir = $(GIRDIR) +gir_DATA = \ + src/udev/gudev/GUdev-1.0.gir + +typelibsdir = $(GIRTYPELIBDIR) +typelibs_DATA = \ + src/udev/gudev/GUdev-1.0.typelib + +CLEANFILES += $(gir_DATA) $(typelibs_DATA) +endif # ENABLE_INTROSPECTION + +# move lib from $(libdir) to $(rootlibdir) and update devel link, if needed +libgudev-install-move-hook: + if test "$(libdir)" != "$(rootlibdir)"; then \ + mkdir -p $(DESTDIR)$(rootlibdir) && \ + so_img_name=$$(readlink $(DESTDIR)$(libdir)/libgudev-1.0.so) && \ + so_img_rel_target_prefix=$$(echo $(libdir) | sed 's,\(^/\|\)[^/][^/]*,..,g') && \ + ln -sf $$so_img_rel_target_prefix$(rootlibdir)/$$so_img_name $(DESTDIR)$(libdir)/libgudev-1.0.so && \ + mv $(DESTDIR)$(libdir)/libgudev-1.0.so.* $(DESTDIR)$(rootlibdir); \ + fi + +libgudev-uninstall-move-hook: + rm -f $(DESTDIR)$(rootlibdir)/libgudev-1.0.so* + +INSTALL_EXEC_HOOKS += libgudev-install-move-hook +UNINSTALL_EXEC_HOOKS += libgudev-uninstall-move-hook +endif + +# ------------------------------------------------------------------------------ +if ENABLE_KEYMAP +keymap_SOURCES = \ + src/udev/keymap/keymap.c + +keymap_CPPFLAGS = \ + $(AM_CPPFLAGS) -I src/udev/keymap + +nodist_keymap_SOURCES = \ + src/udev/keymap/keys-from-name.h \ + src/udev/keymap/keys-to-name.h + +BUILT_SOURCES += \ + $(nodist_keymap_SOURCES) + +udevlibexec_PROGRAMS += \ + keymap + +dist_doc_DATA += \ + src/udev/keymap/README.keymap.txt + +dist_udevrules_DATA += \ + src/udev/keymap/95-keymap.rules \ + src/udev/keymap/95-keyboard-force-release.rules + +dist_udevhome_SCRIPTS += \ + src/udev/keymap/findkeyboards + +udevhome_SCRIPTS += \ + src/udev/keymap/keyboard-force-release.sh + +EXTRA_DIST += \ + src/udev/keymap/check-keymaps.sh \ + src/udev/keymap/keyboard-force-release.sh.in + +CLEANFILES += \ + $(nodist_keymap_SOURCES) \ + src/udev/keymap/keys.txt \ + src/udev/keymap/keys-from-name.gperf \ + src/udev/keymap/keyboard-force-release.sh + +udevkeymapdir = $(libexecdir)/udev/keymaps +dist_udevkeymap_DATA = \ + src/udev/keymap/keymaps/acer \ + src/udev/keymap/keymaps/acer-aspire_5720 \ + src/udev/keymap/keymaps/acer-aspire_8930 \ + src/udev/keymap/keymaps/acer-aspire_5920g \ + src/udev/keymap/keymaps/acer-aspire_6920 \ + src/udev/keymap/keymaps/acer-travelmate_c300 \ + src/udev/keymap/keymaps/asus \ + src/udev/keymap/keymaps/compaq-e_evo \ + src/udev/keymap/keymaps/dell \ + src/udev/keymap/keymaps/dell-latitude-xt2 \ + src/udev/keymap/keymaps/everex-xt5000 \ + src/udev/keymap/keymaps/fujitsu-amilo_li_2732 \ + src/udev/keymap/keymaps/fujitsu-amilo_pa_2548 \ + src/udev/keymap/keymaps/fujitsu-amilo_pro_edition_v3505 \ + src/udev/keymap/keymaps/fujitsu-amilo_pro_v3205 \ + src/udev/keymap/keymaps/fujitsu-amilo_si_1520 \ + src/udev/keymap/keymaps/fujitsu-esprimo_mobile_v5 \ + src/udev/keymap/keymaps/fujitsu-esprimo_mobile_v6 \ + src/udev/keymap/keymaps/genius-slimstar-320 \ + src/udev/keymap/keymaps/hewlett-packard \ + src/udev/keymap/keymaps/hewlett-packard-2510p_2530p \ + src/udev/keymap/keymaps/hewlett-packard-compaq_elitebook \ + src/udev/keymap/keymaps/hewlett-packard-pavilion \ + src/udev/keymap/keymaps/hewlett-packard-presario-2100 \ + src/udev/keymap/keymaps/hewlett-packard-tablet \ + src/udev/keymap/keymaps/hewlett-packard-tx2 \ + src/udev/keymap/keymaps/ibm-thinkpad-usb-keyboard-trackpoint \ + src/udev/keymap/keymaps/inventec-symphony_6.0_7.0 \ + src/udev/keymap/keymaps/lenovo-3000 \ + src/udev/keymap/keymaps/lenovo-ideapad \ + src/udev/keymap/keymaps/lenovo-thinkpad-usb-keyboard-trackpoint \ + src/udev/keymap/keymaps/lenovo-thinkpad_x6_tablet \ + src/udev/keymap/keymaps/lenovo-thinkpad_x200_tablet \ + src/udev/keymap/keymaps/lg-x110 \ + src/udev/keymap/keymaps/logitech-wave \ + src/udev/keymap/keymaps/logitech-wave-cordless \ + src/udev/keymap/keymaps/logitech-wave-pro-cordless \ + src/udev/keymap/keymaps/maxdata-pro_7000 \ + src/udev/keymap/keymaps/medion-fid2060 \ + src/udev/keymap/keymaps/medionnb-a555 \ + src/udev/keymap/keymaps/micro-star \ + src/udev/keymap/keymaps/module-asus-w3j \ + src/udev/keymap/keymaps/module-ibm \ + src/udev/keymap/keymaps/module-lenovo \ + src/udev/keymap/keymaps/module-sony \ + src/udev/keymap/keymaps/module-sony-old \ + src/udev/keymap/keymaps/module-sony-vgn \ + src/udev/keymap/keymaps/olpc-xo \ + src/udev/keymap/keymaps/onkyo \ + src/udev/keymap/keymaps/oqo-model2 \ + src/udev/keymap/keymaps/samsung-other \ + src/udev/keymap/keymaps/samsung-90x3a \ + src/udev/keymap/keymaps/samsung-sq1us \ + src/udev/keymap/keymaps/samsung-sx20s \ + src/udev/keymap/keymaps/toshiba-satellite_a100 \ + src/udev/keymap/keymaps/toshiba-satellite_a110 \ + src/udev/keymap/keymaps/toshiba-satellite_m30x \ + src/udev/keymap/keymaps/zepto-znote + +udevkeymapforcereldir = $(libexecdir)/udev/keymaps/force-release +dist_udevkeymapforcerel_DATA = \ + src/udev/keymap/force-release-maps/dell-touchpad \ + src/udev/keymap/force-release-maps/hp-other \ + src/udev/keymap/force-release-maps/samsung-other \ + src/udev/keymap/force-release-maps/samsung-90x3a \ + src/udev/keymap/force-release-maps/common-volume-keys + +src/udev/keymap/keys.txt: $(INCLUDE_PREFIX)/linux/input.h + $(AM_V_at)mkdir -p src/keymap + $(AM_V_GEN)$(AWK) '/^#define.*KEY_[^ ]+[ \t]+[0-9]/ { if ($$2 != "KEY_MAX") { print $$2 } }' < $< | sed 's/^KEY_COFFEE$$/KEY_SCREENLOCK/' > $@ + +src/udev/keymap/keys-from-name.gperf: src/udev/keymap/keys.txt + $(AM_V_GEN)$(AWK) 'BEGIN{ print "struct key { const char* name; unsigned short id; };"; print "%null-strings"; print "%%";} { print $$1 ", " $$1 }' < $< > $@ + +src/udev/keymap/keys-from-name.h: src/udev/keymap/keys-from-name.gperf Makefile + $(AM_V_GEN)$(GPERF) -L ANSI-C -t --ignore-case -N lookup_key -H hash_key_name -p -C < $< > $@ + +src/udev/keymap/keys-to-name.h: src/udev/keymap/keys.txt Makefile + $(AM_V_GEN)$(AWK) 'BEGIN{ print "const char* const key_names[KEY_CNT] = { "} { print "[" $$1 "] = \"" $$1 "\"," } END{print "};"}' < $< > $@ + +keymaps-distcheck-hook: src/udev/keymap/keys.txt + $(top_srcdir)/src/udev/keymap/check-keymaps.sh $(top_srcdir) $^ +DISTCHECK_HOOKS += keymaps-distcheck-hook +endif + +# ------------------------------------------------------------------------------ +mtd_probe_SOURCES = \ + src/udev/mtd_probe/mtd_probe.c \ + src/udev/mtd_probe/mtd_probe.h \ + src/udev/mtd_probe/probe_smartmedia.c + +mtd_probe_CPPFLAGS = \ + $(AM_CPPFLAGS) + +dist_udevrules_DATA += \ + src/udev/mtd_probe/75-probe_mtd.rules + +udevlibexec_PROGRAMS += \ + mtd_probe + # ------------------------------------------------------------------------------ libsystemd_id128_la_SOURCES = \ src/sd-id128.c @@ -1467,24 +2099,16 @@ systemd_readahead_collect_SOURCES = \ systemd_readahead_collect_LDADD = \ libsystemd-basic.la \ libsystemd-daemon.la \ - $(UDEV_LIBS) - -systemd_readahead_collect_CFLAGS = \ - $(AM_CFLAGS) \ - $(UDEV_CFLAGS) + libudev.la systemd_readahead_replay_SOURCES = \ src/readahead/readahead-replay.c \ src/readahead/readahead-common.c -systemd_readahead_replay_CFLAGS = \ - $(AM_CFLAGS) \ - $(UDEV_CFLAGS) - systemd_readahead_replay_LDADD = \ libsystemd-basic.la \ libsystemd-daemon.la \ - $(UDEV_LIBS) + libudev.la rootlibexec_PROGRAMS += \ systemd-readahead-collect \ @@ -1579,12 +2203,11 @@ systemd_cryptsetup_SOURCES = \ systemd_cryptsetup_CFLAGS = \ $(AM_CFLAGS) \ - $(LIBCRYPTSETUP_CFLAGS) \ - $(UDEV_CFLAGS) + $(LIBCRYPTSETUP_CFLAGS) systemd_cryptsetup_LDADD = \ $(LIBCRYPTSETUP_LIBS) \ - $(UDEV_LIBS) \ + libudev.la \ libsystemd-basic.la systemd_cryptsetup_generator_SOURCES = \ @@ -1797,14 +2420,13 @@ endif systemd_logind_CFLAGS = \ $(AM_CFLAGS) \ $(DBUS_CFLAGS) \ - $(UDEV_CFLAGS) \ $(ACL_CFLAGS) systemd_logind_LDADD = \ libsystemd-basic.la \ libsystemd-daemon.la \ + libudev.la \ $(DBUS_LIBS) \ - $(UDEV_LIBS) \ $(ACL_LIBS) systemd_user_sessions_SOURCES = \ @@ -1828,13 +2450,12 @@ loginctl_SOURCES = \ loginctl_CFLAGS = \ $(AM_CFLAGS) \ - $(DBUS_CFLAGS) \ - $(UDEV_CFLAGS) + $(DBUS_CFLAGS) loginctl_LDADD = \ libsystemd-basic.la \ - $(DBUS_LIBS) \ - $(UDEV_LIBS) + libudev.la \ + $(DBUS_LIBS) rootbin_PROGRAMS += \ loginctl @@ -1955,13 +2576,9 @@ INSTALL_DATA_HOOKS += \ systemd_multi_seat_x_SOURCES = \ src/login/multi-seat-x.c -systemd_multi_seat_x_CFLAGS = \ - $(AM_CFLAGS) \ - $(UDEV_CFLAGS) - systemd_multi_seat_x_LDADD = \ libsystemd-basic.la \ - $(UDEV_LIBS) + libudev.la rootlibexec_PROGRAMS += \ systemd-multi-seat-x @@ -1977,14 +2594,13 @@ endif systemd_uaccess_CFLAGS = \ $(AM_CFLAGS) \ - $(UDEV_CFLAGS) \ $(ACL_CFLAGS) systemd_uaccess_LDADD = \ libsystemd-basic.la \ libsystemd-daemon.la \ libsystemd-login.la \ - $(UDEV_LIBS) \ + libudev.la \ $(ACL_LIBS) rootlibexec_PROGRAMS += \ @@ -2088,6 +2704,9 @@ SED_PROCESS = \ -e 's,@exec_prefix\@,$(exec_prefix),g' \ -e 's,@libdir\@,$(libdir),g' \ -e 's,@includedir\@,$(includedir),g' \ + -e 's,@VERSION\@,$(VERSION),g' \ + -e 's,@rootprefix\@,$(rootprefix),g' \ + -e 's,@udevlibexecdir\@,$(libexecdir)/udev,g' \ < $< > $@ || rm $@ units/%: units/%.in Makefile @@ -2105,9 +2724,13 @@ sysctl.d/%: sysctl.d/%.in Makefile src/%.policy.in: src/%.policy.in.in Makefile $(SED_PROCESS) -src/%.rules: src/%.rules.in Makefile +%.rules: %.rules.in Makefile $(SED_PROCESS) +%.sh: %.sh.in Makefile + $(SED_PROCESS) + $(AM_V_GEN)chmod +x $@ + src/%.c: src/%.gperf $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \ $(GPERF) < $< > $@ @@ -2143,8 +2766,7 @@ CLEANFILES += \ $(nodist_polkitpolicy_DATA) \ src/load-fragment-gperf.gperf \ src/load-fragment-gperf.c \ - src/load-fragment-gperf-nulstr.c \ - src/99-systemd.rules + src/load-fragment-gperf-nulstr.c if HAVE_XSLTPROC XSLTPROC_FLAGS = \ @@ -2424,15 +3046,19 @@ uninstall-hook: $(UNINSTALL_EXEC_HOOKS) install-data-hook: systemd-install-data-hook $(INSTALL_DATA_HOOKS) +distcheck-hook: $(DISTCHECK_HOOKS) + +distclean-local: $(DISTCLEAN_LOCAL_HOOKS) + DISTCHECK_CONFIGURE_FLAGS = \ --with-dbuspolicydir=$$dc_install_base/$(dbuspolicydir) \ --with-dbussessionservicedir=$$dc_install_base/$(dbussessionservicedir) \ --with-dbussystemservicedir=$$dc_install_base/$(dbussystemservicedir) \ --with-dbusinterfacedir=$$dc_install_base/$(dbusinterfacedir) \ - --with-udevrulesdir=$$dc_install_base/$(udevrulesdir) \ --with-pamlibdir=$$dc_install_base/$(pamlibdir) \ - --with-rootprefix=$$dc_install_base \ - --disable-split-usr + --with-rootprefix=$$dc_install_base \ + --disable-split-usr \ + --enable-gtk-doc upload: all distcheck cp -v systemd-$(VERSION).tar.xz /home/lennart/git.fedora/systemd/ diff --git a/TODO b/TODO index 4f3b157040..c12190e58c 100644 --- a/TODO +++ b/TODO @@ -342,3 +342,28 @@ Regularly: * pahole * set_put(), hashmap_put() return values check. i.e. == 0 doesn't free()! + +udev: + - find a way to tell udev to not cancel firmware + requests in initramfs + + - scsi_id -> sg3_utils? + + - make gtk-doc optional like kmod + + - move /usr/lib/udev/devices/ to tmpfiles + + - trigger --subsystem-match=usb/usb_device + + - kill rules_generator + + - have a $attrs{} ? + + - remove RUN+="socket:" + + - libudev.so.1 + - symbol versioning + - return object with *_unref() + - udev_monitor_from_socket() + - udev_queue_get_failed_list_entry() + diff --git a/autogen.sh b/autogen.sh index 9ca53772a4..fba3dc08b8 100755 --- a/autogen.sh +++ b/autogen.sh @@ -21,6 +21,7 @@ if [ -f .git/hooks/pre-commit.sample -a ! -f .git/hooks/pre-commit ] ; then echo "Activated pre-commit hook." fi +gtkdocize intltoolize --force --automake autoreconf --force --install --symlink @@ -32,7 +33,8 @@ args="\ --sysconfdir=/etc \ --localstatedir=/var \ --libdir=$(libdir /usr/lib) \ ---libexecdir=/usr/lib" +--libexecdir=/usr/lib \ +--enable-gtk-doc" if [ ! -L /bin ]; then args="$args \ diff --git a/configure.ac b/configure.ac index 9a9a789235..9baebef1cf 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,7 @@ # This file is part of systemd. # -# Copyright 2010 Lennart Poettering +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers # # systemd is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -18,6 +19,7 @@ AC_PREREQ(2.63) AC_INIT([systemd],[44],[systemd-devel@lists.freedesktop.org]) +AC_SUBST(PACKAGE_URL, [http://www.freedesktop.org/wiki/Software/systemd]) AC_CONFIG_SRCDIR([src/main.c]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_HEADERS([config.h]) @@ -25,16 +27,15 @@ AC_USE_SYSTEM_EXTENSIONS AC_SYS_LARGEFILE AC_PREFIX_DEFAULT([/usr]) AM_INIT_AUTOMAKE([foreign 1.11 -Wall -Wno-portability silent-rules tar-pax no-dist-gzip dist-xz subdir-objects check-news]) - -AC_SUBST(PACKAGE_URL, [http://www.freedesktop.org/wiki/Software/systemd]) - +AM_SILENT_RULES([yes]) AC_CANONICAL_HOST AC_DEFINE_UNQUOTED([CANONICAL_HOST], "$host", [Canonical host string.]) AS_IF([test "x$host_cpu" = "xmips" || test "x$host_cpu" = "xmipsel" || test "x$host_cpu" = "xmips64" || test "x$host_cpu" = "xmips64el"], [AC_DEFINE(ARCH_MIPS, [], [Whether on mips arch])]) -AM_SILENT_RULES([yes]) +LT_PREREQ(2.2) +LT_INIT # i18n stuff for the PolicyKit policy files IT_PROG_INTLTOOL([0.40.0]) @@ -53,6 +54,9 @@ AC_PROG_CC_C99 AM_PROG_CC_C_O AC_PROG_GCC_TRADITIONAL +AC_PATH_PROG([M4], [m4]) +GTK_DOC_CHECK(1.10) + AC_CHECK_TOOL(OBJCOPY, objcopy) AC_CHECK_TOOL(STRINGS, strings) AC_CHECK_TOOL(GPERF, gperf) @@ -110,9 +114,6 @@ CC_CHECK_FLAGS_APPEND([with_ldflags], [LDFLAGS], [\ -Wl,--gc-sections]) AC_SUBST([GCLDFLAGS], $with_ldflags) -LT_PREREQ(2.2) -LT_INIT - AC_SEARCH_LIBS([clock_gettime], [rt], [], [AC_MSG_ERROR([*** POSIX RT library not found])]) AC_SEARCH_LIBS([dlsym], [dl], [], [AC_MSG_ERROR([*** Dynamic linking loader library not found])]) @@ -127,10 +128,11 @@ AC_SUBST(CAP_LIBS) # This makes sure pkg.m4 is available. m4_pattern_forbid([^_?PKG_[A-Z_]+$],[*** pkg.m4 missing, please install pkg-config]) -PKG_CHECK_MODULES(UDEV, [ libudev >= 172 ]) -PKG_CHECK_MODULES(DBUS, [ dbus-1 >= 1.3.2 ]) -PKG_CHECK_MODULES(KMOD, [ libkmod >= 5 ]) +PKG_CHECK_MODULES(DBUS, [dbus-1 >= 1.3.2]) +PKG_CHECK_MODULES(KMOD, [libkmod >= 5]) +PKG_CHECK_MODULES(BLKID,[blkid >= 2.20]) +# ------------------------------------------------------------------------------ have_ima=yes AC_ARG_ENABLE([ima], AS_HELP_STRING([--disable-ima],[Disable optional IMA support]), [case "${enableval}" in @@ -144,6 +146,7 @@ if test "x${have_ima}" != xno ; then AC_DEFINE(HAVE_IMA, 1, [Define if IMA is available]) fi +# ------------------------------------------------------------------------------ have_selinux=no AC_ARG_ENABLE(selinux, AS_HELP_STRING([--disable-selinux], [Disable optional SELINUX support])) if test "x$enable_selinux" != "xno"; then @@ -155,6 +158,7 @@ if test "x$enable_selinux" != "xno"; then fi AM_CONDITIONAL(HAVE_SELINUX, [test "$have_selinux" = "yes"]) +# ------------------------------------------------------------------------------ have_xz=no AC_ARG_ENABLE(xz, AS_HELP_STRING([--disable-xz], [Disable optional XZ support])) if test "x$enable_xz" != "xno"; then @@ -166,6 +170,7 @@ if test "x$enable_xz" != "xno"; then fi AM_CONDITIONAL(HAVE_XZ, [test "$have_xz" = "yes"]) +# ------------------------------------------------------------------------------ AC_ARG_ENABLE([tcpwrap], AS_HELP_STRING([--disable-tcpwrap],[Disable optional TCP wrappers support]), [case "${enableval}" in @@ -190,6 +195,7 @@ else fi AC_SUBST(LIBWRAP_LIBS) +# ------------------------------------------------------------------------------ AC_ARG_ENABLE([pam], AS_HELP_STRING([--disable-pam],[Disable optional PAM support]), [case "${enableval}" in @@ -227,6 +233,7 @@ fi AC_SUBST(PAM_LIBS) AM_CONDITIONAL([HAVE_PAM], [test "x$have_pam" != xno]) +# ------------------------------------------------------------------------------ AC_ARG_ENABLE([acl], AS_HELP_STRING([--disable-acl],[Disable optional ACL support]), [case "${enableval}" in @@ -264,6 +271,7 @@ fi AC_SUBST(ACL_LIBS) AM_CONDITIONAL([HAVE_ACL], [test "x$have_acl" != xno]) +# ------------------------------------------------------------------------------ AC_ARG_ENABLE([audit], AS_HELP_STRING([--disable-audit],[Disable optional AUDIT support]), [case "${enableval}" in @@ -300,6 +308,7 @@ else fi AC_SUBST(AUDIT_LIBS) +# ------------------------------------------------------------------------------ have_libcryptsetup=no AC_ARG_ENABLE(libcryptsetup, AS_HELP_STRING([--disable-libcryptsetup], [disable libcryptsetup tools])) if test "x$enable_libcryptsetup" != "xno"; then @@ -311,6 +320,7 @@ if test "x$enable_libcryptsetup" != "xno"; then fi AM_CONDITIONAL(HAVE_LIBCRYPTSETUP, [test "$have_libcryptsetup" = "yes"]) +# ------------------------------------------------------------------------------ have_binfmt=no AC_ARG_ENABLE(binfmt, AS_HELP_STRING([--disable-binfmt], [disable binfmt tool])) if test "x$enable_binfmt" != "xno"; then @@ -318,6 +328,7 @@ if test "x$enable_binfmt" != "xno"; then fi AM_CONDITIONAL(ENABLE_BINFMT, [test "$have_binfmt" = "yes"]) +# ------------------------------------------------------------------------------ have_vconsole=no AC_ARG_ENABLE(vconsole, AS_HELP_STRING([--disable-vconsole], [disable vconsole tool])) if test "x$enable_vconsole" != "xno"; then @@ -325,6 +336,7 @@ if test "x$enable_vconsole" != "xno"; then fi AM_CONDITIONAL(ENABLE_VCONSOLE, [test "$have_vconsole" = "yes"]) +# ------------------------------------------------------------------------------ have_readahead=no AC_ARG_ENABLE(readahead, AS_HELP_STRING([--disable-readahead], [disable readahead tools])) if test "x$enable_readahead" != "xno"; then @@ -332,6 +344,7 @@ if test "x$enable_readahead" != "xno"; then fi AM_CONDITIONAL(ENABLE_READAHEAD, [test "$have_readahead" = "yes"]) +# ------------------------------------------------------------------------------ have_quotacheck=no AC_ARG_ENABLE(quotacheck, AS_HELP_STRING([--disable-quotacheck], [disable quotacheck tools])) if test "x$enable_quotacheck" != "xno"; then @@ -339,6 +352,7 @@ if test "x$enable_quotacheck" != "xno"; then fi AM_CONDITIONAL(ENABLE_QUOTACHECK, [test "$have_quotacheck" = "yes"]) +# ------------------------------------------------------------------------------ have_randomseed=no AC_ARG_ENABLE(randomseed, AS_HELP_STRING([--disable-randomseed], [disable randomseed tools])) if test "x$enable_randomseed" != "xno"; then @@ -346,6 +360,7 @@ if test "x$enable_randomseed" != "xno"; then fi AM_CONDITIONAL(ENABLE_RANDOMSEED, [test "$have_randomseed" = "yes"]) +# ------------------------------------------------------------------------------ have_logind=no AC_ARG_ENABLE(logind, AS_HELP_STRING([--disable-logind], [disable login daemon])) if test "x$enable_logind" != "xno"; then @@ -354,6 +369,7 @@ fi AM_CONDITIONAL(ENABLE_LOGIND, [test "$have_logind" = "yes"]) AS_IF([test "$have_logind" = "yes"], [ AC_DEFINE(HAVE_LOGIND, [1], [Logind support available]) ]) +# ------------------------------------------------------------------------------ have_hostnamed=no AC_ARG_ENABLE(hostnamed, AS_HELP_STRING([--disable-hostnamed], [disable hostname daemon])) if test "x$enable_hostnamed" != "xno"; then @@ -361,6 +377,7 @@ if test "x$enable_hostnamed" != "xno"; then fi AM_CONDITIONAL(ENABLE_HOSTNAMED, [test "$have_hostnamed" = "yes"]) +# ------------------------------------------------------------------------------ have_timedated=no AC_ARG_ENABLE(timedated, AS_HELP_STRING([--disable-timedated], [disable timedate daemon])) if test "x$enable_timedated" != "xno"; then @@ -368,6 +385,7 @@ if test "x$enable_timedated" != "xno"; then fi AM_CONDITIONAL(ENABLE_TIMEDATED, [test "$have_timedated" = "yes"]) +# ------------------------------------------------------------------------------ have_localed=no AC_ARG_ENABLE(localed, AS_HELP_STRING([--disable-localed], [disable locale daemon])) if test "x$enable_localed" != "xno"; then @@ -375,6 +393,7 @@ if test "x$enable_localed" != "xno"; then fi AM_CONDITIONAL(ENABLE_LOCALED, [test "$have_localed" = "yes"]) +# ------------------------------------------------------------------------------ have_coredump=no AC_ARG_ENABLE(coredump, AS_HELP_STRING([--disable-coredump], [disable coredump hook])) if test "x$enable_coredump" != "xno"; then @@ -382,6 +401,92 @@ if test "x$enable_coredump" != "xno"; then fi AM_CONDITIONAL(ENABLE_COREDUMP, [test "$have_coredump" = "yes"]) +# ------------------------------------------------------------------------------ +if test "x$cross_compiling" = "xno" ; then + AC_CHECK_FILES([/usr/share/pci.ids], [pciids=/usr/share/pci.ids]) + AC_CHECK_FILES([/usr/share/hwdata/pci.ids], [pciids=/usr/share/hwdata/pci.ids]) + AC_CHECK_FILES([/usr/share/misc/pci.ids], [pciids=/usr/share/misc/pci.ids]) +fi + +AC_ARG_WITH(usb-ids-path, + [AS_HELP_STRING([--with-usb-ids-path=DIR], [Path to usb.ids file])], + [USB_DATABASE=${withval}], + [if test -n "$usbids" ; then + USB_DATABASE="$usbids" + else + PKG_CHECK_MODULES(USBUTILS, usbutils >= 0.82) + AC_SUBST([USB_DATABASE], [$($PKG_CONFIG --variable=usbids usbutils)]) + fi]) +AC_MSG_CHECKING([for USB database location]) +AC_MSG_RESULT([$USB_DATABASE]) +AC_SUBST(USB_DATABASE) + +AC_ARG_WITH(pci-ids-path, + [AS_HELP_STRING([--with-pci-ids-path=DIR], [Path to pci.ids file])], + [PCI_DATABASE=${withval}], + [if test -n "$pciids" ; then + PCI_DATABASE="$pciids" + else + AC_MSG_ERROR([pci.ids not found, try --with-pci-ids-path=]) + fi]) +AC_MSG_CHECKING([for PCI database location]) +AC_MSG_RESULT([$PCI_DATABASE]) +AC_SUBST(PCI_DATABASE) + +# ------------------------------------------------------------------------------ +AC_ARG_WITH(firmware-path, + AS_HELP_STRING([--with-firmware-path=DIR[[[:DIR[...]]]]], + [Firmware search path (default=ROOTPREFIX/lib/firmware/updates:ROOTPREFIX/lib/firmware)]), + [], [with_firmware_path="$rootprefix/lib/firmware/updates:$rootprefix/lib/firmware"]) +OLD_IFS=$IFS +IFS=: +for i in $with_firmware_path; do + if test "x${FIRMWARE_PATH}" = "x"; then + FIRMWARE_PATH="\\\"${i}/\\\"" + else + FIRMWARE_PATH="${FIRMWARE_PATH}, \\\"${i}/\\\"" + fi +done +IFS=$OLD_IFS +AC_SUBST([FIRMWARE_PATH], [$FIRMWARE_PATH]) + +# ------------------------------------------------------------------------------ +AC_ARG_ENABLE([gudev], + AS_HELP_STRING([--disable-gudev], [disable Gobject libudev support @<:@default=enabled@:>@]), + [], [enable_gudev=yes]) +AS_IF([test "x$enable_gudev" = "xyes"], [ PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.22.0 gobject-2.0 >= 2.22.0]) ]) + +AC_ARG_ENABLE([introspection], + AS_HELP_STRING([--disable-introspection], [disable GObject introspection @<:@default=enabled@:>@]), + [], [enable_introspection=yes]) +AS_IF([test "x$enable_introspection" = "xyes"], [ + PKG_CHECK_MODULES([INTROSPECTION], [gobject-introspection-1.0 >= 0.6.2]) + AC_DEFINE([ENABLE_INTROSPECTION], [1], [enable GObject introspection support]) + AC_SUBST([G_IR_SCANNER], [$($PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0)]) + AC_SUBST([G_IR_COMPILER], [$($PKG_CONFIG --variable=g_ir_compiler gobject-introspection-1.0)]) + AC_SUBST([G_IR_GENERATE], [$($PKG_CONFIG --variable=g_ir_generate gobject-introspection-1.0)]) + AC_SUBST([GIRDIR], [$($PKG_CONFIG --define-variable=datadir=${datadir} --variable=girdir gobject-introspection-1.0)]) + AC_SUBST([GIRTYPELIBDIR], [$($PKG_CONFIG --define-variable=libdir=${libdir} --variable=typelibdir gobject-introspection-1.0)]) +]) +AM_CONDITIONAL([ENABLE_INTROSPECTION], [test "x$enable_introspection" = "xyes"]) +AM_CONDITIONAL([ENABLE_GUDEV], [test "x$enable_gudev" = "xyes"]) + +# ------------------------------------------------------------------------------ +AC_ARG_ENABLE([keymap], + AS_HELP_STRING([--disable-keymap], [disable keymap fixup support @<:@default=enabled@:>@]), + [], [enable_keymap=yes]) +AS_IF([test "x$enable_keymap" = "xyes"], [ + AC_PATH_PROG([GPERF], [gperf]) + if test -z "$GPERF"; then + AC_MSG_ERROR([gperf is needed]) + fi + + AC_CHECK_HEADER([linux/input.h], [:], AC_MSG_ERROR([kernel headers not found])) + AC_SUBST([INCLUDE_PREFIX], [$(echo '#include ' | eval $ac_cpp -E - | sed -n '/linux\/input.h/ {s:.*"\(.*\)/linux/input.h".*:\1:; p; q}')]) +]) +AM_CONDITIONAL([ENABLE_KEYMAP], [test "x$enable_keymap" = "xyes"]) + +# ------------------------------------------------------------------------------ have_manpages=no AC_ARG_ENABLE(manpages, AS_HELP_STRING([--disable-manpages], [disable manpages])) if test "x$enable_manpages" != "xno"; then @@ -389,11 +494,10 @@ if test "x$enable_manpages" != "xno"; then fi AM_CONDITIONAL(ENABLE_MANPAGES, [test "$have_manpages" = "yes"]) +# ------------------------------------------------------------------------------ AC_PATH_PROG([XSLTPROC], [xsltproc]) AM_CONDITIONAL(HAVE_XSLTPROC, test x"$XSLTPROC" != x) -AC_PATH_PROG([M4], [m4]) - AC_ARG_WITH(distro, AS_HELP_STRING([--with-distro=DISTRO],[Specify the distribution to target: One of fedora, suse, debian, ubuntu, arch, gentoo, slackware, altlinux, mandriva, meego, mageia, angstrom or other])) if test "z$with_distro" = "z"; then if test "$cross_compiling" = yes; then @@ -572,11 +676,6 @@ AC_ARG_WITH([dbusinterfacedir], [], [with_dbusinterfacedir=`pkg-config --variable=session_bus_services_dir dbus-1`/../interfaces]) -AC_ARG_WITH([udevrulesdir], - AS_HELP_STRING([--with-udevrulesdir=DIR], [Directory for udev rules]), - [], - [with_udevrulesdir=`pkg-config --variable=udevdir udev`/rules.d]) - AC_ARG_WITH([rootprefix], AS_HELP_STRING([--with-rootprefix=DIR], [rootfs directory prefix for config files and kernel modules]), [], [with_rootprefix=${ac_default_prefix}]) @@ -608,12 +707,18 @@ AC_SUBST([dbuspolicydir], [$with_dbuspolicydir]) AC_SUBST([dbussessionservicedir], [$with_dbussessionservicedir]) AC_SUBST([dbussystemservicedir], [$with_dbussystemservicedir]) AC_SUBST([dbusinterfacedir], [$with_dbusinterfacedir]) -AC_SUBST([udevrulesdir], [$with_udevrulesdir]) AC_SUBST([pamlibdir], [$with_pamlibdir]) AC_SUBST([rootprefix], [$with_rootprefix]) AC_SUBST([rootlibdir], [$with_rootlibdir]) -AC_CONFIG_FILES([Makefile po/Makefile.in]) +AC_CONFIG_FILES([ + Makefile po/Makefile.in + src/udev/docs/Makefile + src/udev/docs/version.xml + src/udev/gudev/docs/Makefile + src/udev/gudev/docs/version.xml +]) + AC_OUTPUT AC_MSG_RESULT([ $PACKAGE_NAME $VERSION @@ -641,13 +746,23 @@ AC_MSG_RESULT([ localed: ${have_localed} coredump: ${have_coredump} plymouth: ${have_plymouth} + firmware path: ${FIRMWARE_PATH} + usb.ids: ${USB_DATABASE} + pci.ids: ${PCI_DATABASE} + gudev: ${enable_gudev} + gintrospection: ${enable_introspection} + keymap: ${enable_keymap} + prefix: ${prefix} rootprefix: ${with_rootprefix} + sysconf dir: ${sysconfdir} + datarootdir: ${datarootdir} + includedir: ${includedir} + include_prefix: ${INCLUDE_PREFIX} libexec dir: ${libexecdir} lib dir: ${libdir} rootlib dir: ${with_rootlibdir} PAM modules dir: ${with_pamlibdir} - udev rules dir: ${with_udevrulesdir} D-Bus policy dir: ${with_dbuspolicydir} D-Bus session dir: ${with_dbussessionservicedir} D-Bus system dir: ${with_dbussystemservicedir} diff --git a/m4/.gitignore b/m4/.gitignore index 55eaa803a1..cf35a86e88 100644 --- a/m4/.gitignore +++ b/m4/.gitignore @@ -4,3 +4,4 @@ ltoptions.m4 ltsugar.m4 ltversion.m4 lt~obsolete.m4 +gtk-doc.m4 diff --git a/man/udev.xml b/man/udev.xml new file mode 100644 index 0000000000..8eb583a823 --- /dev/null +++ b/man/udev.xml @@ -0,0 +1,695 @@ + + + + + + + udev + udev + + + + udev + 7 + + + + udev + Linux dynamic device management + + + Description + udev supplies the system software with device events, manages permissions + of device nodes and may create additional symlinks in the /dev + directory, or renames network interfaces. The kernel usually just assigns unpredictable + device names based on the order of discovery. Meaningful symlinks or network device + names provide a way to reliably identify devices based on their properties or + current configuration. + + The udev daemon, udevd + 8, receives device uevents directly from + the kernel whenever a device is added or removed from the system, or it changes its + state. When udev receives a device event, it matches its configured set of rules + against various device attributes to identify the device. Rules that match may + provide additional device information to be stored in the udev database or + to be used to create meaningful symlink names. + + All device information udev processes is stored in the udev database and + sent out to possible event subscribers. Access to all stored data and the event + sources is provided by the library libudev. + + + Configuration + udev configuration files are placed in /etc/udev + and /usr/lib/udev. All empty lines or lines beginning with + '#' are ignored. + + Configuration file + udev expects its main configuration file at /etc/udev/udev.conf. + It consists of a set of variables allowing the user to override default udev values. + The following variables can be set: + + + + + Specifies where to place the device nodes in the filesystem. + The default value is /dev. + + + + + + + The logging priority. Valid values are the numerical syslog priorities + or their textual representations: , + and . + + + + + + Rules files + The udev rules are read from the files located in the + system rules directory /usr/lib/udev/rules.d, + the volatile runtime directory /run/udev/rules.d + and the local administration directory /etc/udev/rules.d. + All rules files are collectively sorted and processed in lexical order, + regardless of the directories in which they live. However, files with + identical file names replace each other. Files in /etc + have the highest priority, files in /run take precedence + over files with the same name in /lib. This can be + used to override a system-supplied rules file with a local file if needed; + a symlink in /etc with the same name as a rules file in + /lib, pointing to /dev/null, + disables the rules file entirely. + + Rule files must have the extension .rules; other + extensions are ignored. + + Every line in the rules file contains at least one key-value pair. + There are two kind of keys: match and assignment. + If all match keys are matching against its value, the rule gets applied and the + assignment keys get the specified value assigned. + + A matching rule may rename a network interface, add symlinks + pointing to the device node, or run a specified program as part of + the event handling. + + A rule consists of a comma-separated list of one or more key-value pairs. + Each key has a distinct operation, depending on the used operator. Valid + operators are: + + + + + Compare for equality. + + + + + + + Compare for inequality. + + + + + + + Assign a value to a key. Keys that represent a list are reset + and only this single value is assigned. + + + + + + + Add the value to a key that holds a list of entries. + + + + + + + Assign a value to a key finally; disallow any later changes. + + + + + The following key names can be used to match against device properties. + Some of the keys also match against properties of the parent devices in sysfs, + not only the device that has generated the event. If multiple keys that match + a parent device are specified in a single rule, all these keys must match at + one and the same parent device. + + + + + Match the name of the event action. + + + + + + + Match the devpath of the event device. + + + + + + + Match the name of the event device. + + + + + + + Match the name of a network interface. It can be used once the + NAME key has been set in one of the preceding rules. + + + + + + + Match the name of a symlink targeting the node. It can + be used once a SYMLINK key has been set in one of the preceding + rules. There may be multiple symlinks; only one needs to match. + + + + + + + + Match the subsystem of the event device. + + + + + + Match the driver name of the event device. Only set this key for devices + which are bound to a driver at the time the event is generated. + + + + + + Match sysfs attribute values of the event device. Trailing + whitespace in the attribute values is ignored unless the specified match + value itself contains trailing whitespace. + + + + + + + + Search the devpath upwards for a matching device name. + + + + + + + Search the devpath upwards for a matching device subsystem name. + + + + + + + Search the devpath upwards for a matching device driver name. + + + + + + + Search the devpath upwards for a device with matching sysfs attribute values. + If multiple matches are specified, all of them + must match on the same device. Trailing whitespace in the attribute values is ignored + unless the specified match value itself contains trailing whitespace. + + + + + + + Search the devpath upwards for a device with matching tag. + + + + + + + Match against a device property value. + + + + + + + Match against a device tag. + + + + + + + Test the existence of a file. An octal mode mask can be specified + if needed. + + + + + + + Execute a program to determine whether there + is a match; the key is true if the program returns + successfully. The device properties are made available to the + executed program in the environment. The program's stdout + is available in the RESULT key. + + + + + + + Match the returned string of the last PROGRAM call. This key can + be used in the same or in any later rule after a PROGRAM call. + + + + + Most of the fields support shell-style pattern matching. The following + pattern characters are supported: + + + + + Matches zero or more characters. + + + + + + Matches any single character. + + + + + + Matches any single character specified within the brackets. For + example, the pattern string 'tty[SR]' would match either 'ttyS' or 'ttyR'. + Ranges are also supported via the '-' character. + For example, to match on the range of all digits, the pattern [0-9] could + be used. If the first character following the '[' is a '!', any characters + not enclosed are matched. + + + + + The following keys can get values assigned: + + + + + The name to use for a network interface. The name of a device node + can not be changed by udev, only additional symlinks can be created. + + + + + + + The name of a symlink targeting the node. Every matching rule adds + this value to the list of symlinks to be created. Multiple symlinks may be + specified by separating the names by the space character. In case multiple + devices claim the same name, the link always points to the device with + the highest link_priority. If the current device goes away, the links are + re-evaluated and the device with the next highest link_priority becomes the owner of + the link. If no link_priority is specified, the order of the devices (and + which one of them owns the link) is undefined. Also, symlink names must + never conflict with the kernel's default device node names, as that would + result in unpredictable behavior. + + + + + + + + The permissions for the device node. Every specified value overrides + the compiled-in default value. + + + + + + + The value that should be written to a sysfs attribute of the + event device. + + + + + + + Set a device property value. Property names with a leading '.' + are neither stored in the database nor exported to events or + external tools (run by, say, the PROGRAM match key). + + + + + + + Attach a tag to a device. This is used to filter events for users + of libudev's monitor functionality, or to enumerate a group of tagged + devices. The implementation can only work efficiently if only a few + tags are attached to a device. It is only meant to be used in + contexts with specific device filter requirements, and not as a + general-purpose flag. Excessive use might result in inefficient event + handling. + + + + + + + Add a program to the list of programs to be executed for a specific + device. + If no absolute path is given, the program is expected to live in + /usr/lib/udev, otherwise the absolute path must be specified. The program + name and following arguments are separated by spaces. Single quotes can + be used to specify arguments with spaces. + This can only be used for very short running tasks. Running an + event process for a long period of time may block all further events for + this or a dependent device. Starting daemons or other long running processes + is not appropriate for udev. + + + + + + + A named label to which a GOTO may jump. + + + + + + + Jumps to the next LABEL with a matching name. + + + + + + + Import a set of variables as device properties, + depending on type: + + + + + Execute an external program specified as the assigned value and + import its output, which must be in environment key + format. Path specification, command/argument separation, + and quoting work like in . + + + + + + Import a text file specified as the assigned value, the content + of which must be in environment key format. + + + + + + Import a single property specified as the assigned value from the + current device database. This works only if the database is already populated + by an earlier event. + + + + + + Import a single property from the kernel command line. For simple flags + the value of the property is set to '1'. + + + + + + Import the stored keys from the parent device by reading + the database entry of the parent device. The value assigned to + is used as a filter of key names + to import (with the same shell-style pattern matching used for + comparisons). + + + + + + + + + + Wait for a file to become available or until a timeout of + 10 seconds expires. The path is relative to the sysfs device; + if no path is specified, this waits for an attribute to appear. + + + + + + + Rule and device options: + + + + + Specify the priority of the created symlinks. Devices with higher + priorities overwrite existing symlinks of other devices. The default is 0. + + + + + + Number of seconds an event waits for operations to finish before + giving up and terminating itself. + + + + + + Usually control and other possibly unsafe characters are replaced + in strings used for device naming. The mode of replacement can be specified + with this option. + + + + + + Apply the permissions specified in this rule to the static device node with + the specified name. Static device nodes might be provided by kernel modules + or copied from /usr/lib/udev/devices. These nodes might not have + a corresponding kernel device at the time udevd is started; they can trigger + automatic kernel module loading. + + + + + + Watch the device node with inotify; when the node is closed after being opened for + writing, a change uevent is synthesized. + + + + + + Disable the watching of a device node with inotify. + + + + + + + + The , , , + , , and + fields support simple string substitutions. The + substitutions are performed after all rules have been processed, right before the program + is executed, allowing for the use of device properties set by earlier matching + rules. For all other fields, substitutions are performed while the individual rule is + being processed. The available substitutions are: + + + , + + The kernel name for this device. + + + + + , + + The kernel number for this device. For example, 'sda3' has + kernel number of '3' + + + + + , + + The devpath of the device. + + + + + , + + The name of the device matched while searching the devpath upwards for + , , and . + + + + + + + + The driver name of the device matched while searching the devpath upwards for + , , and . + + + + + + , + + The value of a sysfs attribute found at the device where + all keys of the rule have matched. If the matching device does not have + such an attribute, and a previous KERNELS, SUBSYSTEMS, DRIVERS, or + ATTRS test selected a parent device, then the attribute from that + parent device is used. + If the attribute is a symlink, the last element of the symlink target is + returned as the value. + + + + + , + + A device property value. + + + + + , + + The kernel major number for the device. + + + + + , + + The kernel minor number for the device. + + + + + , + + The string returned by the external program requested with PROGRAM. + A single part of the string, separated by a space character, may be selected + by specifying the part number as an attribute: . + If the number is followed by the '+' character, this part plus all remaining parts + of the result string are substituted: + + + + + , + + The node name of the parent device. + + + + + + + The current name of the device. If not changed by a rule, it is the + name of the kernel device. + + + + + + + A space-separated list of the current symlinks. The value is + only set during a remove event or if an earlier rule assigned a value. + + + + + , + + The udev_root value. + + + + + , + + The sysfs mount point. + + + + + , + + The name of the device node. + + + + + + + The '%' character itself. + + + + + + + The '$' character itself. + + + + + + + Author + Written by Greg Kroah-Hartman greg@kroah.com and + Kay Sievers kay.sievers@vrfy.org. With much help from + Dan Stekloff and many others. + + + + See Also + + udevd8 + , + + udevadm8 + + + diff --git a/man/udevadm.xml b/man/udevadm.xml new file mode 100644 index 0000000000..455ce80ca9 --- /dev/null +++ b/man/udevadm.xml @@ -0,0 +1,472 @@ + + + + + + + udevadm + udev + + + + udevadm + 8 + + + + + udevadmudev management tool + + + + + udevadm + + + + + + udevadm info options + + + udevadm trigger options + + + udevadm settle options + + + udevadm control command + + + udevadm monitor options + + + udevadm test options devpath + + + udevadm test-builtin options command devpath + + + + Description + udevadm expects a command and command specific options. It + controls the runtime behavior of udev, requests kernel events, + manages the event queue, and provides simple debugging mechanisms. + + + OPTIONS + + + + + Print debug messages to stderr. + + + + + + Print version number. + + + + + + Print help text. + + + + + udevadm info <replaceable>options</replaceable> + Queries the udev database for device information + stored in the udev database. It can also query the properties + of a device from its sysfs representation to help creating udev + rules that match this device. + + + + + Query the database for specified type of device data. It needs the + or to identify the specified + device. Valid queries are: + name, symlink, path, + property, all. + + + + + + The devpath of the device to query. + + + + + + The name of the device node or a symlink to query + + + + + + The udev root directory: /dev. If used in conjunction + with a name or symlink query, the + query returns the absolute path including the root directory. + + + + + + The udev runtime directory: /run/udev. + + + + + + Print all sysfs properties of the specified device that can be used + in udev rules to match the specified device. It prints all devices + along the chain, up to the root of sysfs that can be used in udev rules. + + + + + + Print output as key/value pairs. Values are enclosed in single quotes. + + + + + + Add a prefix to the key name of exported values. + + + + + + Print major/minor numbers of the underlying device, where the file + lives on. + + + + + + Export the content of the udev database. + + + + + + Cleanup the udev database. + + + + + + Print version. + + + + + + Print help text. + + + + + + udevadm trigger <optional>options</optional> + Request device events from the kernel. Primarily used to replay events at system coldplug time. + + + + + Print the list of devices which will be triggered. + + + + + + Do not actually trigger the event. + + + + + + Trigger a specific type of devices. Valid types are: + devices, subsystems. + The default value is devices. + + + + + + Type of event to be triggered. The default value is change. + + + + + + Trigger events for devices which belong to a matching subsystem. This option + can be specified multiple times and supports shell style pattern matching. + + + + + + Do not trigger events for devices which belong to a matching subsystem. This option + can be specified multiple times and supports shell style pattern matching. + + + + + + Trigger events for devices with a matching sysfs attribute. If a value is specified + along with the attribute name, the content of the attribute is matched against the given + value using shell style pattern matching. If no value is specified, the existence of the + sysfs attribute is checked. This option can be specified multiple times. + + + + + + Do not trigger events for devices with a matching sysfs attribute. If a value is + specified along with the attribute name, the content of the attribute is matched against + the given value using shell style pattern matching. If no value is specified, the existence + of the sysfs attribute is checked. This option can be specified multiple times. + + + + + + Trigger events for devices with a matching property value. This option can be + specified multiple times and supports shell style pattern matching. + + + + + + Trigger events for devices with a matching tag. This option can be + specified multiple times. + + + + + + Trigger events for devices with a matching sys device name. This option can be + specified multiple times and supports shell style pattern matching. + + + + + + Trigger events for all children of a given device. + + + + + + udevadm settle <optional>options</optional> + Watches the udev event queue, and exits if all current events are handled. + + + + + Maximum number of seconds to wait for the event queue to become empty. + The default value is 120 seconds. A value of 0 will check if the queue is empty + and always return immediately. + + + + + + Wait only for events after the given sequence number. + + + + + + Wait only for events before the given sequence number. + + + + + + Stop waiting if file exists. + + + + + + Do not print any output, like the remaining queue entries when reaching the timeout. + + + + + + Print help text. + + + + + + udevadm control <replaceable>command</replaceable> + Modify the internal state of the running udev daemon. + + + + + Signal and wait for udevd to exit. + + + + + + Set the internal log level of udevd. Valid values are the numerical + syslog priorities or their textual representations: , + and . + + + + + + Signal udevd to stop executing new events. Incoming events + will be queued. + + + + + + Signal udevd to enable the execution of events. + + + + + + Signal udevd to reload the rules files and other databases like the kernel + module index. Reloading rules and databases does not apply any changes to already + existing devices; the new configuration will only be applied to new events. + + + + + + Set a global property for all events. + + + + value + + Set the maximum number of events, udevd will handle at the + same time. + + + + seconds + + The maximum number seconds to wait for a reply from udevd. + + + + + + Print help text. + + + + + + udevadm monitor <optional>options</optional> + Listens to the kernel uevents and events sent out by a udev rule + and prints the devpath of the event to the console. It can be used to analyze the + event timing, by comparing the timestamps of the kernel uevent and the udev event. + + + + + + Print the kernel uevents. + + + + + + Print the udev event after the rule processing. + + + + + + Also print the properties of the event. + + + + + + Filter events by subsystem[/devtype]. Only udev events with a matching subsystem value will pass. + + + + + + Filter events by property. Only udev events with a given tag attached will pass. + + + + + + Print help text. + + + + + + udevadm test <optional>options</optional> <replaceable>devpath</replaceable> + Simulate a udev event run for the given device, and print debug output. + + + + + The action string. + + + + + + The subsystem string. + + + + + + Print help text. + + + + + + udevadm test-builtin <optional>options</optional> <replaceable>command</replaceable> <replaceable>devpath</replaceable> + Run a built-in command for the given device, and print debug output. + + + + + Print help text. + + + + + + + Author + Written by Kay Sievers kay.sievers@vrfy.org. + + + + See Also + + udev7 + + + udevd8 + + + diff --git a/man/udevd.xml b/man/udevd.xml new file mode 100644 index 0000000000..c516eb9793 --- /dev/null +++ b/man/udevd.xml @@ -0,0 +1,151 @@ + + + + + + + udevd + udev + + + + udevd + 8 + + + + + udevdevent managing daemon + + + + + udevd + + + + + + + + + + + Description + udevd listens to kernel uevents. For every event, udevd executes matching + instructions specified in udev rules. See + udev7 + . + On startup the content of the directory /usr/lib/udev/devices + is copied to /dev. If kernel modules specify static device + nodes, these nodes are created even without a corresponding kernel device, to + allow on-demand loading of kernel modules. Matching permissions specified in udev + rules are applied to these static device nodes. + The behavior of the running daemon can be changed with + udevadm control. + + + Options + + + + + Detach and run in the background. + + + + + + Print debug messages to stderr. + + + + + + Limit the number of parallel executed events. + + + + + + Number of seconds to delay the execution of RUN instructions. + This might be useful when debugging system crashes during coldplug + cause by loading non-working kernel modules. + + + + + + Specify when udevd should resolve names of users and groups. + When set to (the default) names will be + resolved when the rules are parsed. When set to + names will be resolved for every event. + When set to names will never be resolved + and all devices will be owned by root. + + + + + + Print version number. + + + + + + Print help text. + + + + + + Environment + + + UDEV_LOG= + + Set the logging priority. + + + + + + Kernel command line + + + udev.log-priority= + + Set the logging priority. + + + + udev.children-max= + + Limit the number of parallel executed events. + + + + udev.exec-delay= + + Number of seconds to delay the execution of RUN instructions. + This might be useful when debugging system crashes during coldplug + cause by loading non-working kernel modules. + + + + + + Author + Written by Kay Sievers kay.sievers@vrfy.org. + + + + See Also + + udev7 + , + udevadm8 + + + diff --git a/rules/.gitignore b/rules/.gitignore new file mode 100644 index 0000000000..93a50ddd80 --- /dev/null +++ b/rules/.gitignore @@ -0,0 +1 @@ +/99-systemd.rules diff --git a/rules/42-usb-hid-pm.rules b/rules/42-usb-hid-pm.rules new file mode 100644 index 0000000000..d5d5897c31 --- /dev/null +++ b/rules/42-usb-hid-pm.rules @@ -0,0 +1,49 @@ +# +# Enable autosuspend for qemu emulated usb hid devices. +# +# Note that there are buggy qemu versions which advertise remote +# wakeup support but don't actually implement it correctly. This +# is the reason why we need a match for the serial number here. +# The serial number "42" is used to tag the implementations where +# remote wakeup is working. +# + +ACTION=="add", SUBSYSTEM=="usb", ATTR{product}=="QEMU USB Mouse", ATTR{serial}=="42", TEST=="power/control", ATTR{power/control}="auto" +ACTION=="add", SUBSYSTEM=="usb", ATTR{product}=="QEMU USB Tablet", ATTR{serial}=="42", TEST=="power/control", ATTR{power/control}="auto" +ACTION=="add", SUBSYSTEM=="usb", ATTR{product}=="QEMU USB Keyboard", ATTR{serial}=="42", TEST=="power/control", ATTR{power/control}="auto" + +# +# Enable autosuspend for KVM and iLO usb hid devices. These are +# effectively self-powered (despite what some claim in their USB +# profiles) and so it's safe to do so. +# + +# AMI 046b:ff10 +ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="046b", ATTR{idProduct}=="ff10", TEST=="power/control", ATTR{power/control}="auto" + +# +# Catch-all for Avocent HID devices. Keyed off interface in order to only +# trigger on HID class devices. +# +ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="0624", ATTR{bInterfaceClass}=="03", TEST=="../power/control", ATTR{../power/control}="auto" + +# Dell DRAC 4 +ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="413c", ATTR{idProduct}=="2500", TEST=="power/control", ATTR{power/control}="auto" + +# Dell DRAC 5 +ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="413c", ATTR{idProduct}=="0000", TEST=="power/control", ATTR{power/control}="auto" + +# Hewlett Packard iLO +ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="03f0", ATTR{idProduct}=="7029", TEST=="power/control", ATTR{power/control}="auto" +ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="03f0", ATTR{idProduct}=="1027", TEST=="power/control", ATTR{power/control}="auto" + +# IBM remote access +ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="04b3", ATTR{idProduct}=="4001", TEST=="power/control", ATTR{power/control}="auto" +ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="04b3", ATTR{idProduct}=="4002", TEST=="power/control", ATTR{power/control}="auto" +ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="04b3", ATTR{idProduct}=="4012", TEST=="power/control", ATTR{power/control}="auto" + +# Raritan Computer, Inc KVM. +ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="14dd", ATTR{idProduct}="0002", TEST=="power/control", ATTR{power/control}="auto" + +# USB HID devices that are internal to the machine should also be safe to autosuspend +ACTION=="add", SUBSYSTEM=="usb", ATTR{bInterfaceClass}=="03", ATTRS{removable}=="fixed", TEST=="../power/control", ATTR{../power/control}="auto" diff --git a/rules/50-udev-default.rules b/rules/50-udev-default.rules new file mode 100644 index 0000000000..5ad787fc76 --- /dev/null +++ b/rules/50-udev-default.rules @@ -0,0 +1,107 @@ +# do not edit this file, it will be overwritten on update + +KERNEL=="pty[pqrstuvwxyzabcdef][0123456789abcdef]", GROUP="tty", MODE="0660" +KERNEL=="tty[pqrstuvwxyzabcdef][0123456789abcdef]", GROUP="tty", MODE="0660" +KERNEL=="ptmx", GROUP="tty", MODE="0666" +KERNEL=="tty", GROUP="tty", MODE="0666" +KERNEL=="tty[0-9]*", GROUP="tty", MODE="0620" +KERNEL=="vcs|vcs[0-9]*|vcsa|vcsa[0-9]*", GROUP="tty" + +# serial +KERNEL=="tty[A-Z]*[0-9]|pppox[0-9]*|ircomm[0-9]*|noz[0-9]*|rfcomm[0-9]*", GROUP="dialout" +KERNEL=="mwave", GROUP="dialout" +KERNEL=="hvc*|hvsi*", GROUP="dialout" + +# virtio serial / console ports +KERNEL=="vport*", ATTR{name}=="?*", SYMLINK+="virtio-ports/$attr{name}" + +# mem +KERNEL=="null|zero|full|random|urandom", MODE="0666" +KERNEL=="mem|kmem|port|nvram", GROUP="kmem", MODE="0640" + +# input +SUBSYSTEM=="input", ENV{ID_INPUT}=="", IMPORT{builtin}="input_id" +KERNEL=="mouse*|mice|event*", MODE="0640" +KERNEL=="ts[0-9]*|uinput", MODE="0640" +KERNEL=="js[0-9]*", MODE="0644" + +# video4linux +SUBSYSTEM=="video4linux", GROUP="video" +KERNEL=="vttuner*", GROUP="video" +KERNEL=="vtx*|vbi*", GROUP="video" +KERNEL=="winradio*", GROUP="video" + +# graphics +KERNEL=="agpgart", GROUP="video" +KERNEL=="pmu", GROUP="video" +KERNEL=="nvidia*|nvidiactl*", GROUP="video" +SUBSYSTEM=="graphics", GROUP="video" +SUBSYSTEM=="drm", GROUP="video" + +# sound +SUBSYSTEM=="sound", GROUP="audio", \ + OPTIONS+="static_node=snd/seq", OPTIONS+="static_node=snd/timer" + +# DVB (video) +SUBSYSTEM=="dvb", GROUP="video" + +# FireWire (firewire-core driver: IIDC devices, AV/C devices) +SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x00010*", GROUP="video" +SUBSYSTEM=="firewire", ATTR{units}=="*0x00b09d:0x00010*", GROUP="video" +SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x010001*", GROUP="video" +SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x014001*", GROUP="video" + +# 'libusb' device nodes +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", MODE="0664" +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", IMPORT{builtin}="usb_id" + +# printer +KERNEL=="parport[0-9]*", GROUP="lp" +SUBSYSTEM=="printer", KERNEL=="lp*", GROUP="lp" +SUBSYSTEM=="ppdev", GROUP="lp" +KERNEL=="lp[0-9]*", GROUP="lp" +KERNEL=="irlpt[0-9]*", GROUP="lp" +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0701??:*", GROUP="lp" + +# block +SUBSYSTEM=="block", GROUP="disk" + +# floppy +SUBSYSTEM=="block", KERNEL=="fd[0-9]", GROUP="floppy" + +# cdrom +SUBSYSTEM=="block", KERNEL=="sr[0-9]*", GROUP="cdrom" +SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="4|5", GROUP="cdrom" +KERNEL=="pktcdvd[0-9]*", GROUP="cdrom" +KERNEL=="pktcdvd", GROUP="cdrom" + +# tape +KERNEL=="ht[0-9]*|nht[0-9]*", GROUP="tape" +KERNEL=="pt[0-9]*|npt[0-9]*|pht[0-9]*", GROUP="tape" +SUBSYSTEM=="scsi_generic|scsi_tape", SUBSYSTEMS=="scsi", ATTRS{type}=="1|8", GROUP="tape" + +# block-related +KERNEL=="sch[0-9]*", GROUP="disk" +SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="0", GROUP="disk" +KERNEL=="pg[0-9]*", GROUP="disk" +KERNEL=="qft[0-9]*|nqft[0-9]*|zqft[0-9]*|nzqft[0-9]*|rawqft[0-9]*|nrawqft[0-9]*", GROUP="disk" +KERNEL=="rawctl", GROUP="disk" +SUBSYSTEM=="raw", KERNEL=="raw[0-9]*", GROUP="disk" +SUBSYSTEM=="aoe", GROUP="disk", MODE="0220" +SUBSYSTEM=="aoe", KERNEL=="err", MODE="0440" + +# network +KERNEL=="tun", MODE="0666", OPTIONS+="static_node=net/tun" +KERNEL=="rfkill", MODE="0644" + +# CPU +KERNEL=="cpu[0-9]*", MODE="0444" + +KERNEL=="fuse", ACTION=="add", MODE="0666", OPTIONS+="static_node=fuse" + +SUBSYSTEM=="rtc", ATTR{hctosys}=="1", SYMLINK+="rtc" +KERNEL=="mmtimer", MODE="0644" +KERNEL=="rflash[0-9]*", MODE="0400" +KERNEL=="rrom[0-9]*", MODE="0400" + +SUBSYSTEM=="firmware", ACTION=="add", IMPORT{builtin}="firmware" diff --git a/rules/60-persistent-alsa.rules b/rules/60-persistent-alsa.rules new file mode 100644 index 0000000000..8154e2dbb5 --- /dev/null +++ b/rules/60-persistent-alsa.rules @@ -0,0 +1,14 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="persistent_alsa_end" +SUBSYSTEM!="sound", GOTO="persistent_alsa_end" +KERNEL!="controlC[0-9]*", GOTO="persistent_alsa_end" + +SUBSYSTEMS=="usb", ENV{ID_MODEL}=="", IMPORT{builtin}="usb_id" +ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="?*", SYMLINK+="snd/by-id/$env{ID_BUS}-$env{ID_SERIAL}-$env{ID_USB_INTERFACE_NUM}" +ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="", SYMLINK+="snd/by-id/$env{ID_BUS}-$env{ID_SERIAL}" + +IMPORT{builtin}="path_id" +ENV{ID_PATH}=="?*", SYMLINK+="snd/by-path/$env{ID_PATH}" + +LABEL="persistent_alsa_end" diff --git a/rules/60-persistent-input.rules b/rules/60-persistent-input.rules new file mode 100644 index 0000000000..fb798ddb05 --- /dev/null +++ b/rules/60-persistent-input.rules @@ -0,0 +1,38 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="persistent_input_end" +SUBSYSTEM!="input", GOTO="persistent_input_end" +SUBSYSTEMS=="bluetooth", GOTO="persistent_input_end" + +SUBSYSTEMS=="usb", ENV{ID_BUS}=="", IMPORT{builtin}="usb_id" + +# determine class name for persistent symlinks +ENV{ID_INPUT_KEYBOARD}=="?*", ENV{.INPUT_CLASS}="kbd" +ENV{ID_INPUT_MOUSE}=="?*", ENV{.INPUT_CLASS}="mouse" +ENV{ID_INPUT_TOUCHPAD}=="?*", ENV{.INPUT_CLASS}="mouse" +ENV{ID_INPUT_TABLET}=="?*", ENV{.INPUT_CLASS}="mouse" +ENV{ID_INPUT_JOYSTICK}=="?*", ENV{.INPUT_CLASS}="joystick" +DRIVERS=="pcspkr", ENV{.INPUT_CLASS}="spkr" +ATTRS{name}=="*dvb*|*DVB*|* IR *", ENV{.INPUT_CLASS}="ir" + +# fill empty serial number +ENV{.INPUT_CLASS}=="?*", ENV{ID_SERIAL}=="", ENV{ID_SERIAL}="noserial" + +# by-id links +KERNEL=="mouse*|js*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-$env{.INPUT_CLASS}" +KERNEL=="mouse*|js*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", ATTRS{bInterfaceNumber}=="?*", ATTRS{bInterfaceNumber}!="00", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$attr{bInterfaceNumber}-$env{.INPUT_CLASS}" +KERNEL=="event*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-event-$env{.INPUT_CLASS}" +KERNEL=="event*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", ATTRS{bInterfaceNumber}=="?*", ATTRS{bInterfaceNumber}!="00", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$attr{bInterfaceNumber}-event-$env{.INPUT_CLASS}" +# allow empty class for USB devices, by appending the interface number +SUBSYSTEMS=="usb", ENV{ID_BUS}=="?*", KERNEL=="event*", ENV{.INPUT_CLASS}=="", ATTRS{bInterfaceNumber}=="?*", \ + SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-event-if$attr{bInterfaceNumber}" + +# by-path +SUBSYSTEMS=="pci|usb|platform|acpi", IMPORT{builtin}="path_id" +ENV{ID_PATH}=="?*", KERNEL=="mouse*|js*", ENV{.INPUT_CLASS}=="?*", SYMLINK+="input/by-path/$env{ID_PATH}-$env{.INPUT_CLASS}" +ENV{ID_PATH}=="?*", KERNEL=="event*", ENV{.INPUT_CLASS}=="?*", SYMLINK+="input/by-path/$env{ID_PATH}-event-$env{.INPUT_CLASS}" +# allow empty class for platform and usb devices; platform supports only a single interface that way +SUBSYSTEMS=="usb|platform", ENV{ID_PATH}=="?*", KERNEL=="event*", ENV{.INPUT_CLASS}=="", \ + SYMLINK+="input/by-path/$env{ID_PATH}-event" + +LABEL="persistent_input_end" diff --git a/rules/60-persistent-serial.rules b/rules/60-persistent-serial.rules new file mode 100644 index 0000000000..2948200c53 --- /dev/null +++ b/rules/60-persistent-serial.rules @@ -0,0 +1,20 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="persistent_serial_end" +SUBSYSTEM!="tty", GOTO="persistent_serial_end" +KERNEL!="ttyUSB[0-9]*|ttyACM[0-9]*", GOTO="persistent_serial_end" + +SUBSYSTEMS=="usb-serial", ENV{.ID_PORT}="$attr{port_number}" + +IMPORT{builtin}="path_id" +ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="", SYMLINK+="serial/by-path/$env{ID_PATH}" +ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="?*", SYMLINK+="serial/by-path/$env{ID_PATH}-port$env{.ID_PORT}" + +IMPORT{builtin}="usb_id" +ENV{ID_SERIAL}=="", GOTO="persistent_serial_end" +SUBSYSTEMS=="usb", ENV{ID_USB_INTERFACE_NUM}="$attr{bInterfaceNumber}" +ENV{ID_USB_INTERFACE_NUM}=="", GOTO="persistent_serial_end" +ENV{.ID_PORT}=="", SYMLINK+="serial/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$env{ID_USB_INTERFACE_NUM}" +ENV{.ID_PORT}=="?*", SYMLINK+="serial/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$env{ID_USB_INTERFACE_NUM}-port$env{.ID_PORT}" + +LABEL="persistent_serial_end" diff --git a/rules/60-persistent-storage-tape.rules b/rules/60-persistent-storage-tape.rules new file mode 100644 index 0000000000..f2eabd92a8 --- /dev/null +++ b/rules/60-persistent-storage-tape.rules @@ -0,0 +1,25 @@ +# do not edit this file, it will be overwritten on update + +# persistent storage links: /dev/tape/{by-id,by-path} + +ACTION=="remove", GOTO="persistent_storage_tape_end" + +# type 8 devices are "Medium Changers" +SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="8", IMPORT{program}="scsi_id --sg-version=3 --export --whitelisted -d $devnode", \ + SYMLINK+="tape/by-id/scsi-$env{ID_SERIAL}" + +SUBSYSTEM!="scsi_tape", GOTO="persistent_storage_tape_end" + +KERNEL=="st*[0-9]|nst*[0-9]", ATTRS{ieee1394_id}=="?*", ENV{ID_SERIAL}="$attr{ieee1394_id}", ENV{ID_BUS}="ieee1394" +KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" +KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", KERNELS=="[0-9]*:*[0-9]", ENV{.BSG_DEV}="$root/bsg/$id" +KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --whitelisted --export --device=$env{.BSG_DEV}", ENV{ID_BUS}="scsi" +KERNEL=="st*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}" +KERNEL=="nst*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}-nst" + +# by-path (parent device path) +KERNEL=="st*[0-9]|nst*[0-9]", IMPORT{builtin}="path_id" +KERNEL=="st*[0-9]", ENV{ID_PATH}=="?*", SYMLINK+="tape/by-path/$env{ID_PATH}" +KERNEL=="nst*[0-9]", ENV{ID_PATH}=="?*", SYMLINK+="tape/by-path/$env{ID_PATH}-nst" + +LABEL="persistent_storage_tape_end" diff --git a/rules/60-persistent-storage.rules b/rules/60-persistent-storage.rules new file mode 100644 index 0000000000..b74821edd4 --- /dev/null +++ b/rules/60-persistent-storage.rules @@ -0,0 +1,89 @@ +# do not edit this file, it will be overwritten on update + +# persistent storage links: /dev/disk/{by-id,by-uuid,by-label,by-path} +# scheme based on "Linux persistent device names", 2004, Hannes Reinecke + +# forward scsi device event to corresponding block device +ACTION=="change", SUBSYSTEM=="scsi", ENV{DEVTYPE}=="scsi_device", TEST=="block", ATTR{block/*/uevent}="change" + +ACTION=="remove", GOTO="persistent_storage_end" + +# enable in-kernel media-presence polling +ACTION=="add", SUBSYSTEM=="module", KERNEL=="block", ATTR{parameters/events_dfl_poll_msecs}=="0", ATTR{parameters/events_dfl_poll_msecs}="2000" + +SUBSYSTEM!="block", GOTO="persistent_storage_end" + +# skip rules for inappropriate block devices +KERNEL=="fd*|mtd*|nbd*|gnbd*|btibm*|dm-*|md*", GOTO="persistent_storage_end" + +# ignore partitions that span the entire disk +TEST=="whole_disk", GOTO="persistent_storage_end" + +# for partitions import parent information +ENV{DEVTYPE}=="partition", IMPORT{parent}="ID_*" + +# virtio-blk +KERNEL=="vd*[!0-9]", ATTRS{serial}=="?*", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/virtio-$env{ID_SERIAL}" +KERNEL=="vd*[0-9]", ATTRS{serial}=="?*", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/virtio-$env{ID_SERIAL}-part%n" + +# ATA devices with their own "ata" kernel subsystem +KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="ata", IMPORT{program}="ata_id --export $devnode" +# ATA devices using the "scsi" subsystem +KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", IMPORT{program}="ata_id --export $devnode" +# ATA/ATAPI devices (SPC-3 or later) using the "scsi" subsystem +KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", ATTRS{type}=="5", ATTRS{scsi_level}=="[6-9]*", IMPORT{program}="ata_id --export $devnode" + +# Run ata_id on non-removable USB Mass Storage (SATA/PATA disks in enclosures) +KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", ATTR{removable}=="0", SUBSYSTEMS=="usb", IMPORT{program}="ata_id --export $devnode" +# Otherwise fall back to using usb_id for USB devices +KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" + +# scsi devices +KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --whitelisted -d $devnode", ENV{ID_BUS}="scsi" +KERNEL=="cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --whitelisted -d $devnode", ENV{ID_BUS}="cciss" +KERNEL=="sd*|sr*|cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}" +KERNEL=="sd*|cciss*", ENV{DEVTYPE}=="partition", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}-part%n" + +# firewire +KERNEL=="sd*[!0-9]|sr*", ATTRS{ieee1394_id}=="?*", SYMLINK+="disk/by-id/ieee1394-$attr{ieee1394_id}" +KERNEL=="sd*[0-9]", ATTRS{ieee1394_id}=="?*", SYMLINK+="disk/by-id/ieee1394-$attr{ieee1394_id}-part%n" + +KERNEL=="mmcblk[0-9]", SUBSYSTEMS=="mmc", ATTRS{name}=="?*", ATTRS{serial}=="?*", ENV{ID_NAME}="$attr{name}", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/mmc-$env{ID_NAME}_$env{ID_SERIAL}" +KERNEL=="mmcblk[0-9]p[0-9]", ENV{ID_NAME}=="?*", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/mmc-$env{ID_NAME}_$env{ID_SERIAL}-part%n" +KERNEL=="mspblk[0-9]", SUBSYSTEMS=="memstick", ATTRS{name}=="?*", ATTRS{serial}=="?*", ENV{ID_NAME}="$attr{name}", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/memstick-$env{ID_NAME}_$env{ID_SERIAL}" +KERNEL=="mspblk[0-9]p[0-9]", ENV{ID_NAME}=="?*", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/memstick-$env{ID_NAME}_$env{ID_SERIAL}-part%n" + +# by-path (parent device path) +ENV{DEVTYPE}=="disk", DEVPATH!="*/virtual/*", IMPORT{builtin}="path_id" +ENV{DEVTYPE}=="disk", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}" +ENV{DEVTYPE}=="partition", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}-part%n" + +# skip unpartitioned removable media devices from drivers which do not send "change" events +ENV{DEVTYPE}=="disk", KERNEL!="sd*|sr*", ATTR{removable}=="1", GOTO="persistent_storage_end" + +# probe filesystem metadata of optical drives which have a media inserted +KERNEL=="sr*", ENV{DISK_EJECT_REQUEST}!="?*", ENV{ID_CDROM_MEDIA_TRACK_COUNT_DATA}=="?*", ENV{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}=="?*", \ + IMPORT{builtin}="blkid --offset=$env{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}" +# single-session CDs do not have ID_CDROM_MEDIA_SESSION_LAST_OFFSET +KERNEL=="sr*", ENV{DISK_EJECT_REQUEST}!="?*", ENV{ID_CDROM_MEDIA_TRACK_COUNT_DATA}=="?*", ENV{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}=="", \ + IMPORT{builtin}="blkid --noraid" + +# probe filesystem metadata of disks +KERNEL!="sr*", IMPORT{builtin}="blkid" + +# watch metadata changes by tools closing the device after writing +KERNEL!="sr*", OPTIONS+="watch" + +# by-label/by-uuid links (filesystem metadata) +ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_UUID_ENC}=="?*", SYMLINK+="disk/by-uuid/$env{ID_FS_UUID_ENC}" +ENV{ID_FS_USAGE}=="filesystem|other", ENV{ID_FS_LABEL_ENC}=="?*", SYMLINK+="disk/by-label/$env{ID_FS_LABEL_ENC}" + +# by-id (World Wide Name) +ENV{DEVTYPE}=="disk", ENV{ID_WWN_WITH_EXTENSION}=="?*", SYMLINK+="disk/by-id/wwn-$env{ID_WWN_WITH_EXTENSION}" +ENV{DEVTYPE}=="partition", ENV{ID_WWN_WITH_EXTENSION}=="?*", SYMLINK+="disk/by-id/wwn-$env{ID_WWN_WITH_EXTENSION}-part%n" + +# by-partlabel/by-partuuid links (partition metadata) +ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_ENTRY_UUID}=="?*", SYMLINK+="disk/by-partuuid/$env{ID_PART_ENTRY_UUID}" +ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_ENTRY_NAME}=="?*", SYMLINK+="disk/by-partlabel/$env{ID_PART_ENTRY_NAME}" + +LABEL="persistent_storage_end" diff --git a/rules/75-net-description.rules b/rules/75-net-description.rules new file mode 100644 index 0000000000..ce57d48e86 --- /dev/null +++ b/rules/75-net-description.rules @@ -0,0 +1,14 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="net_end" +SUBSYSTEM!="net", GOTO="net_end" + +SUBSYSTEMS=="usb", ENV{ID_MODEL}=="", IMPORT{builtin}="usb_id" +SUBSYSTEMS=="usb", IMPORT{builtin}="usb-db" +SUBSYSTEMS=="usb", ATTRS{idVendor}!="", ATTRS{idProduct}!="", ENV{ID_VENDOR_ID}="$attr{idVendor}", ENV{ID_MODEL_ID}="$attr{idProduct}" +SUBSYSTEMS=="usb", GOTO="net_end" + +SUBSYSTEMS=="pci", IMPORT{builtin}="pci-db" +SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}" + +LABEL="net_end" diff --git a/rules/75-tty-description.rules b/rules/75-tty-description.rules new file mode 100644 index 0000000000..2e63e140cb --- /dev/null +++ b/rules/75-tty-description.rules @@ -0,0 +1,14 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="tty_end" +SUBSYSTEM!="tty", GOTO="tty_end" + +SUBSYSTEMS=="usb", ENV{ID_MODEL}=="", IMPORT{builtin}="usb_id" +SUBSYSTEMS=="usb", IMPORT{builtin}="usb-db" +SUBSYSTEMS=="usb", ATTRS{idVendor}!="", ATTRS{idProduct}!="", ENV{ID_VENDOR_ID}="$attr{idVendor}", ENV{ID_MODEL_ID}="$attr{idProduct}" +SUBSYSTEMS=="usb", GOTO="tty_end" + +SUBSYSTEMS=="pci", IMPORT{builtin}="pci-db" +SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}" + +LABEL="tty_end" diff --git a/rules/78-sound-card.rules b/rules/78-sound-card.rules new file mode 100644 index 0000000000..e564441893 --- /dev/null +++ b/rules/78-sound-card.rules @@ -0,0 +1,89 @@ +# do not edit this file, it will be overwritten on update + +SUBSYSTEM!="sound", GOTO="sound_end" + +ACTION=="add|change", KERNEL=="controlC*", ATTR{../uevent}="change" +ACTION!="change", GOTO="sound_end" + +# Ok, we probably need a little explanation here for what the two lines above +# are good for. +# +# The story goes like this: when ALSA registers a new sound card it emits a +# series of 'add' events to userspace, for the main card device and for all the +# child device nodes that belong to it. udev relays those to applications, +# however only maintains the order between father and child, but not between +# the siblings. The control device node creation can be used as synchronization +# point. All other devices that belong to a card are created in the kernel +# before it. However unfortunately due to the fact that siblings are forwarded +# out of order by udev this fact is lost to applications. +# +# OTOH before an application can open a device it needs to make sure that all +# its device nodes are completely created and set up. +# +# As a workaround for this issue we have added the udev rule above which will +# generate a 'change' event on the main card device from the 'add' event of the +# card's control device. Due to the ordering semantics of udev this event will +# only be relayed after all child devices have finished processing properly. +# When an application needs to listen for appearing devices it can hence look +# for 'change' events only, and ignore the actual 'add' events. +# +# When the application is initialized at the same time as a device is plugged +# in it may need to figure out if the 'change' event has already been triggered +# or not for a card. To find that out we store the flag environment variable +# SOUND_INITIALIZED on the device which simply tells us if the card 'change' +# event has already been processed. + +KERNEL!="card*", GOTO="sound_end" + +ENV{SOUND_INITIALIZED}="1" + +SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" +SUBSYSTEMS=="usb", IMPORT{builtin}="usb-db" +SUBSYSTEMS=="usb", GOTO="skip_pci" + +SUBSYSTEMS=="firewire", ATTRS{vendor_name}=="?*", ATTRS{model_name}=="?*", \ + ENV{ID_BUS}="firewire", ENV{ID_VENDOR}="$attr{vendor_name}", ENV{ID_MODEL}="$attr{model_name}" +SUBSYSTEMS=="firewire", ATTRS{guid}=="?*", ENV{ID_ID}="firewire-$attr{guid}" +SUBSYSTEMS=="firewire", GOTO="skip_pci" + + +SUBSYSTEMS=="pci", IMPORT{builtin}="pci-db" +SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}" + +LABEL="skip_pci" + +ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="?*", ENV{ID_ID}="$env{ID_BUS}-$env{ID_SERIAL}-$env{ID_USB_INTERFACE_NUM}-$attr{id}" +ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="", ENV{ID_ID}="$env{ID_BUS}-$env{ID_SERIAL}-$attr{id}" + +IMPORT{builtin}="path_id" + +# The values used here for $SOUND_FORM_FACTOR and $SOUND_CLASS should be kept +# in sync with those defined for PulseAudio's src/pulse/proplist.h +# PA_PROP_DEVICE_FORM_FACTOR, PA_PROP_DEVICE_CLASS properties. + +# If the first PCM device of this card has the pcm class 'modem', then the card is a modem +ATTR{pcmC%nD0p/pcm_class}=="modem", ENV{SOUND_CLASS}="modem", GOTO="sound_end" + +# Identify cards on the internal PCI bus as internal +SUBSYSTEMS=="pci", DEVPATH=="*/0000:00:??.?/sound/*", ENV{SOUND_FORM_FACTOR}="internal", GOTO="sound_end" + +# Devices that also support Image/Video interfaces are most likely webcams +SUBSYSTEMS=="usb", ENV{ID_USB_INTERFACES}=="*:0e????:*", ENV{SOUND_FORM_FACTOR}="webcam", GOTO="sound_end" + +# Matching on the model strings is a bit ugly, I admit +ENV{ID_MODEL}=="*[Ss]peaker*", ENV{SOUND_FORM_FACTOR}="speaker", GOTO="sound_end" +ENV{ID_MODEL_FROM_DATABASE}=="*[Ss]peaker*", ENV{SOUND_FORM_FACTOR}="speaker", GOTO="sound_end" + +ENV{ID_MODEL}=="*[Hh]eadphone*", ENV{SOUND_FORM_FACTOR}="headphone", GOTO="sound_end" +ENV{ID_MODEL_FROM_DATABASE}=="*[Hh]eadphone*", ENV{SOUND_FORM_FACTOR}="headphone", GOTO="sound_end" + +ENV{ID_MODEL}=="*[Hh]eadset*", ENV{SOUND_FORM_FACTOR}="headset", GOTO="sound_end" +ENV{ID_MODEL_FROM_DATABASE}=="*[Hh]eadset*", ENV{SOUND_FORM_FACTOR}="headset", GOTO="sound_end" + +ENV{ID_MODEL}=="*[Hh]andset*", ENV{SOUND_FORM_FACTOR}="handset", GOTO="sound_end" +ENV{ID_MODEL_FROM_DATABASE}=="*[Hh]andset*", ENV{SOUND_FORM_FACTOR}="handset", GOTO="sound_end" + +ENV{ID_MODEL}=="*[Mm]icrophone*", ENV{SOUND_FORM_FACTOR}="microphone", GOTO="sound_end" +ENV{ID_MODEL_FROM_DATABASE}=="*[Mm]icrophone*", ENV{SOUND_FORM_FACTOR}="microphone", GOTO="sound_end" + +LABEL="sound_end" diff --git a/rules/80-drivers.rules b/rules/80-drivers.rules new file mode 100644 index 0000000000..38ebfeb0e6 --- /dev/null +++ b/rules/80-drivers.rules @@ -0,0 +1,12 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="drivers_end" + +DRIVER!="?*", ENV{MODALIAS}=="?*", IMPORT{builtin}="kmod load $env{MODALIAS}" +SUBSYSTEM=="tifm", ENV{TIFM_CARD_TYPE}=="SD", IMPORT{builtin}="kmod load tifm_sd" +SUBSYSTEM=="tifm", ENV{TIFM_CARD_TYPE}=="MS", IMPORT{builtin}="kmod load tifm_ms" +SUBSYSTEM=="memstick", IMPORT{builtin}="kmod load ms_block mspro_block" +SUBSYSTEM=="i2o", IMPORT{builtin}="kmod load i2o_block" +SUBSYSTEM=="module", KERNEL=="parport_pc", IMPORT{builtin}="kmod load ppdev" + +LABEL="drivers_end" diff --git a/rules/95-udev-late.rules b/rules/95-udev-late.rules new file mode 100644 index 0000000000..eca0faa5c5 --- /dev/null +++ b/rules/95-udev-late.rules @@ -0,0 +1,4 @@ +# do not edit this file, it will be overwritten on update + +# run a command on remove events +ACTION=="remove", ENV{REMOVE_CMD}!="", RUN+="$env{REMOVE_CMD}" diff --git a/rules/99-systemd.rules.in b/rules/99-systemd.rules.in new file mode 100644 index 0000000000..d306f71b63 --- /dev/null +++ b/rules/99-systemd.rules.in @@ -0,0 +1,55 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +ACTION=="remove", GOTO="systemd_end" + +SUBSYSTEM=="tty", KERNEL=="tty[0-9]|tty1[0-2]", TAG+="systemd" +SUBSYSTEM=="tty", KERNEL=="tty[a-zA-Z]*|hvc*|xvc*|hvsi*", TAG+="systemd" + +KERNEL=="vport*", TAG+="systemd" + +SUBSYSTEM=="block", KERNEL!="ram*|loop*", TAG+="systemd" +SUBSYSTEM=="block", KERNEL!="ram*|loop*", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", ENV{SYSTEMD_READY}="0" + +# Ignore encrypted devices with no identified superblock on it, since +# we are probably still calling mke2fs or mkswap on it. + +SUBSYSTEM=="block", KERNEL!="ram*|loop*", ENV{DM_UUID}=="CRYPT-*", ENV{ID_PART_TABLE_TYPE}=="", ENV{ID_FS_USAGE}=="", ENV{SYSTEMD_READY}="0" + +# We need a hardware independent way to identify network devices. We +# use the /sys/subsystem path for this. Current vanilla kernels don't +# actually support that hierarchy right now, however upcoming kernels +# will. HAL and udev internally support /sys/subsystem already, hence +# it should be safe to use this here, too. This is mostly just an +# identification string for systemd, so whether the path actually is +# accessible or not does not matter as long as it is unique and in the +# filesystem namespace. +# +# http://git.kernel.org/?p=linux/hotplug/udev.git;a=blob;f=libudev/libudev-enumerate.c;h=da831449dcaf5e936a14409e8e68ab12d30a98e2;hb=HEAD#l742 + +SUBSYSTEM=="net", KERNEL!="lo", TAG+="systemd", ENV{SYSTEMD_ALIAS}="/sys/subsystem/net/devices/$name" +SUBSYSTEM=="bluetooth", TAG+="systemd", ENV{SYSTEMD_ALIAS}="/sys/subsystem/bluetooth/devices/%k" + +SUBSYSTEM=="bluetooth", TAG+="systemd", ENV{SYSTEMD_WANTS}="bluetooth.target" +ENV{ID_SMARTCARD_READER}=="*?", TAG+="systemd", ENV{SYSTEMD_WANTS}="smartcard.target" +SUBSYSTEM=="sound", KERNEL=="card*", TAG+="systemd", ENV{SYSTEMD_WANTS}="sound.target" + +SUBSYSTEM=="printer", TAG+="systemd", ENV{SYSTEMD_WANTS}="printer.target" +SUBSYSTEM=="usb", KERNEL=="lp*", TAG+="systemd", ENV{SYSTEMD_WANTS}="printer.target" +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0701??:*", TAG+="systemd", ENV{SYSTEMD_WANTS}="printer.target" + +# Apply sysctl variables to network devices (and only to those) as they appear. + +SUBSYSTEM=="net", KERNEL!="lo", RUN+="@rootlibexecdir@/systemd-sysctl --prefix=/proc/sys/net/ipv4/conf/$name --prefix=/proc/sys/net/ipv4/neigh/$name --prefix=/proc/sys/net/ipv6/conf/$name --prefix=/proc/sys/net/ipv6/neigh/$name" + +# Asynchronously mount file systems implemented by these modules as +# soon as they are loaded. + +SUBSYSTEM=="module", KERNEL=="fuse", ACTION=="add", TAG+="systemd", ENV{SYSTEMD_WANTS}="sys-fs-fuse-connections.mount" +SUBSYSTEM=="module", KERNEL=="configfs", ACTION=="add", TAG+="systemd", ENV{SYSTEMD_WANTS}="sys-kernel-config.mount" + +LABEL="systemd_end" diff --git a/src/99-systemd.rules.in b/src/99-systemd.rules.in deleted file mode 100644 index d306f71b63..0000000000 --- a/src/99-systemd.rules.in +++ /dev/null @@ -1,55 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. - -ACTION=="remove", GOTO="systemd_end" - -SUBSYSTEM=="tty", KERNEL=="tty[0-9]|tty1[0-2]", TAG+="systemd" -SUBSYSTEM=="tty", KERNEL=="tty[a-zA-Z]*|hvc*|xvc*|hvsi*", TAG+="systemd" - -KERNEL=="vport*", TAG+="systemd" - -SUBSYSTEM=="block", KERNEL!="ram*|loop*", TAG+="systemd" -SUBSYSTEM=="block", KERNEL!="ram*|loop*", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", ENV{SYSTEMD_READY}="0" - -# Ignore encrypted devices with no identified superblock on it, since -# we are probably still calling mke2fs or mkswap on it. - -SUBSYSTEM=="block", KERNEL!="ram*|loop*", ENV{DM_UUID}=="CRYPT-*", ENV{ID_PART_TABLE_TYPE}=="", ENV{ID_FS_USAGE}=="", ENV{SYSTEMD_READY}="0" - -# We need a hardware independent way to identify network devices. We -# use the /sys/subsystem path for this. Current vanilla kernels don't -# actually support that hierarchy right now, however upcoming kernels -# will. HAL and udev internally support /sys/subsystem already, hence -# it should be safe to use this here, too. This is mostly just an -# identification string for systemd, so whether the path actually is -# accessible or not does not matter as long as it is unique and in the -# filesystem namespace. -# -# http://git.kernel.org/?p=linux/hotplug/udev.git;a=blob;f=libudev/libudev-enumerate.c;h=da831449dcaf5e936a14409e8e68ab12d30a98e2;hb=HEAD#l742 - -SUBSYSTEM=="net", KERNEL!="lo", TAG+="systemd", ENV{SYSTEMD_ALIAS}="/sys/subsystem/net/devices/$name" -SUBSYSTEM=="bluetooth", TAG+="systemd", ENV{SYSTEMD_ALIAS}="/sys/subsystem/bluetooth/devices/%k" - -SUBSYSTEM=="bluetooth", TAG+="systemd", ENV{SYSTEMD_WANTS}="bluetooth.target" -ENV{ID_SMARTCARD_READER}=="*?", TAG+="systemd", ENV{SYSTEMD_WANTS}="smartcard.target" -SUBSYSTEM=="sound", KERNEL=="card*", TAG+="systemd", ENV{SYSTEMD_WANTS}="sound.target" - -SUBSYSTEM=="printer", TAG+="systemd", ENV{SYSTEMD_WANTS}="printer.target" -SUBSYSTEM=="usb", KERNEL=="lp*", TAG+="systemd", ENV{SYSTEMD_WANTS}="printer.target" -SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0701??:*", TAG+="systemd", ENV{SYSTEMD_WANTS}="printer.target" - -# Apply sysctl variables to network devices (and only to those) as they appear. - -SUBSYSTEM=="net", KERNEL!="lo", RUN+="@rootlibexecdir@/systemd-sysctl --prefix=/proc/sys/net/ipv4/conf/$name --prefix=/proc/sys/net/ipv4/neigh/$name --prefix=/proc/sys/net/ipv6/conf/$name --prefix=/proc/sys/net/ipv6/neigh/$name" - -# Asynchronously mount file systems implemented by these modules as -# soon as they are loaded. - -SUBSYSTEM=="module", KERNEL=="fuse", ACTION=="add", TAG+="systemd", ENV{SYSTEMD_WANTS}="sys-fs-fuse-connections.mount" -SUBSYSTEM=="module", KERNEL=="configfs", ACTION=="add", TAG+="systemd", ENV{SYSTEMD_WANTS}="sys-kernel-config.mount" - -LABEL="systemd_end" diff --git a/src/udev/.gitignore b/src/udev/.gitignore index fa3500ba96..e697a57ec3 100644 --- a/src/udev/.gitignore +++ b/src/udev/.gitignore @@ -1,31 +1,4 @@ -*~ -*.o -*.a -*.lo -*.la -.libs -.deps -.dirstamp -Makefile -Makefile.in -/aclocal.m4 -/autom4te.cache -/config.h -/config.h.in -/config.log -/config.status -/config.guess -/config.sub -/libtool -/ltmain.sh -/install-sh -/missing -/configure -/stamp-h1 -/depcomp /gtk-doc.make -/build-aux -/udev-test-install /udevd /udevadm /test-udev @@ -38,3 +11,8 @@ Makefile.in /v4l_id /keymap /scsi_id +*.[78] +*.html +udev.pc +libudev.pc +udev*.service diff --git a/src/udev/COPYING b/src/udev/COPYING deleted file mode 100644 index d159169d10..0000000000 --- a/src/udev/COPYING +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/src/udev/ChangeLog b/src/udev/ChangeLog deleted file mode 100644 index dd58138263..0000000000 --- a/src/udev/ChangeLog +++ /dev/null @@ -1,6387 +0,0 @@ -Summary of changes from v181 to v182 -============================================ - -Kay Sievers (22): - build-sys: unpack test sysfs only for 'make check' - build-sys: add --disable-manpages - update sd-daemon files - test: remove outdated key attributes - update TOO - builtin: path_id - remove dead cciss code - rules: do not create by-id/scsi-* links for ATA devices - remove udev-acl - udev.conf - do not set any value by default - move src/extras subdirectories to src/ - rules: delete outdated 30-kernel-compat.rules - rules: move 42-qemu-usb.rules to rules/ dir - remove edd_id extra - build-sys: remove empty directory - rules: delete s390 rules, they will move to s390utils - update TODO - rules: move all rules to top level rules/ dir - extras: path_id - skip ATA transport class devices - extras: path_id - add comment about readdir() rebase logic - extras: ata_id - do not log error if HDIO_GET_IDENTITY fails - rules sort order: /lib, /run, /etc - build-sys: place build binaries in the root - -Matthew Garrett (1): - rules: Enable USB autosuspend on more USB HID devices - - -Summary of changes from v180 to v181 -============================================ - -Andreas Schwab (1): - ata_id: fix identify string fixup - -Bruno Redondi (1): - keymap: Add Fujitsu Siemens Amilo Li 2732 - -James M. Leddy (1): - keymap: Fix touchpad toggle button on Lenovo Ideapad - -Kay Sievers (4): - configure: show ROOTPREFIX in firmware path option help text - extras: cdrom_id - create /dev/cdrom and conditionally /dev/dvd for sr0 - extras: cdrom_id - create only /dev/cdrom - ata_id: whitespace fixes - -Lucas De Marchi (1): - builtin: kmod - depend on libkmod >= 5 - - -Summary of changes from v179 to v180 -============================================ - -Kay Sievers (4): - Makefile: update kernel.org hooks - build-sys: we need to install shipped man pages without xsltproc installed - builtin: blkid - add missing ID_ prefix for PART_ENTRY_* keys - do not stop rule processing when device node is no longer around - - -Summary of changes from v178 to v179 -============================================ - -Kay Sievers (8): - fix some fallout from tab removal - use devnode() for $name not sysname(), device nodes might be in a subdirectory - print warning when rules try to rename kernel device nodes - move variable inside condition - update TODO - build-sys: enable everything for 'make distcheck' - use sysname() for devices without a device node - fix path to extras - - -Summary of changes from v177 to v178 -============================================ - -Evan Nemerson (1): - gudev: several minor introspection fixes - -Kay Sievers (7): - Makefile: update kernel.org doc hooks for kup - builtin: blkid - add missing ID_ prefix - udevd: kill hanging event processes after 30 seconds - Makefile: switch from .asc to .sign - rules: rtc - point /dev/rtc symlink to 'hctosys' device - warn about deprecated RUN+="socket:" use - libudev: do not set DEVNAME= twice - -Martin Pitt (4): - keymap: Fix rfkill button on Hewlett-Packard HP ProBook - keymap: Fix eject button on Samsung 700Z series - keymap: Fix keyboard brightness keys on Samsung 700Z series - keymap: Add Alienware M14xR1 - - -Summary of changes from v176 to v177 -============================================ - -Kay Sievers (3): - Makefile: update kernel.org sign and upload hook - rule_generator: fix to install rules in rules.d/ - rule_generator: use += for dist_udevhome_DATA - - -Summary of changes from v175 to v176 -============================================ - -Alan Stern (1): - [PATCH[ udev: ata_id: Fix length of INQUIRY command - -Kay Sievers (61): - libudev: print log_fn address instead of ctx when setting logging function - do not ship autogen.sh in the tarball - man: clarify 'config file stack' - rename 'init' directory to 'systemd' - systemd: use PassCred=yes - use libexecdir, bindir, sbindir, switch to /usr/lib/udev in documentation - configure: fix typo - make: do not (mis-)use the config file generator, create .xz tarball - prepare builtins for blkid and kmod - add builtin load/unload initializers - build argv[] for builtin commands - update blkid builtin - rules: switch to built-in blkid - rules: do not preprocess 60-persistent-storage.rules - buildsys: disable tar.gz - builtin: blkid - add missing newline - builtin: blkid - add missing ID_FS_USAGE - builtin: kmod - switch modprobe to builtin - rules: do not preprocess 80-drivers.rules + 75-probe_mtd.rules - builtin: apply format string - remove last sbindir use - update NEWS - autogen.sh: moce CFLAGS from to configure.ac; print common ./configure options - builtin: kmod - link against libkmod - add copyright - builtin: kmod - reload index when rules are reloaded - builtin: rename load()/unload() to init()/exit() - invalidate rules and kmod index with 'udevadm control --reload' - update NEWS - builtin: firmware - move 'firmware' tool to builtins - builtin: firmware - add missing file - builtin: kmod - hook up udev main logging to libkmod - make: introduce --with-rootprefix= - update NEWS - move rules dirs to udev context; replace inotify with time-controlled stat() - udevd: always create runtime dir - builtin: move usb-db, pci-db to builtins - builtin: kmod - switch to kmod_module_probe_insert_module() - udevd: remove TIMEOUT= handling - update README - systemd: rename PassCred= to PsssCredentials= - remove mknod() logic and rely on 'devtmpfs' - builtin: kmod - hook up kmod_validate_resources() - build-sys: use use ${ac_default_prefix} - require kmod >= 3 - build-sys: use --libexecdir=/usr/lib instead of /usr/lib/udev - autogen.sh: enable git pre-commit - merge udev/, libudev/, systemd/ files in src/; move extras/ to src/ - replace unpacked sysfs test tree 'test/sys/' with packed tarball - rules: delete arch specific rules - doc: fix out of tree build (copy from libkmod) - autogen.sh: add CFLAGS and print entire line, so that mouse copy/paste works - build-sys: try to build without installed xsltproc - add test/src to .gitignore - tabs are as useful as a hole in the head - autogen.sh: makedev() misteriously breaks with -O0 here, use -O1 for now - fix debug message - add .vimrc - cdrom_id: int -> bool - fix compiler warning - man: mention that no daemons should be started by udev - -Lucas De Marchi (1): - builtin: kmod - log if modules are blacklisted - -Luis Felipe Strano Moraes (1): - Switch spawn_read to void and remove useless stores there. - -Martin Pitt (1): - 75-persistent-net-generator.rules: Add Xen - -Mike Frysinger (1): - hwdb: drop useless line freeing - -Sjoerd Simons (1): - keymap: Add Lenovo Thinkpad X220 Tablet - -Ville Skyttä (1): - man: spelling fix - - -Summary of changes from v174 to v175 -============================================ - -David Zeuthen (2): - gudev: Use strtoul to parse unsigned 64-bit integers - gudev: Use g_ascii_strtoull() instead of strtoul() - -Harald Hoyer (1): - extras/keymap/findkeyboards: beautify shell code and get rid of grep - -Jerone Young (1): - keymap: Fix micmute remap for Lenovo Thinkpads - -Kay Sievers (7): - make: add gpg signing bits - ignore entire rules line if unknown keys are used - do not skip /dev/{disk,char}/M:m removal when the device node is already gone - replace AC_DISABLE_STATIC with LT_INIT([disable-static]) - make: tweak some autofoo according to Flameeyes' recommendations for libabc - rules: restore rule to set cdrom group for optical drives - rules: fix typo - -Martin Pitt (8): - check-keymaps.sh: Allow running separately - extras/keymap/findkeyboards: Filter out non-event devices - findkeyboards: Consistently use spaces instead of tabs - keymap: Fix stuck keys on GIGABYTE i1520M - keymap: More Asus module variants - keymap: Fix "internet" key on HP G62 - keymap: Fix bluetooth key on Acer TravelMate 7720 - keymap: Fix stuck keys on BenQ nScreen - - -Summary of changes from v173 to v174 -============================================ - -David Zeuthen (1): - ata_id: Check for Compact Flash card - -Jerone Young (1): - Add mic mute keycode support for Lenovo Thinkpad USB keyboard - -Kay Sievers (34): - gtk-doc: delete empty files - libudev: list - use binary search for list lookup - rules: move input_id to default rules - implement path_id, usb_id, input_id as built-in command - do not remove static nodes on module unload - rules: remove legacy rules for cdrom and usb printer - update TODO - preserve 'sticky bit' on 'add/change' events - libudev: util_get_sys_(subsystem,driver}() -> util_get_sys_core_link_value() - export USEC_INITIALIZED= and take timestamp on message receive time - libudev: udev_device_get_sysattr_value() return syspath of custom links - libudev: list - properly sort linked list not only the index - mknod: do not complain about existing node - update README - libudev: fix typo in documentation - rules: fuse: do not mount fusectl from udev rules - keymap: add genius keymap to Makefile - update NEWS - usb_id: can't use global variables when used as built-in - remove 'udevadm trigger --type=failed' and SYSFS, ID, BUS keys - libudev: export udev_util_encode_string() - update TODO - systemd: no not start udev in a container - systemd: no not start udev in a container - delete left-over files in extras/ - systemd: update drop-in sd-daemon files - udevadm: control - use /run/udev/control socket instead of abstract namespace one - udevd: control - no not delete socket file when --daemon is used - udev_ctrl_cleanup()- accept NULL as argument - update NEWS - udevd: install into /lib/udev instead of /sbin - udevd: add missing braces - systemd: use ConditionCapability=CAP_MKNOD instead of ConditionVirtualization=!container - rules: do not load sg module - -Kir Kolyshkin (1): - keymap: add Genius SlimStar 320 - -Martin Pitt (1): - keymap: Update Acer Aspire 5920g - -Matthias Clasen (1): - make: allow to pass ${ACLOCAL_FLAGS} - -Paul Fox (1): - keymap: update the OLPC keymap for correct function key behavior - -Petr Uzel (1): - udevadm: settle - return failure if unknown option is given - -Steve Langasek (1): - udevd: exit - process events before signals in worker - -Thomas Hood (2): - keymap: Support keymap overrides in /etc/udev/keymaps - keymap: Support for microphone mute button on ThinkPad X220 et al - - -Summary of changes from v172 to v173 -============================================ - -Allin Cottrell (1): - configure: allow to disable mtd_probe - -Kay Sievers (15): - make: fix 'make tar-sync' - udevd: use 'uptime' in debug timestamp - udevd: fix (recently) broken static node permission setting - rules: mount fuse filesystem only 'add' - udevadm: move udevadm command descriptions into their files - udev-acl: skip ACLs when systemd is running, disable by default - do not delete database when renaming netif, the db name does not change anymore - do not allow kernel properties to be set by udev rules - configure: reorder options - rules: input - do not create (broken) links for bluetooth devices - rules: serial - do not export ID_PORT, use ID_USB_INTERFACE_NUM - rules: sound - instead of ID_IFACE use standard ID_USB_INTERFACE_NUM - keymap: do not run usb_id for bluetooth devices - udevadm: trigger --type=failed - log deprecation warning - udevd: debug - put timestamp in [] - -Martin Pitt (4): - gudev: Ship JavaScript examples - scsi_id: Ship README - Remove obsolete extras/scsi_id/scsi_id.config - keymap: Only run on key devices - - -Summary of changes from v171 to v172 -============================================ - -Bastien Nocera (3): - accelerometer: add orientation property - udev-acl: fix memleak - accelerometer: add documentation - -Harald Hoyer (2): - udevadm-*.c: return != 0, if unknown option given - udev/udevadm-monitor.c: fixed misplaced brace - -Kay Sievers (33): - rules: apply 'audio' group of the static snd/{seq,timer} nodes - Makefile: add tar-sync - rules: static_node - use 0660 if group is given to get the cigar - rule-syntax-check.py: use print() - make: use 'git tag' - rules: run input_id for main input devices too - update TODO - configure: add AC_CONFIG_AUX_DIR, AC_CONFIG_SRCDIR - cdrom_id: add tray lock and eject handling - rules: enable in-kernel media-presence polling - update TODO - delete mobile-action-modeswitch which has moved to usb_modeswitch - libudev: enumerate - scan /sys/module - rules: move polling rule above 'block' match - libudev: monitor - update doc - rules: set polling value only if it is disabled - libudev: device - fix udev_device_get_tags_list_entry() to always load database - rules: remove redundant MODE="0664" from lp rules - rules: fix wrong wildcard match, we always need a ':*' at the end - libudev: device - export udev_device_has_tag() - path_id: add missing '-' to tape suffix - path_id: add ID_PATH_TAG= to be used in udev tags - enforce valid TAG+= names - update TODO - libudev: device - add udev_device_has_tag() to libudev.h and gtk-doc - libudev: enumerate - add udev_enumerate_add_match_parent() - libudev: enumerate - include parent device itself with match_parent() - libudev: enumerate - clarify documentation - path_id: recognize ACPI parent devices - rules: input - call path_id for ACPI devices - udevadm: monitor - use uptime to match the kernel's timestamp - libudev: ctrl - move code to udev directory - update sd-daemon.[ch] - -Keshav P.R (1): - rules: support for gpt partition uuid/label - -Lee, Chun-Yi (1): - Support more MSI notebook by using asterisk on dmi vendor name - -Marco d'Itri (1): - Add missing commas to 95-keymap.rules - -Martin Pitt (3): - keymap: Add Microsoft Natural Keyboard - keymap: Add force-release quirk for Hannspree SN10. - keymap: Add slight name variations of Toshiba Satellites - -Peter Jones (1): - ata_id: show the error message when HDIO_GET_IDENTITY fails - - -Summary of changes from v170 to v171 -============================================ - -Kay Sievers (17): - libudev: export symbols explicitely and individually from C code not from separate file or prefix match - libudev: device - make a bunch of symbols static - systemd: Replace Requires= with Wants=, run trigger in parallel - systemd: sort trigger after socket - systemd: trigger - run after udev.service (for now) - systemd: set socket buffer size to 128 MB like udev has - update TODO - update TODO - libudev: monitor - use SOCK_NONBLOCK - systemd: split socket file - systemd: add missing socket files - rules: fix whitespace - rules: implement TAGS== match - libudev: enumerate - do not ignore other matches when add_match_tag() is used - rules: support substitutions in TAG= - path_id: allow to be asked about usb_devices not only usb_interfaces - systemd: run udev.service and udev-trigger.service in parallel - -Scott James Remnant (1): - configure: allow usb.ids location to be specified - - -Summary of changes from v169 to v170 -============================================ - -Kay Sievers (1): - libudev: ctrl - properly wait for incoming message after connect - -Michal Soltys (1): - configure.ac: fixes for rule_generator and modeswitch - - -Summary of changes from v168 to v169 -============================================ - -Kay Sievers (26): - simplify rules file overwrite logic - libudev: list - use bit flags for 'sort' and 'unique' - libudev: queue - _unref() should return the object - remove dead fstab_import files - hid2hci: prepare move to bluez package - set event timeout to 60 sec and settle timeout to 120 - udevd: improve error message in case exec() fails - configure: allow to enable/disable extras individually - delete hid2hci which moved to the bluez tree - update TODO/NEWS - bump requirement to Linux kernel 2.6.32 and ARM 2.6.36 - libudev: ctrl - log accept4() errors - update NEWS - update INSTALL, NEWS, configure comment, queue doc - update TODO - udevd: create queue file before daemonizing to reliably block 'settle' - udevd: remove left-over SIGALRM - gudev: silent gtk-doc warnings - cdrom_id: remove unused --export switch to silent gcc - libudev: queue - always rebuild queue file when nothing is queued anymore - libudev: device - use DEVMODE from kernel as the default mode - update TODO - Merge branch 'docs/udev.xml' of git://github.com/mfwitten/udev - udate TODO, NEWS, INSTALL - build: use --gc-sections, -fvisibility=hidden - udevadm: settle: wake up more often if --seq-start= or --exit-if-exists= is used - -Koen Kooi (1): - configure: reintroduce introspection flags to fix crosscompilation - -Michael Witten (36): - Docs: udev.xml: Offset daemon name with commas - Docs: udev.xml: Remove commas (and unnecessary repetition) - Docs: udev.xml: `are' -> `is'; the subject is `Access' - Docs: udev.xml: Use present tense - Docs: udev.xml: Clarification through proper wording - Docs: udev.xml: `,' -> `;' - Docs: udev.xml: `key value' -> `key-value' - Docs: udev.xml: `,' -> `:' - Docs: udev.xml: Use `assignment' consistently - Docs: udev.xml: `comma-separated' is a better description - Docs: udev.xml: Remove unnecessary repitition - Docs: udev.xml: Add a few more words for context - Docs: udev.xml: Use `unless' for clarity - Docs: udev.xml: Clarify PROGRAM key - Docs: udev.xml: `a shell style' -> `shell-style' - Docs: udev.xml: Clean `*' description - Docs: udev.xml: Clean character range description - Docs: udev.xml: Clean up description of NAME assignment key - Docs: udev.xml: Clean up description of SYMLINK assignment key - Docs: udev.xml: Clean up description of ENV assignment key - Docs: udev.xml: Clean up description of RUN assignment key - Docs: udev.xml: Clean up description of LABEL assignment key - Docs: udev.xml: Add missing `.' - Docs: udev.xml: `which' -> `content of which' - Docs: udev.xml: `commandline' -> `command line' - Docs: udev.xml: Clean up WAIT_FOR description - Docs: udev.xml: `a' -> `the' - Docs: udev.xml: Clean up introduction to substitutions. - Docs: udev.xml: Use normal sentence structure - Docs: udev.xml: Actually make a separate paragraph - Docs: udev.xml: Add comma - Docs: udev.xml: `char' -> `character' - Docs: udev.xml: `comma-separated' is a better description - Docs: udev.xml: Clarify through a change in word ordering - Docs: udev.xml: Improved word order - Docs: udev.xml: Fix dangling modifier - -Nix (1): - libudev: queue - accept NULL passed into udev_queue_export_cleanup() - - -Summary of changes from v167 to v168 -============================================ - -David Zeuthen (1): - Run ata_id on non-removable USB devices - -Harald Hoyer (1): - udevd: clarify worker exit status - -Kay Sievers (35): - version bump - systemd: let settle depend on trigger, do not block basic with trigger - selinux: do not label files in runtime dir - selinux: firmware - do not label files in runtime dir - udevadm: control - add --exit - trivial cleanups - udevd: log warning if /run is not writable - libudev: ctrl - fix refcounting in connection handling - udevadm: settle - watch queue file - libudev: bump revision - udevadm: info --cleanup-db - udevd: do not nice processes - "db_persist=" -> "db_persist" - udevd: move OOM disable into --daemon option - systemd: add OOMScoreAdjust=-1000 - require explicit "db_persist" to exclude device info from --db-cleanup - udevd: get netlink socket from systemd - fix more warnings - libudev: ctrl, monitor - use SOCK_NONBLOCK - systemd: socket -> sockets - udevadm: monitor - use epoll - libudev: test - use epoll - udevadm: test - use printf() instead of info() for non-debug output - use 'else if' in epoll event array loop - libudev: run_program() - select() -> epoll - udevd: ppoll() -> epoll + signalfd - Merge branch 'docs/README' of git://github.com/mfwitten/udev - timeout handling without alarm() - udevadm: settle - kill alarm() - udevd: netif rename - use ifindex for temporary name - udevd: always use udevd[] log prefix - udevd: rules files - accept empty or /dev/null links - udevd: log signal number when spawned processes fail - systemd: Reqires= -> Wants=udev.socket - udevd, udev-event: sync waitpid() error handling - -Lee, Chun-Yi (1): - Add rule for Acer Aspire One ZG8 to use acer-aspire_5720 keymap - -Leonid Antonenkov (1): - rule-generator: net - ignore Hyper-V virtual interfaces - -Martin Pitt (3): - Revert "Do not build extras with --disable-extras" - Avoid spinning up CD on pressing eject button - keymap: Another ID for Logitech Wave keyboard - -Michael Reed (1): - path_id: rework SAS device handling - -Michael Witten (12): - Docs: README: `to replace' -> `replacing' - Docs: README: `,' -> `;' - Docs: README: Clean up a sentence - Docs: README: Use present tense - Docs: README: Add missing `and' - Docs: README: Remove commas and use subjective mood - Docs: README: Clean up `udev extras' requirements - Docs: README: Clarify configuration of existing devices - Docs: README: `does never apply' -> `never applies' - Docs: README: Flip sentence structure to improve wording - Docs: README: `set up' is the verb; `setup' is a noun - Docs: README: Add a comma to offset the modifier - -Seth Forshee (1): - keymap: Support Dell Latitude XT2 tablet-mode navigation keys - -Thomas Egerer (1): - udevd: add 'N:' to optstring in getopt_long - - -Summary of changes from v166 to v167 -============================================ - -Andrey Borzenkov (1): - udev-acl: add /dev/sgX nodes for CD-ROM - -David Zeuthen (1): - cdrom_id: Don't ignore profiles when there is no media available - -Harald Hoyer (2): - cdrom_id: cd_media_toc() extend toc size to 65536 - udev-acl/70-acl.rules: tag ID_REMOTE_CONTROL with acl - -Kay Sievers (29): - version bump - Merge branch 'master' of git+ssh://master.kernel.org/pub/scm/linux/hotplug/udev - v4l_id: kill the v4l1 ioctl - v4l_id: remove left-over variable - update some comments - test-libudev: add short options - libudev: udev_device_get_sysattr_list_entry() update - libudev: resolve ifindex in udev_device_new_from_id_filename() - libudev: bump minor version - udev-acl: move sg rule to optical drive rule - move /dev/.udev/ to /dev/.run/udev/ and convert old udev database at udevd startup - NEWS: clarify /dev/.run/ requirements - input_id: silent gcc warnings - fstab_import: disable build - systemd: remove deprecated udev-retry.service - fstab_import: remove from configure - update sd-daemon.[ch] - udevd: use facility == LOG_DAEMON when writing to /dev/kmsg - udevd: initialize fds, for proper close() on exit - use /run/udev/ if possible and fall back to /dev/.udev/ - rules: run ata_id only on SPC-3 or later optical drives - systemd: bind udev control socket in systemd and split udev.service - systemd: use sockets.target not socket.target - man: remove trigger --type=failed handling - libudev: export udev_get_run_path() - libudev: docs - add udev_get_run_path() - libudev: make valgrind happy - systemd: do not enable udev-settle.service by default - systemd: udev.socket - disable implicit dependencies - -Kei Tokunaga (1): - udevadm: enumerate - update prev pointer properly - -Lee, Chun-Yi (2): - Remap Acer WMI touchpad toggle key to F21 used by X - Remap MSI Laptop touchpad on/off key to F22 and F23 - -Martin Pitt (12): - 60-persistent-input.rules: Support multiple interfaces - Only build v4l_id if V4L1 header file is available - 60-persistent-input.rules: Do not create duplicate links - Fix building with --disable-extras - Do not build extras with --disable-extras - v4l_id: Drop videodev.h check again - keymap: Fix Acer Aspire 5920G media key - input_id: Consistently use tabs for indentation - input_id: Add some debugging output - input_id: Avoid memory overflow with too long capability masks - input_id: Cover key devices which only have KEY_* > 255 - input_id: Rewrite debug logging to use standard udev info() - -Seth Forshee (1): - keymap: continue reading keymap after invalid scancodes - -Thomas Egerer (3): - libudev: allow to get list of all available sysfs attrs for a device - libudev: use sysfs attr ilist interface for attribute walk - udevadm: info - make attribute array static and const - - -Summary of changes from v165 to v166 -============================================ - -Chris Bagwell (1): - Remap Eee PC touchpad toggle key to F21 used by X - -Gerd Hoffmann (1): - extras: add rules for qemu guests - -Jürgen Kaiser (1): - keymap: Add Acer Aspire 8930 - -Kay Sievers (7): - version bump - man: generate html pages for www.kernel.org - man: fix typo - make: fix qemu rules file name - extras: qemu - fix typo - ata_id: do not print empty serial numbers to avoid unwanted trailing '_' - update gitignore - -Martin Pitt (6): - keymap: Add Acer TravelMate C310 - keymap: Update README.keymap.txt - keymap: Add Lenovo ThinkPad X201 tablet - keymap: Move reading of event in separate function - keymap: More robust state machine - keymap: Explain how to end the program - -Matthew Garrett (1): - keymap: Remove wlan from Dell - - -Summary of changes from v164 to v165 -============================================ - -Andy Whitcroft (1): - keymap: Add release quirks for two Zepto Znote models and AMILO Xi 2428 - -Bastien Nocera (2): - keymap: Add force release for HP touchpad off - extras/keymap: Make touchpad buttons consistent - -David Henningsson (1): - Add ACLs for FFADO supported sound cards - -David Zeuthen (6): - ata_id: Support SG_IO version 4 interface - Run scsi_id and ata_id on the scsi_device object - Use ata_id, not scsi_id, on ATAPI devices - Add GUdevEnumerator type and Device.get_tags() method - Add g_udev_device_get_is_initialized() method - gudev: Add Device.get_usec_since_initialized - -Harald Hoyer (2): - udev-rules.c: change import property buffer to 16384 bytes - 70-acl.rules: add ACLs for ID_PDA devices - -Jakub Wilk (1): - man: udev - workaraound -> workaround - -Jan Drzewiecki (1): - cdrom_id: Fix media state for unreadable DVDs - -Kay Sievers (19): - version bump - rules: 78-sound-card - remove specific hardware matches, they do not belong here - rules: drop OSS audio rule - rules: drop alsa jack-plug input devices - rules: revert bsg use until the event ordering problem is sorted out - libudev: do not overwrite path with readlink() call - udevadm: info - honor --export and --export-prefix for property query - udevadm: info - honor --export, --export-prefix= - udevd: use dev_t or netif ifindex as database key - udevd: always create /dev/{char,block}/$major:$minor - udevd: simplify udev database and fix DEVNAME handling - udevd: switch to common id_filename functions - udevd: write full database file for (unsupported) renamed device nodes - check ifindex > 0 instead of subsystem == "net" - libudev: enumerate - allow to filter-out not-already-initialized devices - libudev: fix renamed device nodes detection logic - libudev: record and export "age" of device record - gudev: bump minor version - update NEWS - -Martin Pitt (5): - keymap: Add Sony Vaio VGN71 - keymap: Add some more Sony Vaio VGN-* models - Add ACL for media player USB devices - keymap: Fix struck Touchpad key on Dell Latitude E series - keymap: Fix struck Touchpad key on Dell Precision M series - -Michal Soltys (1): - udevd: create static nodes before /dev/null is needed - - -Summary of changes from v163 to v164 -============================================ - -David Zeuthen (1): - Install libgudev-1.0.so in prefix / instead of prefix /usr - -Harald Hoyer (1): - cdrom_id: request the drive profile features with a dynamic length - -Kay Sievers (4): - version bump - udevd: do not wrongly delay events for devices with swapped names - return proper error code in rename_netif() - libudev: return kernel provided devnode when asked before we handled any rules - -Martin Pitt (2): - keymap: Apply force-release rules to all Samsung models. - keymap: Add Toshiba Satellite U500 - - -Summary of changes from v162 to v163 -============================================ - -David Zeuthen (2): - gudev: Deliver ::uevent signal in the thread-default main loop - Bump required GLib version to 2.22 - -Hannes Reinecke (1): - scsi_id: export target port group - -Kay Sievers (5): - version bump - scsi_id: fix compiler warnings - systemd: hook into basic.target instead of sysinit.target - systemd: sort before basic.target - udevd: add sd-daemon.c - -Lee, Chun-Yi (1): - keymap: Add alternate MSI vendor name - -Martin Pitt (8): - keymap: Add Lenovo Y550 - Clarify WAIT_FOR documentation - fix various syntax errors in rules - Add automatic rules syntax check - cdrom_id: Try reading the medium if all MMC commands fail - Revert "cdrom_id: Try reading the medium if all MMC commands fail" - cdrom_id: Fall back to CDROM_DRIVE_STATUS if all MMC commands fail - cdrom_id: Don't read beyond "last track" in TOC - -Torsten Schoenfeld (1): - gudev: add a few annotations that newer gobject-introspection versions demand - - -Summary of changes from v161 to v162 -============================================ - -David Woodhouse (1): - Add keymap for Lenovo IdeaPad S10-3 - -Jan Drzewiecki (2): - cdrom_id: Drop MEDIA_SESSION_NEXT for DVD-RW-RO - cdrom_id: Fix DVD blank detection for sloppy firmware - -Kay Sievers (10): - init: update systemd service files - init: update systemd service files - init: add 'udev -' to description in systemd service files - udevd: add pid to kmsg logs - init: edit systemd service descriptions - version bump - udevd: remove unneeded credential passing from init_notify() - set SELinux context on 'add' but not on 'change' events - systemd: enable all udev services unconditionally - Revert "Add alternative KVM MAC address blacklist" - -Luca Tettamanti (1): - Add support for oom_score_adj - -Marco d'Itri (2): - udev-acl: do not mistake all SCSI "processor" devices for scanner - do not create persistent name rules for KVM network interfaces - -Martin Pitt (12): - cdrom_id: Add media status debugging - udev(7): Point out required extension, and remove some confusion - keymap: Add Onkyo PC - keymap: Add HP G60 - keymap: Fix Sony VAIO VGN-SZ2HP/B - udev(7) manpage: Fix description of $attr - gudev: fix crash if netlink is not available - keymap: Fix Acer TravelMate 4720 - cdrom_id: Fix DVD-RW media detection - Fix KVM MAC address range - do not create persistent name rules for VMWare network interfaces - Add alternative KVM MAC address blacklist - -Michael Forney (1): - Don't install systemd scripts with --without-systemdsystemunitdir - -Michal Soltys (1): - ChangeLog fix - - -Summary of changes from v160 to v161 -============================================ - -Fortunato Ventre (1): - keymap: Add force-release quirks for a lot more Samsung models - -Harald Hoyer (3): - udev-event.c: rename interface to -, if taken - rule_generator/write_net_rules: prevent interface to be named "eth" - cdrom_id: READ TOC before READ DISC INFORMATION fixes qemu - -Jan Drzewiecki (5): - cdrom_id: Fix detection of reblanked DVD+RW and DVD-RAM - cdrom_id: Handle pre-MMC2 drives - cdrom_id: Also apply format check to DVD-RW - cdrom_id: No "next session" for "other" media state - cdrom_id: Fix state for fresh DVD-RW - -Jerone Young (1): - Fix volume keys not releasing on Mivvy G310 - -Kay Sievers (12): - version bump - rules: remove firewire rules for deprecated drivers - udev-acl: update firewire matches to recent rule changes - libudev: bump minor so version after adding symbols - call util_delete_path() only when we actually deleted stuff - udev-acl: properly handle CK change events for root user - udev-acl: remove specific device matches from the rules file - fix broken "compile warning fix" - always log error when renaming a network interface fails - do not rename the database on device rename - cdrom_id: whitespace fix - cdrom_id: do not bail out when we can not read the TOC like for empty CDRW - -Marco d'Itri (3): - hid2hci: fix Logitech diNovo, MX5500 and other keyboards - log an error when a message from the wrong version of udevadm is ignored - hid2hci: fix for Logitech diNovo Edge keyboard - -Martin Pitt (1): - keymap: Generalize Samsung keymaps - -Michal Schmidt (1): - udev-acl: really fix ACL assignment in CK events - -Richard Hughes (1): - udev-acl: add DDC_DEVICE to the types that are managed - -Stefan Richter (1): - rules: add more FireWire IDs: Point Grey IIDC; AV/C + vendor unique - -Yin Kangkai (7): - udevadm: fix short options in getopt() - udevd: fix some memory leaks in error path - malloc()+memset() -> calloc() - udevd: fix short options in getopt() - udevd: fix unref'ing of device in error path - udevd: create static device links only when the target exists - udev: fix compile warning - - -Summary of changes from v159 to v160 -============================================ - -Harald Hoyer (2): - 60-persistent-storage-tape: s/path_id.sh/path_id/ - 60-persistent-storage-tape.rules: make own by-path symlink for nst tapes - -Kay Sievers (4): - version bump - rules: tape - remove WAIT_FOR instruction and don't export BSG_DEV - allow final assignment for OPTIONS:="nowatch" - udevd: init_notify() fix abstract namespace name handling - -Lennart Poettering (1): - systemd: make service files readable by GKeyFile - -Martin Pitt (2): - keymap: Find alternate Lenovo module - keymap: Add Lenovo ThinkPad SL Series extra buttons - - -Summary of changes from v158 to v159 -============================================ - -Jerone Young (1): - Fix stuck volume key presses for Toshiba Satellite U300 & U305models - -Kay Sievers (5): - version bump - add systemd service files - make: pre-process and install systemd service files when needed - make: fix 'make distcheck' - switch a few left-over from GPLv2 to GPLv2 or later - -Lennart Poettering (1): - systemd: update service files for newly introduced DefaultDependencies= option - -Martin Pitt (1): - keymap: Add Logitech Cordless Wave Pro - -Matthew Garrett (1): - keymap: Add support for IBM-branded USB devices - -Michael Meeks (1): - gudev: respect possibly given LD_LIBRARY_PATH - -Ryan Harper (2): - Add virtio-blk support to path_id - Add virtio-blk by-id rules based on 'serial' attribute - - -Summary of changes from v157 to v158 -============================================ - -Harald Hoyer (1): - extras/keymap: add Samsung N210 to keymap rules - -Kay Sievers (7): - version bump - libudev: fix fd leak in udev_enumerate_scan_devices() when tags are searched - udevd: in case we don't daemonize, send READY message to /sbin/init - delete last distro specific rules - remove a few comments in file headers - mtd_probe: add needed include, modprobe blacklist flag, and change some whitespace - rules: remove unused subdir - -Martin Pitt (4): - Fix hid2hci rules harder - add Vala vapi for gudev-1.0 - Revert "add Vala vapi for gudev-1.0" - Fix usb printer rule for multiple USB interfaces - -Maxim Levitsky (1): - mtd_probe: add autodetection for xD cards - -Paul Bender (1): - configure.ac: fix cross compilation - - -Summary of changes from v156 to v157 -============================================ - -Harald Hoyer (1): - 40-redhat.rules: removed file - -Jerone Young (3): - Fix wlan key on Inspirion 1210 - Fix wlan key on Inspiron 910 - Fix wlan key on Inspiron 1010 & 1110 - -Kay Sievers (25): - configure.ac: version bump - Makefile.am: silent build mkdir - rules: mount fuse control filesystem - fix compilation with --enable-debug - while (1) -> for (;;) - childs -> children - udevd: replace --debug-trace with --children-max - udevd: fix comments - rules: add -v to modprobe calls to be able see what will be loaded - udevd: read debug settings from kernel commandline - update NEWS - rules: delete pilot rules and remove redhat directory - man: add static device nodes and udevd debug options - man: add kernel command line parameters - man: udevd - update intro - rules: rename packages -> arch - rules: SUSE - move last distro rule to package - rules: add misc/30-kernel-compat.rules - make: mkdir /lib/udev/devices/ - make: fix rules/ subdir names - udevd: set umask before creating files/directories - add IMPORT{cmdline} - IMPORT{cmdline}: start at first char after '=' - libudev: doc - fix typo - update NEWS - - -Summary of changes from v155 to v156 -============================================ - -Bryan Kadzban (1): - udevd: fix typo /proc/fd -> /proc/self/fd - -Kay Sievers (4): - configure.ac: version bump - cdrom_id: do not export ID_CDROM_MEDIA_SESSION_LAST_OFFSET= for single session media - rules: optical drives - use ID_CDROM_MEDIA_TRACK_COUNT_DATA - libudev: fix udev_queue_get_seqnum_sequence_is_finished() with empty queue file - - -Summary of changes from v154 to v155 -============================================ - -Kay Sievers (11): - reset process priority before executing RUN+= - configure.ac: version bump - rules: SUSE - delete device-mapper rules - libudev: add O_CLOEXEC - use default mode of 0600 for nodes if gid == 0 - udevd: create standard symlinks and handle /lib/udev/devices - update NEWS README - fix tests and allow MODE=000 - create static nodes provided by kernel modules to allow module autoloading - update NEWS - man: directly use 'refentry' - - -Summary of changes from v153 to v154 -============================================ - -Harald Hoyer (2): - Makefile.am: add LGPL COPYING file to EXTRA_DIST - cdrom_id: only mark sr[0-9]* as ID_CDROM - -Jerone Young (1): - Fix volume keys not releasing for Pegatron platform - -Kay Sievers (23): - configure.ac: version bump - more readlink buffer size handling - remove left-over from ignore_remove and all_partitions - fix previous commit - udevadm: info --export-db -- remove watch handle export - add TAG= to improve event filtering and device enumeration - all to match against a given TAG== - udev-acl: use a tag instead of a property to mark devices - fix logic on-demand loading logic for db and uevent - use the usual TAG+=, TAG= logic - delete old tags when configuration changes - libudev: accept NULL in udev_device_get_tags_list_entry() - export tag functions - export udev_device_get_tags_list_entry() - udevd: always try to find an idle worker instead of forking a new one - remove unused parameter from udev_node_mknod() - remove debug output during rules parsing - warn when renaming kernel-provided nodes instead of adding symlinks - man: udevadm trigger - the default is "change" not "add" - update README regarding kernel version and default rules - add info message when empty NAME is given - libudev: add documentation for recently added functions - udevd: reload config only for *.rules files - -Martin Pitt (1): - keymap: Fix Bluetooth key on Acer TravelMate 4720 - -Mathias Nyman (1): - remove buffer-overrun risk in readlink call - -Matthias Schwarzott (1): - rules: Gentoo - remove old devfs compat rules - -Michael Thayer (1): - fix device node deletion - -Robby Workman (1): - configure.ac: move firmware-path setting out of extras section - -Yin Kangkai (2): - keymap: Add keymap and force-release quirk for Samsung N128 - keymap: Add keymap quirk of WebCam key for MSI netbooks. - - -Summary of changes from v152 to v153 -============================================ - -Kay Sievers (1): - configure.ac: version bump - -Robby Workman (1): - configure.ac: fix broken firmware search path in configure.ac - - -Summary of changes from v151 to v152 -============================================ - -Adrian Bunk (1): - udev needs automake 1.10 - -Amit Shah (2): - Fix virtio-ports rule to use $attr instead of $ATTR - rules: virtio - fix is to check if the 'name' attribute is present - -Andy Whitcroft (2): - keymap: Add Samsung Q210/P210 force-release quirk - keymap: Add Fujitsu Amilo 1848+u force-release quirk - -Dan Williams (1): - modeswitch: morph into tool that only switches Mobile Action cables - -David Zeuthen (3): - Decrease buffer size when advancing past NUL byte - Use UTIL_LINE_SIZE, not UTIL_PATH_SIZE to truncate properties - Increase UTIL_LINE_SIZE from 2048 to 16384 - -Harald Hoyer (1): - cdrom_id: remove debugging code - -Jerone Young (6): - Force key release for volume keys on Dell Studio 1557 - Fix Keymapping for upcoming Dell Laptops - Add new Dell touchpad keycode - Revert special casing 0xD8 to latitude XT only - Fix Dell Studio 1558 volume keys not releasing - Add support for another Dell touchpad toggle key - -Kamal Mostafa (3): - keymap: Unite laptop models needing common volume-key release quirk - keymap: Add force-release quirk for Coolbox QBook 270-02 - keymap: Add force-release quirk for Mitac 8050QDA - -Kay Sievers (43): - libudev: bump minor version - udevadm: fix untested and broken commit to set buffer size - configure.ac: version bump - udev-acl: no not encourage use of ACL_MANAGE outside of rules file - replace utimes() with utimensat() - libbudev-private: rename udev_list_entry_get_flag() - udevadm: monitor - use / as separator in --subsystem-match=subsystem[/devtype] - use major:minor as entries in symlink stack instead of devpath - use major:minor as entries in watch directory - libudev: docs - .gitignore backup files - firmware: fix possible segfault when firmware device goes away while loading - do not reset SELinux context when the node was not touched - libudev: add udev_device_new_from_environment() - add LGPL COPYING to libudev and GUdev - cdrom_id: open non-mounted optical media with O_EXCL - libudev: update documentation - extras: mobile-action-modeswitch - update gitignore - scsi_id: add rand() in retry loop - cdrom_id: retry to open the device, if EBUSY - cdrom_id: check mount state in retry loop - cdrom_id: always set ID_CDROM regardless if we can run cdrom_id - rules: delete outdated packagees rules - rules: we do not have static devices which are renamed - unify/cleanup event handling - allow IMPORT{db}="KEY" - usb-db: remove double '/' - replace "add|change" with "!remove" - update NEWS - log info only if we actually delete the node - udevadm: trigger - switch default action from "add" to "change" - remove "all_partitions" option - rules: call modprobe on all events but "remove" - remove "ignore_remove" option - update NEWS - cdrom_id: rework feature/profiles buffer parsing - cdrom_id: print more debug messages - cdrom_id: debug - print feature values in hex - cdrom_id: debug - print feature values in hex - cdrom_id: set ID_CDROM_MEDIA=1 only for known media - Revert "Fix switching Logitech bluetooth adapters into hci mode." - add O_NOFOLLOW when creating files in link stack - delete only device nodes, not symlinks when deleting a devtmpfs node - doc: add section about how *not* to rename device nodes - -Marco d'Itri (3): - rules: input - create by-path/ links for pci devices - Fix switching Logitech bluetooth adapters into hci mode. - doc: document the WAIT_FOR timeout - -Martin Pitt (12): - keymap: Add Dell Inspiron 1011 (Mini 10) - Fix brightness keys on MSI Wind U-100 - keymap: Fix LG X110 - keymap: Add Toshiba Satellite M30X - udev-acl: Correctly handle ENV{ACL_MANAGE}==0 - input_id: Fix linking - keymap: Add Acer TravelMate 6593G and Acer Aspire 1640 - keymap: Fix another key for Acer TravelMate 6593 - cdrom_id: Fix uninitialized variables - cdrom_id: Fix uninitialized buffers - cdrom_id: Do not ignore errors from scsi_cmd_run() - cdrom_id: Swap media state and TOC info probing - -Mike Brudevold (1): - cdrom_id: add missing profiles to feature_profiles - -Robert Hooker (1): - keymap: Add support for Gateway AOA110/AOA150 clones. - -Scott James Remnant (2): - libudev: export udev_monitor_set_receive_buffer_size() - udevadm monitor: increase netlink buffer size - -Thomas Bächler (1): - firmware: fix error reporting on missing firmware files - -Yury G. Kudryashov (3): - configure.ac - fix typo in --with-pci-ids-path option - hid2hci: include linux/types.h for __u32 - configure.ac: ddd --with-firmware-path option - - -Summary of changes from v150 to v151 -============================================ - -Amit Shah (1): - rules: Add symlink rule for virtio ports - -Bryan Kadzban (1): - Fix reverted floppy-device permissions - -Egbert Eich (1): - rulews: suse - add do-not-load-KMS-modules rules - -Frederic Crozat (1): - rules: acl - add COLOR_MEASUREMENT_DEVICE match - -Kay Sievers (11): - configure.ac: version bump - udevd: inotify - do not parse rules at create but at close - do not remove device nodes of active kernel devices - libudev: device - create db file atomically - clarify message about not removed device node - input_id: include limits.h - keymap: include linux/limits.h - keymap: linux/input.h - get absolute include path from gcc - delete outdated and unmaintained writing_udev_rules - update README and NEWS - update tests - -Marco d'Itri (2): - writing_udev_rules: update rules files names - keymap: support for the Samsung N140 keyboard - -Martin Pitt (4): - add ACL rule for Garmin GPSMap 60 - keymap: move force-release directory - extras/keymap/check-keymaps.sh: Ignore comment-only lines - keymap: Fix invalid map line - - -Summary of changes from v149 to v150 -============================================ - -Clemens Buchacher (2): - add Samsung R70/R71 keymap - keymap: Samsung R70/R71 force-release quirk - -Daniel Drake (2): - keymap: Add OLPC XO key mappings - keymap: Fix typo in compal rules - -Daniel Elstner (1): - libudev: wrap in extern "C" block for C++ - -David Zeuthen (1): - Export ID_WWN_VENDOR_EXTENSION and ID_WWN_WITH_EXTENSION - -Jerone Young (1): - keymap: Lenovo Thinkpad USB Keyboard with Tracepoint - -Johannes Stezenbach (2): - keymap: add Samsung N130 - keymap: handle atkbd force_release quirk - -Kay Sievers (15): - util_unlink_secure(): chmod() before chown() - floppy: fix rule to create additional floppy device nodes - configure.ac: version bump - remove remaining support for CONFIG_SYSFS_DEPRECATED - cdrom_id: remove deprecated device matches - rules: add "block" match to floppy rule - update mtime of nodes and links when we re-use them - udevadm: info - fix info --root --query=name --path= for device without a device node - remove remaining support for CONFIG_SYSFS_DEPRECATED - fix typo in log message priority handling - remove UDEV_RUN environment variable - udevadm: logging - copy va_list and do not use it twice - libudev: doc - add symbols to sections.txt - work around gtk-doc which breaks distcheck - gobject-introspection: use $datadir instead of $prefix - -Marco d'Itri (2): - build: keymap - create subdir - rules: udev-acl - add firewire video devices - -Martin Pitt (12): - keymap: Add Acer Aspire 1810T - 95-keymap.rules: Run on change events, too - keymap: fix findkeyboards - Speed up udev_enumerate_scan_* - keymap: Add hotkey quirk for Acer Aspire One (AO531h/AO751h) - Clarify RUN/IMPORT documentation - keymap: Add Logitech S510 USB keyboard - keymap: add Acer TravelMate 8471 - keymap: Add Acer Aspire 1810TZ - keymap: Add LG X110 - keymap: Add Fujitsu Amilo Li 1718 - keymap: Document force-release - -Piter PUNK (1): - firmware: convert shell script to C - -Scott James Remnant (1): - 70-acl.rules: ACL manage Android G1 dev phones - -Thomas de Grenier de Latour (1): - libudev: enumerate - fix move_later logic - - -Summary of changes from v148 to v149 -============================================ - -Daniel Elstner (1): - really fix both in-tree and out-of-tree builds - -Dmitry Torokhov (1): - input-id: identify touchscreens - -Kay Sievers (4): - libudev: doc - use #NULL - configure.ac: version bump - really really fix both in-tree and out-of-tree builds - fix both in-tree and out-of-tree builds - -Martin Pitt (6): - input_id: Fix endless loop for non-input devices - input_id: Do not tag non-input devices with ID_INPUT - input_id: small optimization - input_id: check event mask - input_id: Check mouse button for ID_INPUT_MOUSE - udev_device_get_parent_with_subsystem_devtype(): Clarify documentation - - -Summary of changes from v147 to v148 -============================================ - -Dan Williams (3): - Revert "modem-modeswitch: add a device" - Revert "extras/modem-modeswitch: Add Huawei E1550 GSM modem" - modem-modeswitch: 61-option-modem-modeswitch.rules is only for Option NV devices - -Daniel Mierswa (1): - Fix typo in NEWS, ConsoleKit-0.4.11 -> 0.4.1 - -David Zeuthen (4): - cdrom_id: Still check profiles even if there is no media - scsi_id: Export WWN and Unit Serial Number - Create /dev/disk/by-id/wwn-0x... symlinks - Also create /dev/disk/by-id/wwn-0x..-part%n symlinks for partitions - -Dmitry Torokhov (1): - extras/input_id: Correctly identify touchpads - -Harald Hoyer (1): - modem-modeswitch: add a device - -Kay Sievers (8): - rules: set mode of floppy device nodes to 0660 - remove "ignore_device" - print warning for BUS=, SYSFS{}=, ID= - test-udev: remove "ignore_device" code - udev-test.pl: catch-up with recent changes - rules: remove support for IDE (hd*) devices - ata_id: skip ATA commands if we find an optical drive - Revert "Fix out-of-tree builds" - -Martin Pitt (5): - README.keymap.txt: small clarification - extras: Add input_id - 70-acl.rules: Use new-style input properties - input: Deprecate ENV{ID_CLASS} - input_id: code cleanup - -Scott James Remnant (1): - Fix out-of-tree builds - - -Summary of changes from v146 to v147 -============================================ - -Alan Jenkins (1): - udevd: queue-export - remove retry loop - -Andrew Church (1): - fix wrong parameter size on ioctl FIONREAD - -Daniel Mierswa (2): - don't compare a non-existing function with NULL - use nanosleep() instead of usleep() - -David Zeuthen (4): - gudev: remove G_UDEV_API_IS_SUBJECT_TO_CHANGE since API is now stable - ata_id: export more advanced ATA features - gudev: Fix up GUdevDeviceNumber - gudev: Remove LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE from priv header - -Florian Zumbiehl (10): - util_delete_path(): use util_strscpy() - util_lookup_group(): fix memory leak if realloc() fails - util_delete_path(): handle multiple leading slashes - util_create_path(): fix possible out of bounds array access - ude_rules.c: fix possible NULL pointer dereference in get_key() - util_resolve_sys_link(): fix possible buffer overflow - udev_util_encode_string(): fix possible buffer overflow - udev-rules.c: parse_file() - fix possible buffer overflow - udev_queue_get_seqnum_sequence_is_finished(): fix possible file handle leak - util_run_program(): fix possible buffer overflow #2 - -Harald Hoyer (2): - scsi_id: prevent buffer overflow in check_fill_0x83_prespc3() - rename interfaces to _rename if rename fails - -Jeremy Kerr (1): - util_run_program: restore signal mask before executing event RUN commands - -Kay Sievers (45): - make: sort Makefile.am per target/extra - configure.ac: version bump - udev-acl: allow to skip ACL handling - rules: rfkill has no group, so use 0644 - rule_generator: net - fix MATCHDEVID - make: add comment - update NEWS - print warning for NAME="%k" - it breaks the kernel supplied DEVNAME - warn about non-readable or empty rules file - change database file names - assign errno for getgrnam_r()/getpwnam_r() - doc: udevadm test *does* create nodes and links these days - util_unlink_secure(): chmod() before chown() - util_create_path(): fix errno usage - inotify_add_watch(): do not store watch, if it failed - update TODO - update README - rules: suse - use NAME for mapper/control - libudev-util.c: get_sys_link() - return error for empty link target - udev-rules.c: remove 'first_token' variable - Revert "udev-rules.c: remove 'first_token' variable" - test: catch possible bug in GOTO resolving - udevadm: remove symlink support for old commands - util_run_program(): skip multiple spaces in argv creation - fix whitespace - require 2.6.27 for proper signalfd handling - fix randonm findings from llvm-clang-analyzer - simplify "symlink name stack" - reorder create_path() and node/link creation to be called in a direct sequence - put util_create_path() and file creastion in a retry loop - udevadm: control - remove compat code - scsi_id: delete copy of bsg.h - fix SYMLINK{} option parsing - rules: remove remaining NAME="%k" - rules: drop almost all NAME= keys - update TODO, NEWS - udevd: serialize events for with the same major/minor - break loops if util_create_path() returns error - remove "last_rule" option - use CLOEXEC flags instead of fcntl() - unblock signals we might want to handle - udevd: create /dev/.udev/rules.d/ before watching it wit inotify - gudev: fix pkg-config call to work with "make distcheck" - update NEWS - Revert "gudev: fix out-of-tree build" - -Lennart Poettering (5): - pci-db: make sure we actually read the pci.ids file instead of usb.ids - sound: recognize saa7134 TV card sound devices as TV cards - sound: include ALSA sound card id in ID_ID property - sound: include ALSA sound card id in /dev/snd/by-id/ links - Revert "sound: include ALSA sound card id in /dev/snd/by-id/ links" - -Marco d'Itri (6): - doc: writing_udev_rules updated for the new command names - rules: sound - do not use /usr/bin/env - udevadm: print all messages to stderr with priority higher or equal than LOG_ERR - udevadmi: control = exit with rc=2 if there is some system error - gudev: gir-scanner workaround for out of tree builds - gudev: fix out-of-tree build - -Mario Limonciello (1): - hid2hci: remove superfluous bmAttributes match - -Martin Pitt (24): - extras/keymap: Add Acer Aspire 6920 - extras/modem-modeswitch: eject ZTE MF6xx fake CD-ROMs - extras/keymap: Fix hold key on Acer Aspire 6920 - extras/keymap: Fix case matching for Micro-Star - Revert "extras/keymap: Fix case matching for Micro-Star" - make raw USB printer devices accessible for lp - modem-modeswitch rules: Match more devices - extras/keymap: fix hash table collisions - extras/keymap: Rename KEY_COFFEE to KEY_SCREENLOCK - fix single-session CD detection - fix previous commit for CD detection - make raw USB printer devices world-readable again - 50-udev-default.rules: fix printer MODE - keymap: Add Logitech Wave USB - keymap: add missing map file - keymap: fix usb_id invocation - keymap: make USB keyboards really work - keymap: Add Logitech Wave cordless - keymap: add HP Pavillion dv6315ea - keymap: add HP 2230s - Makefile.am: fix build with mawk - extras/keymap/README.keymap.txt: Fix bug report link - fix major fd leak in link handling - modem-modeswitch: fix ZTE MF6xx rule - -Matthias Schwarzott (2): - rules: Gentoo update - rules: Gentoo update - -Maxim Levitsky (1): - keymap for Acer Aspire 5720 - -Peter Rajnoha (1): - libudev: allow to store negative values in the udev database - -Scott James Remnant (1): - util_run_program: *really* restore signal mask before executing event RUN commands - -William Jon McCann (1): - udev-acl: catch up with ConsoleKit 0.4.1 - - -Summary of changes from v145 to v146 -============================================ - -Alan Jenkins (3): - man: fix unused, inaccurate metadata - man: SYMLINK can be matched as well as assigned - fix spelling - -Anssi Hannula (2): - rules: exclude digitizers from joystick class - udev-acl: add joystick devices - -Diego Elio 'Flameeyes' Pettenò (21): - Merge libudev, udev, and the unconditional extras in a single Makefile.am. - Replace the custom test-run target with the standard make check. - Also merge into the top-level Makefile.am the simpler extras. - Change hook handling to be more portable. - Merge keymap building in the top-level Makefile.am. - Make keymap generation rules be silent (backward-compatible). - Move pkg-config docs and man pages before conditionals. - Finally, also merge gudev into the top-level Makefile.am. - Make sure to clean up all the built sources. - Make sure to use dependency/target variables. - Add silent-rule support for the gudev rules. - Fix building of introspection library on top-level Makefile.am. - Fix another relative path for the new working directory. - Include the correct directory for out-of-source builds. - Add tests to the distribution; this fixes "make distcheck". - Ask gperf to use ANSI-C for generation. - Merge in Makefile.am.inc into Makefile.am - Use the keymap check during “make distcheck” rather than “check”. - Fix building of documentation when doing out-of-source builds. - Fix “make distcheck” run outside of the source directory. - Use LT_INIT to explicit that udev needs libtool series 2. - -Eric W. Biederman (1): - fix util_lookup_group to handle large groups - -Erik Forsberg (1): - extras/modem-modeswitch: Add Huawei E1550 GSM modem - -Kay Sievers (18): - udevd: add timestamp to --debug output - v4l_id: exit with 0 when --help is given - configure.ac: version bump - hid2hci: remove hid structures and include kernel header - path_id: make global variable static - udevadm: trigger - add --sysname-match= - rules: serial - fix path_id call - path_id: fix typo in comment - format names are not case insensitive - hid2hci: rewrite (and break) rules and device handling - make: build internal tools against libudev-private.la - update a few years of copyright - libudev: silent gcc warning: may be used uninitialized in this function - make: suppress enter/leaving directory messages - re-enable failed event tracking - "record_failed" -> "fail_event_on_error" - udevd: block for 15 seconds after error when too old kernel is detected - make: fix issues from non-recursive conversion - -Lennart Poettering (1): - enumeration: move ALSA control devices to the end of the enumerated devices of each card - -Mario Limonciello (2): - hid2hci: support to hid2hci for recovering Dell BT devices after S3 - hid2hci: install re-trigger for hid device when recovering from S3 - -Martin Pitt (17): - add keymap for Clevo D410J laptop - extras/keymap: add Zepto ZNote - extras/keymap: add Everex Stepnote XT5000T - extras/keymap: add Compal Hel80i - keymap tool: improve help - keymap tool: support scancode/keycode pair arguments - keymap: inline one-line key maps - extras/keymap: fix check-keymaps.sh for inline mappings - extras/keymap: add recently added keymap files to Makefile.am - extras/keymap: Add HP Presario 2100 - extras/keymap: cover more Compaq Evo models - extras/keymap: Add Fujitsu Amilo M - extras/keymap: teach findkeyboards about USB keyboards - extras/keymap: Add Samsung SX22S - extras/keymap: Fix crash for unknown keys - extras/keymap: Add Samsung NC20 - extras/keymap: Fix Bluetooth key on Acer Aspire 6920 - - -Summary of changes from v144 to v145 -============================================ - -Ian Campbell (1): - scsi_id: correct error handling in prepend_vendor_model - -Kay Sievers (10): - README: add CONFIG_BLK_DEV_BSG - use MIN() MAX() from param.h - configure.ac: version bump - libudev: device - free values before updating them - libudev: enumerate - sort with qsort() - udevd: detach event from worker if we kill a worker - udevadm: info - add space after R:, A:, W: on database export - udevd: make sure a worker finishes event handling before exiting - udevd: handle SIGCHLD before the worker event message - udevd: use bool - - -Summary of changes from v143 to v144 -============================================ - -Jon Masters (1): - firmware: search for third party or sysadmin supplied firmware updates - -Kay Sievers (19): - configure.ac: add AM_SILENT_RULES - configure.ac: version bump - TODO: add cleanup of ATA_COMPAT - libudev: queue - add comments for queue format - udev/.gitignore: add udev.pc - configure.ac: version bump - do not exports properties starting with a '.' - scsi_id: --reformat_serial - use udev_util_replace_whitespace() - ata_id: sync ID_SERIAL(_SHORT) with other *_id tools - rules: make ata_id properties the default for all ATA block devices - scsi_id: delete no longer needed config file - update NEWS - man: udev - add private properties like ENV{.FOO}="bar" - Merge branch 'firmware' of git://git.kernel.org/pub/scm/linux/kernel/git/jcm/udev-jcm - udevadm: test - print list of properties - build: do not delete .la files - libudev: monitor - handle kernel supplied DEVNAME properly - update NEWS - build: add *exec* to the internal rootlibdir name - -Martin Pitt (2): - hid2hci: narrow matches to real HCI devices - extras/udev-acl: add smartcard readers - -Stefan Richter (1): - rules: set group ownership of new firewire driver device files - - -Summary of changes from v142 to v143 -============================================ - -Alan Jenkins (5): - udevadm: settle - fix timeout - udevd: remove tiny bit of dead code - udevd: implement a more efficient queue file format - udev-selinux.c: remove libudev header - udevd: queue-export - fix crash - -Benjamin Gilbert (1): - test: check string substitutions in OWNER and GROUP - -Dan Williams (2): - rules: tty/net - move from udev-extras - extras/modem-modeswitch: move from udev-extras - -David Zeuthen (1): - gudev: move from udev-extras - -Kay Sievers (95): - version bump - rules: v4l do not mix vbi and video nodes - fix possible endless loop for GOTO to non-existent LABEL - Revert "rules: v4l do not mix vbi and video nodes" - rule-generator: cd - skip by-path links if we create by-id links - remove format char string truncation syntax - use more efficient string copying - edd_id: use openat() - use openat(), unlinkat(), fstatat() - update TODO - remove unused GL_FORMAT from rules parser - require key names in uppercase - keep the ifdef'd udevd testing/profiling hack - fix location of database files - udevadm: settle - make --timeout=0 working - update NEWS - rules: add SUBSYSTEM match to scsi rules - cdrom_id: suppress ID_CDROM_MEDIA_STATE=blank for plain non-writable CDROM media - udevadm: control - add comment to man page about --reload-rules - cdrom_id: add error message if open() fails - udevadm: settle - add --exit-if-exists= - udevd: remove check for dev_t, DEVPATH_OLD takes care of that - str[sp]cpyl: add __attribute__ ((sentinel)) - udevd: convert to event worker processes - udevd: close netlink socket in worker and set cloexec - rules: do not call path_id for virtual devices - udevd: use enum instead of char in struct declaration - allow format substitution in path of ATTR{}=="" - cleanup $attr{} substitution - path_id: implement in C using libudev - path_id: update SCSI handling - path_id: add comments - fix signed/unsigned warning - libudev: enumerate - allow multiple keys with the same name - udevadm: trigger - add --property-match=: - udevadm: info - accept --query without a value and print properties - udevadm: control - --env -> --property - udevadm: monitor --environment -> --property - path_id: handle fibre channel - path_id: add iscsi support - path_id: delete old shell script - udevd: print error if worker dies unexpectedly - path_id: rename scsi sub-fuctions - libudev: add comments to libudev.h - libudev: move to top-level directory - fix libudev include in Makefile.am.in - libudev: device_new() -> udev_device_new() - udevd: log info for created/killed workers - libudev: call log functions conditionally - move syslog wrapper to libudev - move common stuff from udev/ to private parts of libudev/ - libudev: rename private files to *-private.c - rules: remove scsi ch module loading rule - update NEWS - udevadm: info -revert "accept --query without argument" - README: add kernel options - README: add INOTIFY and SIGNALFD - USE_LOG -> ENABLE_LOGGING, DEBUG -> ENABLE_DEBUG, USE_SELINUX -> WITH_SELINUX - libudev: add gtk-doc - libudev: update documentation - libudev: doc - add section headers - libudev: doc - add enumerate - libudev: doc - add queue - update TODO - libudev: doc - add namespace for index - libudev: move .so version to libudev Makefile - autogen.sh: simplify - TODO: update - libudev: remove prefix from .so version variables - libudev: doc - add empty libudev.types - udev-acl: move from udev-extras - INSTALL: add --enable-extras - udev-acl: handle missing action when called in CK mode - v4l_id: move from udev-extras - libudev: doc - libudev-docs.sgml -> libudev-doc.xml - gudev: fix typo in configure option - v4l_id: 70-v4l.rules -> 60-persistent-v4l.rules - configure: enable all extras by default, provide --disable-extras - autogen.sh: make "CFLAGS=-O0 ./autogen.sh" working - NEWS: add --disable-extras - cleanup ./configure installation directory options - rules: remove MMC rule, 2.6.30 has the modalias - configure.ac: print error if gperf is missing - libudev: install in $libdir and move later to $rootlibdir - extras/keymap: use LIBEXECDIR instead /lib/udev - README: add /lib/udev/ is private - rules: do not install usb-id/pci-id rules when --disable-extras is used - extras: delete man pages for private udev tools - README: update - extras/keymap: install findkeyboards in /lib/udev - INSTALL: use /sbin instead of %{sbindir} - NEWS: update - udev.pc: add - Merge branch 'master' of git+ssh://master.kernel.org/pub/scm/linux/hotplug/udev - docs: install writing_udev_rules - -Lennart Poettering (2): - rules: sound - move from udev-extra - usb-db: move from udev-extras - -Marcel Holtmann (1): - rules: make RFKILL control device world readable - -Mario Limonciello (1): - hid2hci: move from udev-extras - -Martin Pitt (5): - keymap: move from udev-extras - extras/keymap: Fix WLAN button on ThinkPads - keymap: Update findkeyboard path in docs - udev-acl: Manage hplip device permissions - extras/keymap: Update findkeyboards location - -Matthias Schwarzott (3): - rules: Gentoo update - rules: Gentoo update - rules: Gentoo update - -Scott James Remnant (1): - OWNER/GROUP: fix if logic - - -Summary of changes from v141 to v142 -============================================ - -Andre Przywara (1): - rules: create /dev/cpu//cpuid world readable - -Ian Campbell (1): - path_id: support identification of Xen virtual block devices - -John Wright (1): - edd_id: add cciss devices - -Kay Sievers (46): - version bump - libudev: path_encode - always return 0 if encoded string does not fit into size - libudev: monitor - clarify socket handling documentation - udevd: log error for too old kernels or CONFIG_SYSFS_DEPRECATED - rules: remove DVB shell script - update NEWS - cdrom_id: add Xen cdrom support - test-libudev: update monitor source - TODO: add packet filter - update NEWS - cdrom_id: add and use ID_CDROM_MEDIA to decide if we run vol_id - libudev: monitor - add client socket filter for subsystem value - udevadm: monitor - print error if we can not bind to socket - update TODO - udevadm monitor - add --subsystem-match= - libudev: monitor - use simpler hash - libudev: monitor - switch to filter_add_match_subsystem_devtype() - libudev: monitor - do not filter messages with wrong magic - udevadm: monitor - add : support - libudev: monitor - add udev_monitor_filter_remove - libudev: queue - fix get_seqnum_is_finished() - cdrom_id: skip media tests if CDROM_DRIVE_STATUS != CDS_DISC_OK - libudev: queue - clarify comments - libudev: monitor - export filter_update() - update NEWS - drop "extern" keyword from non-static function - rule_generator: net - fix usb comment generation - rules: input - add links for USB/platform non-kbd/mouse devices - rules: input - fix comments - rules: add rfcomm* to group dialout - accept DEVNAME from the kernel as a hint for the node name - update TODO - build: use AC_MSG_RESULT - rules: add "event*" match - udevd: revert initial device node creation - rules: remove initramfs comment - handle devtmpfs nodes - oops, removed ppp entry from rules got committed - remove all PHYSDEVPATH handling and warning about - remove asmlinkage - rules: fix ieee1394 rules - add "static" back to the inline functions - update TODO - delete vol_id and require util-linux-ng's blkid - delete libvolume_id - -Lubomir Rintel (1): - rule-generator: net - whitelist NICs that violate MAC local scheme - - -Summary of changes from v140 to v141 -============================================ - -Adam Buchbinder (4): - usb_id: add manpage - cdrom_id: update manpage - create_floppy_devices: expand manpage - vol_id: fix language in manpage - -Alan Jenkins (1): - avoid leaking netlink socket fd to external programs - -Borislav Petkov (1): - rules: rename ide-floppy to ide-gd - -David Brownell (1): - rules: exclude mtd* from persistent disk links - -Kay Sievers (15): - rules: fix extra quote in 50-udev-default.rules - version bump - udevadm: test - handling trailing '/' in devpath - udevadm: monitor - clarify printed header - rules: remove ram* from persisten disk links blacklist - rules: serial - support ttyACM devices - rules: replace IDE driver with media match - usb_id: add ID_VENDOR_ID, ID_MODEL_ID, ID_USB_INTERFACE_NUM, ID_USB_DRIVER - libudev: GPL -> LGPL - usb_id: remove unused variable - send monitor events back to netlink socket - "UDEV_MONITOR_KERNEL/UDEV" -> "kernel/udev" - IMPORT: 2048 -> 4096 bytes buffer - path_encode: fix max length calculation - libudev: monitor - unify socket message handling - -Michal Soltys (1): - rules: md-raid.rules fix - -Robby Workman (1): - udevadm: trigger - add "--action" to --help - -Scott James Remnant (1): - libudev: monitor - ignore messages from unusual sources - - -Summary of changes from v139 to v140 -============================================ - -Harald Hoyer (1): - libvolume_id: bump age - -Kay Sievers (12): - version bump - update TODO - volume_id: ntfs - fix uuid setting - update TODO - rules: Fedora update - libudev: queue - use lstat() to check existence of symlink - udevadm: settle - add --seq-start= --seq-end= - udevd: switch watch symlinks to devpath - udevadm: add text for new options to command and man page - update TODO - libudev: ctrl - return error after sending ctrl message - udevadm: settle - use timeout signal, instead of loop counter - -Michael Prokop (1): - fix compile error in debug mode - -Scott James Remnant (1): - udevadm: settle - synchronise with the udev daemon - - -Summary of changes from v138 to v139 -============================================ - -Kay Sievers (11): - version bump - remove static local variable - use the event udev_device to disable the watch on "remove" - add "nowatch" to disable a default installed watch with a later rule - add m4/ subdir - use AC_USE_SYSTEM_EXTENSIONS instead of AC_GNU_SOURCE - usb_id: add ID_USB_INTERFACES=:0e0100:0e0200:010100:010200: - usb_id: return values if called directly for an usb_device - usb_id: fix NULL string usage - usb_id: fix comment - udevadm: info - export all devices with --export-db - -Scott James Remnant (10): - Don't add inotify watch until RUN rules processed. - Clear existing inotify watch before processing. - Cleanup a little. - Allow watch handle to be stored in the udevdb. - Store watch handle in db. - Use the udevdb to speed up watch clearing. - Put a log message in a more sensible place. - Output watch handle in udevadm info. - lookup the old watch handle; reload only if has a path - Look at more inotify events in the buffer than just the first. - - -Summary of changes from v137 to v138 -============================================ - -David Zeuthen (1): - *_id: add model/vendor enc strings - -Karel Zak (2): - vol_id: fix ddf version string - vol_id: add missing id->type to swap0 - -Kay Sievers (13): - man: fix grammar - version bump - fix NAME="" logic - rules: dm - add escape for uuid links with whitespace - test: add test for empty and non-existent ATTR - rules: fix md "change"/"remove" handling - autogen.sh: add more warnings - fix NAME= and OPTION+="string_escape=..." logic - rules: move OPTIONS to separate rule - use global "reload_config" flag - rules: add "watch" option to dm and md rules - rules: include loop block devices in persistent links - release 138 - -Matthias Schwarzott (1): - rules: Gentoo update - -Miklos Vajna (1): - doc: writing udev rules - refer to 'udevadm info' instead of 'udevinfo' - -Scott James Remnant (2): - udevd: optionally watch device nodes with inotify - rules: update persistent storage rules to use inotify watches - - -Summary of changes from v136 to v137 -============================================ - -Alan Jenkins (2): - man: typo fixes - remove stray initializer - -Kay Sievers (17): - version bump - rules: fix typo in ide cd rule - libudev: use 4096 bytes buffer for attribute reading - rules: add drm devices to group "video" - do not complain about a missing /etc/udev/rules.d/ - udevadm: test - remove --force option - update NEWS - remove name from index if the node name has changed - cleanup old names before creating the new names - open-code pollfd setup - increase netif renaming timeout from 30 to 90 seconds - Merge commit '5f03ed8a56d308af72db8a48ab66ed68667af2c6' - Merge commit '9032f119f07ad3b5116b3d4858816d851d4127de' - split up long line - udevd: add back SA_RESTART - usb_id: handle ATAPI devices like SCSI devices - udevadm: settle - fix typo - -Lennart Poettering (1): - fix naming for tape nst devices in /dev/tape/by-path/ - -Olaf Kirch (2): - udevd: use ppoll instead of signal pipes - reap children faster - -Scott James Remnant (2): - Allow user and group lookup to be disabled. - Expose delayed name resolution - -Sven Jost (1): - volume_id: support via raid version 2 - - -Summary of changes from v135 to v136 -============================================ - -Adam Buchbinder (1): - extras: fix mis-spelling of "environment" - -Harald Hoyer (1): - rule_generator: fix enumeration for write_cd_rules - -Jeremy Higdon (1): - path_id: rework SAS persistent names - -Karel Zak (1): - volume_id: HPFS code clean up - -Kay Sievers (54): - rules: ATA_COMPAT do not try to match on sr*, it will never have vendor ATA - scsi_id: do not fail if no serial is found like for optical drives - update configure and NEWS - rules: fix isdn rules - rules: add persistent /dev/serial/{by-id,by-path} rules - make: install serial rules file - make: do not delete autotools generated file with distclean - udevadm: settle - allow --timeout=0 and --quiet - rules: move aoe rules to default rules file - volume_id: btrfs - update format - rules: add "do not edit header" - volume_id: support sub-uuid's and plug in btrfs device uuid - libudev: include - build: add -lsepol - build: just use autoreconf -i - rules: remove ide-scsi - rules: first simple step merging with Ubuntu rules - "'/sbin/modprobe abnormal' exit" - also print program options - rules: more changes toward Ubuntu rules merge - rules: more changes toward Ubuntu rules merge - rules: remove /dev/raw/raxctl symlink, it's a devfs leftover - rules: rtc - create rtc compat link only for cmos type rtc - rules: remove legacy symlinks - rules: do not put raw1394 in "video" group - rules: second round merging with Ubuntu rules - rules: remove /dev/dsp /dev/audio - rules: put alsa in group "audio" - rules: isdn - remove /dev/isdn/capi20 symlink - rules: provide /dev/raw/rawctl - if needed, store database entries also for devices which do not have a device node - build: use autoreconf --symlink - usb_id: add "image" class - require non-SYSFS_DEPRECATED 2.6.20+ kernel - build: default to --prefix=/usr --exec-prefix="" - libudev: enumerate - add lookup by property - rules: input - make sure needed variables are set - libudev: device - read "uevent" only if info is not already loaded - libudev: subsytem -> subsystem - libudev: bump revision - usb_id: use devtype lookup - require 2.6.22+ kernel - rules: Ubuntu merge - use group "cdrom" - rules: Ubuntu merge - use group "tape" - rules: replace DVB shell script rule - rules: Ubuntu merge - s/uucp/dialout/ - update NEWS - update NEWS - enable skipping of "naming-only" rules - usb_id: s/image/media/ - udevadm: s/udevinfo/udevadm info/ - rules: reorder block rules - rules: zaptel - add "dialout" group - libudev: device - add udev_device_get_property_value() - libudev: test - add udev_device_get_property_value() - -Marcel Holtmann (3): - libudev: device - add devtype support - libudev: device - lookup subsystem and devtype together - libudev: device - remove udev_device_get_parent_with_subsystem - -Michal Soltys (1): - man: udev - update NAME assignment - -Ryan Thomas (1): - rules: add rules for AoE devices - - -Summary of changes from v134 to v135 -============================================ - -Kay Sievers (6): - usb_id: add "break" to currently unused case labels - rules: fix cciss disk/by-id/ links - rules: add infiniband rules - rules: infiniband.rules -> 40-infiniband.rules - fix network interface name swapping - update configure and NEWS - -Marcel Holtmann (1): - usb_id: fix switch statement for video type - -Piter PUNK (2): - rules: /dev/null -> X0R - rules: add usb device nodes - - -Summary of changes from v133 to v134 -============================================ - -Gabor Z. Papp (1): - include errno.h in sysdeps.h - -Harald Hoyer (1): - rules: add persistent rules for memory stick block devices - -Kay Sievers (19): - autogen.sh: fix -print-multi-os-directory usage - volume_id: update btrfs magic - bump version - rules: merge group "video" into default rules - rules: v4l - add by-id/ links for USB devices - libudev: accept NULL whitelist in util_replace_chars() - usb_id: replace chars in returned strings - ata_id: make sure, we do not have slashes in values - scsi_id: make sure, we do not have slashes in values - volume_id: remove unused usage types - vol_id: if regular files are probed, use stat() for the size value - volume_id: update btrfs - volume_id: clear probing result before probing and do not probe a second time, if not needed - path_id: fix fibre channel handling - update NEWS TODO - floppy: use ARRAY_SIZE() - fix handling of swapping node name with symlink name - silence PHYSDEV* warning for WAIT_FOR* rules - rules: exclude "btibm" devices from vol_id calls - -Matthias Schwarzott (1): - rules: Gentoo update - -Peter Breitenlohner (2): - man: fix typos - floppy: fix array bounds check and minor calculation - - -Summary of changes from v132 to v133 -============================================ - -Alan Jenkins (2): - udevd: de-duplicate strings in rules - scsi_id: we don't use DEVPATH env var anymore, update man page - -Karel Zak (1): - volume_id: fat - move check for msdos signature (0x55 0xaa) - -Kay Sievers (22): - silence "comparison between signed and unsigned" - string index - split nodes and childs to allow and unlimited number of childs - reserve child slot 0 - merge trie nodes, childs and root into a single array - set errno = ENOSYS in inotify stub - udevadm: info - unify -V and --version - rules: remove DEVTYPE disk/partition - rules: remove pnp shell script, acpi loads these modules properly - update NEWS - configure: add linux-hotplug mail address - remove len == 0 check, the index root is always '\0' - volume_id: bump revision - volume_id: always check for all filesystem types and skip conflicting results - volume_id: fat - accept empty FAT32 fsinfo signature - fix spelling in comment - volume_id: ntfs - mark as no other fs must match - vol_id: clarify error message - libudev: device - handle disk "device" link for partitions in deprecated sysfs layout - limit $attr() magic to well-known links only - udevd: fix cleanup of /dev/.udev/uevent_seqnum - fix $links substitution for devices without any link - update NEWS - -Sergey Vlasov (1): - udevadm: fix option parsing breakage with klibc - - -Summary of changes from v131 to v132 -============================================ - -Kay Sievers (2): - fix size_t compiler warning on 32 bit platforms - convert debug string arrays to functions - - -Summary of changes from v130 to v131 -============================================ - -Alan Jenkins (17): - libudev: fix sysnum logic for digit-only device names - udevd: avoid overhead of calling rmdir on non-empty directories - use more appropriate alternatives to malloc() - libudev: util - optimize path_encode() - libudev: allocate udev_device->envp[] dynamically - replace strncpy() with strlcpy() - use re-entrant variants of getpwnam and getgrnam - udevd: fix memory leak - udevd: fix WAIT_FOR_SYSFS execution order - fix handling of string_escape option - udevd: use a tighter loop for compare_devpath() - udevd: avoid implicit memset in match_attr() - kerneldoc comment fixes - udevd: simplify rules execution loop - udevd: fix termination of rule execution - udevd: be more careful when matching against parents - udevd: shrink struct token to 12 bytes - -Kay Sievers (113): - remove outdated docs/README-gcov_for_udev - libudev: device - add device lookup by subsystem:sysname - libudev: also prefix non-exported functions with udev_* - libudev: add udev_monitor_send_device() - libudev: list - add flag - libudev: device - generate DEVNAME and DEVLINKS properties - vol_id: update README - libudev: handle ! in sysname, add sysnum, return allocated list_entry on add - delete simple-build-check.sh - test: move global ENV{ENV_KEY_TEST}="test" to local rule - libudev: monitor - fix send_device() property copying - libudev: device - add get_envp() to construct envp from property list - libudev: do not include ctrl in libudev.so - libudev: monitor - do not mangle DEVLINKS property - libudev: update DEVLINKS property when properties are read - libudev: device - lookup "subsystem" and "driver" only once - libudev: device - export properties when values are set - libudev: list - handle update of key with NULL value - libudev: ctrl - fix typo in set_env() - libudev: add global property list - libudev: device - copy global properties, unset empty properties - volume_id: btrfs - update magic to latest disk format - udevd: use libudev - move udev_device_db to libudev - rename udev source files - libudev: always add UDEV_LOG - libudev: monitor - export MAJOR/MINOR only if available - udev-node: name_list -> udev_list - udev-rules-parse: name_list -> udev_list - delete name_list, move common file functions - fix sorting of rules files - run_program: prevent empty last argv entry - update IMPORT= file/stdout property parsing - update rules file parsing - delete udev-util-file.c - libudev: list - prepend udev_* to all functions - libudev: add sysnum to test program - test: fix a few unintentially wrongly written rules which cause parse errors - libudev: monitor - add set_receive_buffer_size() - libudev: ctrl - change magic to integer - libudev: make list_node functions available - udevd: use udev_list_node - collect: use udev_list - delete list.h - merge udev-rules.c and udev-rules-parse.c - make struct udev_rules opaque - move run_program to util - udev_event_run() -> udev_event_execute_rules() - udev_rules_run() -> udev_event_execute_run(); - move udev_rules_apply_format() to udev-event.c - udev_list_cleanup() -> udev_list_cleanup_entries() - selinux_init(udev) -> udev_selinux_init(udev) - prefix udev-util.c functions with util_* - pass make distcheck - libudev: device - get_attr_value() -> get_sysattr_value() - cdrom_id: remove ARRAY_SIZE() declaration - replace missing get_attr_value() -> get_sysattr_value() - add "root" == 0 shortcuts to lookup_user/group() - do not use the new work-in-progress parser rule matcher - libudev: device - 128 -> ENVP_SIZE - add util_resolve_subsys_kernel() - handle numerical owner/group string in lookup_user/group() - replace in-memory rules array with match/action token list - do not create temporary node ($tempnode) if node already exists - shrink struct udev_event - shrink struct udev_event - rule_generator: fix netif NAME= value extraction regex - skip SYMLINK rules for devices without a device node - rules: let empty strings added to buffer always return offset 0 - fix uninitialized variable warnings - cache uid/gid during rule parsing - distinguish "match" from "assign" by (op < OP_MATCH_MAX) - determine at rule parse time if we need to call fnmatch() - special-case "?*" match to skip fnmatch() - libudev: monitor - replace far too expensive snprintf() with strlcpy() - libudev: monitor - cache result of monitor send buffer - fix "unused" warnings - remove debug printf - match KEY="A|B" without temporary string copy - match_attr() - copy attr value only when needed - do not init string arrays, just clear first byte - fix $attr{[/]} substitution - libudev: device - fill envp array while composing monitor buffer - test: add RUN+="socket: ..." to a test to run monitor code - libudev: device - allocate envp array only once - update NEWS - udevd: merge exec and run queue to minimize devpath string compares - ATTR{}== always fails if the attribute does not exist - rules: remove SCSI timeouts - rules: remove "add" match from usb device node rule - edd_id: add "change" event match - fstab_import: add "change" event match - write trace log to stderr - log rules file and line number when NAME, SYMLINK, OWNER, GROUP, MODE, RUN is applied - skip entire rule containing device naming keys, if no device can be named - fix udev_node_update_old_links() logic - move some info() to dbg() - add "devel" and "install" switches to autogen.sh - move debugging strings inside #ifdef DEBUG - firmware.sh: record missing files in /dev/.udev/firmware-missing/ - fix list handling in enumerate and rules file sorting - volume_id: btrfs update - info() PROGRAM and IMPORT execution - fix $links substitution - fix cleanup of possible left-over symlinks - do not import the "uevent" file when we only read the db to get old symlinks - usb_id: MassStorage SubClass 6 is "scsi" not "disk" - unify string replacement - $links should be relative - fix indentation - rules: md - add mdadm 3 device naming - cleanup /dev/.udev/queue on startup and exit - udevadm: settle - exit if udevd exits - -Matthias Koenig (1): - volume_id: swap - larger PAGE_SIZE support - -Steven Whitehouse (1): - volume_id: support for GFS2 UUIDs - - -Summary of changes from v129 to v130 -============================================ - -Kay Sievers (26): - fix compile error with --disable-logging - libudev: enumerate - add_device() -> add_syspath() - volume_id: hpfs - read label and uuid - use no_argument, required_argument, optional_argument in longopts - libudev: get rid of selinux - libudev: device - add get_parent_with_subsystem() - usb_id: use libudev - udevadm: info - fix --query=all for devices without a device node - vol_id: add size= option - move selinux noops to udev.h - volume_id: add dbg() as noop to check for compile errors - vol_id: fix logging glue - vol_id: always use the safe string versions for unencoded label and uuid - volume_id: better DDF raid detection - volume_id: add btrfs - volume_id: use PRIu64i, PRIx64 macros - udevd: clarify deprecated sysfs layout warning - libudev: fix --enable-debug - don not print error if GOTO jumps just to next rule - volume_id: add more vfat debugging information - libudev: libudev.pc remove selinux - store node name and symlinks into db symlink target if they are small enough - volume_id: more fat debugging - libudev: fix typo in "multiple entries in symlink" handling - connect /sys and /dev with /sys/dev/{block,char}/: and /dev/{block,char}/: - replace spaces in dm and md name symlinks - - -Summary of changes from v128 to v129 -============================================ - -Alan Jenkins (7): - udev-test.pl: set non-zero exitcode if tests fail - scsi_id: compiler warning on 32-bit - trivial cleanup in udev_rules_iter - avoid repeated scans for goto targets (udev_iter_find_label) - replace strerror() usage with threadsafe "%m" format string - fix messages (inc. debug compile failure) introduced when optimizing "goto" - allow compiler to check dbg() arguments on non-debug builds - -Kay Sievers (46): - libudev: switch to "udev_device_get_parent" - libudev: udev_device - add attribute cache - libudev: handle "device" link as parent, handle "class" "block" as "subsystem" - udevadm: info - fix lookup-by-name - libudev: switch API from devpath to syspath - libudev: rename ctrl_msg to ctrl_msg_wire - vol_id: fix lib logging glue - fix broken symlink resolving - fix udevadm trigger - libudev: pass udev_device in enumerate - libudev: fix "subsystem" value - always include config.h from Makefile - libudev: udev_device_get_devname -> udev_device_get_devnode - libudev: add udev_device_new_from_devnum() - libudev: also import "uevent" file when reading udev database - libudev: add userdata pointer - libudev: replace awkward callback list interfaces with list iterators - libudev: get devnum from uevent file - libudev: enumerate_get_devices_list -> enumerate_get_list - libudev: initialize selinux only when needed - libudev: device - read database only when needed - libudev: rework list handling - libudev: more list rework - lubudev: accept more sys directories as devices, and parent devices - libudev: enumerate - accept list of subsystems to scan, or skip - libudev: enumerate "subsystem" - libudev: enumerate - scan /sys/block/ if needed - libudev: enumerate - split new() and scan() - test: replace ancient sysfs tree with recent one - test: add missing pci directory because of .gitignore *.7 - gitignore: move *.8 to subdirs - test: replace last reference of "/class/*" devpath - fix dbg() callers - libudev: enumerate - scan devices and subsystems, add subsystem and attribute filter - udevadm: trigger: use libudev - fix segfault caused by wrong pointer used in dbg() - libudev: device_init() -> device_new() - udevadm: trigger fix long option --type= - libudev: add queue interface - udevadm: settle - use libudev queue - libudev: device - handle /sys/block// - libudev: enumerate - ignore regular files while scanning - udevadm: trigger --type=failed - use libudev queue - rules: ieee1394 - create both, by-id/scsi-* and by-id/ieee-* links - build: include Makefile.am.inc in all Makefile.am - udevd: print warning if CONFIG_SYSFS_DEPRECATED is used - - -Summary of changes from v127 to v128 -============================================ - -Alan Jenkins (8): - fix uninitialized name_list error::ignore_error - do not needlessly declare some local variables in udev_rules_parse.c as static - remove deprecated envp[] in main() - fix name compare bug name_list_key_add() - remove redundant string copy in udev_rules_apply_format() - remove redundant "remove trailing newlines" in udevadm info - threadsafe rules iteration - fix off-by-one in pass_env_to_socket() - -Kay Sievers (53): - libudev: add monitor documentation - libudev: fix --disable-log - autogen.sh: add --with-selinux - volume_id: hfs - calculate proper uuid - fix dangling pointer returned by attr_get_by_subsys_id() - udev-test.pl: add --valgrind option - libudev: libudev.pc add Libs.private - volume_id: fail on undefined __BYTE_ORDER - remove FAQ - libudev: fix monitor documentation - libudev: add udev_device_get_syspath() - udev_device_init() remove statically allocated device support - udevadm: info - fix broken --device-id-of-file= - udevadm: control - use getopt_long() - udevadm: print warning to stderr if udevadm is called by symlink - udev-test.pl: remove left-over comment from --valgrind option - udevadm: rename source files - udevadm: rename internal functions to udevadm_* - udevadm: split out control functions - udevadm: move init from commands to udevadm - autogen.sh: add debug - use libudev code, unify logging, pass udev context around everywhere - volume_id: linux_raid - fix logic for volumes with size == 0 - vol_id: add --debug option - udevadm: add --version --help options to man page, hide them as commands - move udev_ctrl to libudev-private - udev-test.pl: set udev_log="err" - test-udev: cleanup libudev context and overridden rules file string - test-udev: remove unused var - add a bunch of private device properties to udev_device - udevadm: monitor - use libudev for udev monitor - libudev: monitor - add event properties to udev_device - udevadm: log message if udevadm link is used - udevd: remove max_childs_running logic - libudev: monitor- add netlink uevent support - udevadm: monitor - use libudev code to retrieve device data - libudev: udev_device - read "driver" value - libudev: rename enumerate function - libudev: add selinux - libudev: initialize selinux after logging - volume_id: merge util.h in libvolume_id-private.h - update file headers - libudev: udev_device - add more properties - libudev: do not use udev_db.c - libudev: get rid of udev_sysfs.c - libudev: get rid of udev_utils.c - libudev: rename libudev-utils.c libudev-util.c - libudev: do not use any udev source file - extras: use libudev code - convert to libudev and delete udev_utils_string.c - get rid of udev_sysdeps.c - use size definitions from libudev - udevadm: info - use "udev_device" - - -Summary of changes from v126 to v127 -============================================ - -Karel Zak (2): - build-sys: don't duplicate file names - build-sys: remove non-POSIX variable names - -Kay Sievers (26): - add inotify dummy definitions if inotify is not available - build: remove autopoint check - udevadm: trigger - add missing attr filter to synthesized "subsystem" register events - ignore duplicated rules file names - fix .gitignore - rules: delete all distro rules which do not use default rules - rules: add nvram - rules: add isdn rules - rules: Gentoo update - add missing includes - add some warnings - update .gitignore - add missing 'v' for "make changelog" - build: fix "make dist" - vol_id: make the --offset= argument optional - rules: optical drives - probe at last session offset, do not probe for raid - libudev: add library to access udev information - libudev: split source files - update INSTALL - libudev: add udev event monitor API - volume_id: remove deprecated functions and bump major version - volume_id: remove left-over fd close() - split udev_device.c to leave out rules handling from libudev - libudev: link against selinux if needed - firmware.sh: lookup lookup kernel provided firmware directory - libudev: require LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE - -Michal Soltys (1): - rules: fix md rules for partitioned devices - - -Summary of changes from v125 to v126 -============================================ - -Kay Sievers (9): - delete all Makefiles and move udev source to udev/ - use autotools - rules: mode 0660 for group "disk" - rules: update Fedora rules - update ChangeLog - INSTALL: --enable-selinux not --with-selinux - volume_id: move static lib to $prefix - volume_id: create relative links - rules: run vol_id on opticals only if media is found - -Marco d'Itri (1): - rules: Debian update - -Thomas Koeller (1): - use proper directory lib/lib64 for libvolume_id - - -Summary of changes from v124 to v125 -============================================ - -John Huttley (1): - rules: tape rules - add nst to usb and 1394 links - -Karl O. Pinc (1): - man: clarify $attr{} parent searching - -Kay Sievers (14): - collect: fix size_t printf - path_id: suppress trailing '-' like 'ID_PATH=pci-0000:05:01.0-' - rules: add v4l persistent links - docs: update some docs and delete outdated stuff - scsi_id: fix fallback to sg v3 for sg nodes - rules: fix cciss rules for partition numbers > 9 - udev.conf: udevcontrol -> udevadm control - rules: use consistently OPTIONS+= - scsi_id: the fallback fix broke error handling - man: rebuild from xml - do not touch node ownership and permissions, if already correct - rules: tape rules - add nst to by-path/ links - udevadm: info - add --export format to --device-id-of-file= - move default rules from /etc/udev/rules.d/ to /lib/udev/rules.d/ - -Marco d'Itri (7): - rules_generator: net rules - do not print error if file is missing and ignore commented rules - man: add link_priority default value - scsi_id: man page fix - udevadm: settle - add verbose output when running into timeout - rules: Debian update - rules: Debian update - ignore rule with GOTO to a non-existent label - -Thomas Koeller (1): - scsi_id: include sys/stat.h - -Tobias Klauser (1): - collect: check realloc return value - - -Summary of changes from v123 to v124 -============================================ - -Kay Sievers (1): - cdrom_id: fix recognition of blank media - - -Summary of changes from v122 to v123 -============================================ - -Erik van Konijnenburg (3): - add substitution in MODE= field - Makefile: use udevdir in "make install" - volume_id: support for oracleasm - -Harald Hoyer (1): - scsi_id: retry open() on -EBUSY - -Karel Zak (2): - volume_id: remove unnecessary global variable - volume_id: enable GFS probing code, add LABEL support - -Kay Sievers (5): - edd_id: call it only for sd* and hd* - rename WAIT_FOR_SYSFS to WAIT_FOR and accept an absolute path - rules: tape rules - use bsg device nodes for SG_IO - rules: persistent net - handle "locally administered" ibmveth MAC addresses - cdrom_id: export ID_CDROM_MEDIA_TRACK_COUNT_AUDIO=, ID_CDROM_MEDIA_TRACK_COUNT_DATA= - -Michal Soltys (1): - man: add NAME== match entry - -Xinwei Hu (2): - collect: realloc buffer, if needed - udevd: export .udev/queue/$seqnum before .udev/uevent_seqnum - - -Summary of changes from v121 to v122 -============================================ - -Hannes Reinecke (2): - scsi_id: remove all sysfs dependencies - scsi_id: add SGv4 support - -Karel Zak (1): - volume_id: clean up linux_raid code - -Kay Sievers (8): - scsi_id: update man page - scsi_id: remove bus_id option - scsi_id: add --sg-version= option - rules: adapt to new scsi_id - rules: adapt tape rules to new scsi_id - scsi_id: add bsg.h - volume_id: bump version - Makefile: do not create udevcontrol, udevtrigger symlinks - -MUNEDA Takahiro (2): - man: udevd- fix udev(8) reference - man: scsi_id - -Matthias Schwarzott (1): - cdrom_id: fix segfault - - -Summary of changes from v120 to v121 -============================================ - -Damjan Georgievski (1): - libvolume_id: recognize swap partitions with a tuxonice hibernate image - -Daniel Drake (1): - writing udev rules: fix rule typos - -David Woodhouse (1): - rules_generator: net rules - add "dev_id" value to generated rules - -Harald Hoyer (1): - selinux: more context settings - -Kay Sievers (21): - udevinfo: do not replace chars when printing ATTR== matches - vol_id: add --offset option - cdrom_id: replace with version which also exports media properties - udevd: at startup write message including version number to kernel log - rules_generator: net rules - always add KERNEL== match to generated rules - selinux: fix missing includes - allow setting of MODE="0000" - path_id: remove subsystem whitelist - logging: add trailing newline to all strings - scsi_id: initialize serial strings - persistent device naming: also read unpartitioned media - cdrom_id: add more help text - add $links substitution - fstab_import: add program to IMPORT matching fstab entry - add OPTIONS+="event_timeout=" - write "event_timeout" to db - udevadm: trigger - add --env= option - udevadm: control - fix --env key to accept --env== - udevadm: info - do not print ATTR{dev}== - persistent device naming: update tape rules - rules: update md rules - - -Summary of changes from v119 to v120 -============================================ - -Kay Sievers (9): - test: remove duplicated EXTRA entry - rules: remove last WAIT_FOR_SYSFS, load ppdev, switch scsi_device - udevadm: trigger - option to synthesize events and pass them to a socket - udevadm: info - resolve devpath if symlink is given - udevadm: remove old man page links and compat links for debugging tools - udevadm: trigger - fix broken socket option check - udevadm: trigger - fix --socket== + --verbose - also accept real socket files for RUN+="socket:" - persistent device naming: cleanup storage rules - -Michael Kralka (1): - udevd: serialize events if they refer to the same major:minor number - - -Summary of changes from v118 to v119 -============================================ - -Anthony L. Awtrey (1): - do not skip RUN execution if device node removal fails - -Harald Hoyer (2): - rules: Fedora update - rules: do not set GROUP="disk" for scanners - -Jiri Slaby (1): - rules_generator: add missing write_net_rules unlock - -Karel Zak (2): - volume_id: fix UUID raw buffer usage - volume_id: fix typo in function documentation - -Kay Sievers (10): - switch mailing lists to linux-hotplug@vger.kernel.org - rules: remove tty rule which can never run because of an earlier "last_rule" - volume_id: update ext detection - selinux: set context for real file name not the temp name - hack to allow ATTR{block/*/uevent}="change" - rules_generator: add KERNEL=="*" to generated rules - persistent device naming: also run on "change" event - test: add "subsystem" links to all devices - sysfs: depend on "subsystem" link - extend hack to allow TEST=="*/start" - -Matthias Schwarzott (1): - volume_id: respect LDFLAGS - -Neil Williams (1): - volume_id: add prefix=, exec_prefix= - -Roy Marples (1): - Makefile: do not require GNU install - - -Summary of changes from v117 to v118 -============================================ - -Daniel Drake (1): - doc: update "writing udev rules" - -Hannes Reinecke (1): - volume_id: LVM - add uuid - -Kay Sievers (9): - remove udevstart - rules_generator: do not create rules with insufficient matches - man: udevadm settle - mention 180 seconds default timeout - libvolume_id: squashfs - add endianess support for LZMA compression - rules: add AOE rule - volume_id: md - add metadata minor version - volume_id: run only once into a timeout for unreadable devices - create_floppy_devices: fix logic for more than one floppy device - volume_id: also add readable check to probe_all() - -Matthias Schwarzott (1): - rules: Gentoo update - -Michael Prokop (1): - libvolume_id: squashfs+LZMA compression detection - - -Summary of changes from v116 to v117 -============================================ - -Dan Nicholson (2): - extras: ignore built and generated files - volume_id: create relative symlink when $(libdir) = $(usrlibdir) - -Kay Sievers (15): - usb_id: fail if vendor/product can not be retrieved - rules: SUSE update - firmware: do not print error if logger is missing - volume_id: vfat - allow all possible sector sizes - volume_id: LUKS - export version - volume_id: ntfs - rely on valid master file table - volume_id: bump version - udevinfo: exclude "uevent" file from --attribute-walk - udevadm: merge all udev tools into a single binary - udevadm: accept command as option, like --help, --version - udevadm: add info option --device-id-of-file= - Makefile: fix bogus version number than got committed - udevadm: also return major==0 results for --device-id-of-file - man: udevd.8 - remove udevcontrol section - udevadm: control - allow command to be passed as option - -MUNEDA Takahiro (1): - man: fix udevadm.8 typo - -Matthias Schwarzott (2): - firmware: remove hardcoded path to logger - rules: Gentoo update - -VMiklos (1): - rules: Frugalware update - - -Summary of changes from v115 to v116 -============================================ - -Bryan Kadzban (1): - rules: fix typos - -Harald Hoyer (3): - check line length after comment check and whitespace strip - only install *.rules - remove extra space from udevinfo symlink output - -Kay Sievers (29): - rules: fix two trivial typos - rules: random and urandom are 0666 - rules: add REMOVE_CMD rule - track "move" events to rename database and failed files - rules: Gentoo update - rules: add i2o driver rule - man: recreate man pages - volume_id: fix linux_raid metadata version 1.0 detection - add $name substitution - do not delete the device node with ignore_remove, but handle the event - print warning for invalid TEST operations - rules: do not delete /lib/udev/devices/ nodes on "remove" - rules: remove broken nvram group assignment without any permission - add /dev/rtc symlink if new rtc drivers are used - increase WAIT_FOR_SYSFS timeout to 10 seconds - rules: put bsd nodes in /dev/bsd/ directory - path_id: fix for stacked class devices - ignore device node names while restoring symlinks from the stack - use SEQNUM in /dev/.udev/queue/ instead of devpath - rules: add memstick module loading - udevinfo: simplify symlink printing logic - prevent wrong symlink creation if database disagress with current rules - fix wrong variable used in logged string - update README - rule_generator: move all policy from write_net_rules to the rules file - rules: call usb_id only for SUBSYSTEMS=="usb" - rules: split out and fix persistent tape rules - fix debug output string - rule_generator: always match netif type in generated rule - -Matthias Schwarzott (3): - rules: Gentoo update - rules: Gentoo update - rules: Gentoo update - -Michael Morony (1): - set buffer size if strlcpy/strlcat indicate truncation - -maximilian attems (1): - correct includes in udev_selinux.c - - -Summary of changes from v114 to v115 -============================================ - -Harald Hoyer (1): - rules: fix typo in 80-drivers.rules - -Kay Sievers (15): - rules: add default rules - rules: update SUSE rules - rules: add packages rules - rules: add ia64 rules - rules: move md-raid rules to packages dir - rules: run vol_id only for partitions - rules: update Fedora rules - edd_id: move persistent rules to its own file - accept relative path for TEST - rules: add iowarrior rule - volume_id: fix sqashfs detection - do not ignore dynamic rule if it is the last one in the list - rule_generator: fix wrong DRIVERS!= logic - rules: update Fedora - Makefile: install default rules - -Marco d'Itri (3): - rules_generator: remove policy from write_cd_rules - rules_generator: fix write_cd_rules when similar names exist in the root directory - rules: Debian update - - -Summary of changes from v113 to v114 -============================================ - -Hannes Reinecke (3): - collect: extra to synchronize actions across events - add $driver subtitution - rules_generator: add S/390 persistent network support - -Kay Sievers (24): - rules_generator: remove executable flag from include file - always unlink temporary file before creating new one - rules: SUSE update - volume_id: ext4 detection - udevtrigger: allow to specify action string - add option to RUN key to ignore the return value of the program - use global udev_log variable instead of parameter in run_program - add udev_rules_run() to handle RUN list - move udev_utils_run.c into udev_rules.c - rules: SUSE update - name_list: rename loop_name -> name_loop - handle dynamic rules created in /dev/.udev/rules.d/ - allow SYMLINK== match - libvolume_id: use /usr/$libdir in pc file - Makefile: add --as-needed flag to ld - restore behavior of NAME== - rules_generator: remove "installation" function - udevtrigger: trigger "driver" events - rules: update SUSE - rules: Fedora update - rules: add "do not edit" comment - rules: Fedora update - rules_generator: skip random MAC addresses - write changed network interface names to the kernel log - -Matthias Schwarzott (3): - rules: Gentoo update - fix inotify to work not only once - rules: Gentoo update - -Richard Hughes (1): - Makefile: add "make dist" for nightly snapshots - - -Summary of changes from v112 to v113 -============================================ - -David Zeuthen (1): - vol_id: do not fail if unable to drop privileges - -Kay Sievers (12): - add missing ChangeLog - make ATTR{[$SUBSYSTEM/$KERNEL]}="" working - rules: recognize partitions and disk devices properly - rules: SUSE update - atomically replace existing nodes and symlinks - do not try to create existing file - info() for ignore_remove - rules: SUSE update - Makefile: check for missing ChangeLog or RELEASE-NOTES at release - allow to disable the replacement of unusual characters - no newline in log messages - udevd: do not use syslog if --verbose (debugging) is used - -Tobias Klauser (1): - fix typo in udev_utils_run.c - - -Summary of changes from v111 to v112 -============================================ - -Fabio Massimo Di Nitto (1): - rules: ignore partitons that span the entire disk - -Hannes Reinecke (1): - cciss device support - -Kay Sievers (34): - udevd: close /proc/meminfo after reading - create_floppy_devices: remove dead "unlink" code - volume_id: add function documentation - udev_db: escape path names with \x00 instead of %00 - udevsettle: use long options - replace_chars: replace spaces in node name - volume_id: add and export string encoding function - vol_id: export encoded strings - rules: use encoded strings instead of skipping characters - udevtest: print message before log output - volume_id: escape % character - replace_chars: replace % character - IMPORT: do not mangle whitespace - scsi_id: do not install symlink in /sbin - rules: SUSE update - volume_id: terminate overlong label strings - scsi_id: add long options - rules: use long options for scsi_id - path_id: skip subsystem directory - rules: fix cciss rule - rules: SUSE update - scsi_id: fix typo in help text - fix "do not access parent" warning for ATTR{} - sysfs: add device lookup by $SUBSYSYTEM:$KERNEL - events for "bus" and "class" registration must be matched as "subsystem" - udevtest: add --subsystem option - sysfs: change order of subsystem lookup - add $sys substitution - add TEST=="" key - add "[$SUBSYSTEM/$KERNEL]" lookup - sysfs: handle bus/class top-level directories - sysfs: skip unknown sysfs directories - rules: SUSE update - release 112 - -Miklos Vajna (2): - create_floppy_devices: add man page - path_id: remove on make uninstall - -Ryan Lortie (1): - volume_id: support for long-filename based labels - -Scott James Remnant (2): - replace_untrusted_chars: replace all whitespace with space - run_program: log "info" not "error" if program is missing - - -Summary of changes from v110 to v111 -============================================ - -Kay Sievers (19): - rules: SUSE update - rules: Fedora update - volume_id: use md native uuid format - vol_id: use long options - volume_id: add volume_id_get_* functions - vol_id: use volume_id_get_* - udevd: use fgets() to read /proc files - volume_id: add internal UUID_STRING - volume_id: add DDF support - vol_id: README update - volume_id: rename UUID_64BIT_LE/BE - vol_id: add ID_FS_UUID_SAFE - rules: use ID_FS_UUID_SAFE - rules: SUSE update - volume_id: give access to list of all available probers - vol_id: use libvolume_id prober list for --probe-all - volume_id: add remaining names for prober lookup by type - rules: SUSE update - volume_id: vol_id depends on libvolume_id - -Matthias Schwarzott (2): - volume_id: fix Makefile for parallel make - rules: Gentoo update - - -Summary of changes from v109 to v110 -============================================ - -Harald Hoyer (1): - udevcontrol: allow to set global variables in udevd - -Kay Sievers (13): - remove eventrecorder.sh - update SUSE rules - volume_id: add md metadata 1.0, 1.1, 1.2 support - unset variable with ENV{VAR}="" - delete copies of default rules in SUSE rules - volume_id: ext - fix endianess in version number - rules: Fedora update - volume_id: old md metadata has only 32 bit for the uuid - volume_id: minix version 3 support - don't create $tempnode for devices without major - usb_id: add to help text - ata_id: use getopt_long() - rules: SUSE update - -Matthias Schwarzott (3): - Makefile: respect CFLAGS/LDFLAGS - rules: Gentoo update - ata_id: don't log error for libata devices on older kernels - - -Summary of changes from v108 to v109 -============================================ - -Harald Hoyer (1): - create_floppy_devices: create nodes with correct selinux context - -Kay Sievers (11): - udevtest: export ACTION string if given as option - update SUSE rules - make ACTION!="add|change" working - udevtest: import uevent variables if possible - udevinfo: export all information stored in database - default rules: add libata compat links - create_path: don't fail if something else created the directory - udevd: fix serialization of events - path_id: remove broken example - libvolume_id: do not install static library - update SUSE rules - -Matthias Schwarzott (2): - update Gentoo rules - persistent device naming: add joystick links - -VMiklos (1): - path_id: add man page - - -Summary of changes from v107 to v108 -============================================ - -Kay Sievers (3): - udevinfo: relax check for the correct device if looked up by name - don't write to sysfs files during test run - finally remove the directory event-multiplexer crap - -Matthias Schwarzott (2): - write_cd_rules: set default link type to "by-id" for usb and ieee1394 devices - update Gentoo rules - -Pozsar Balazs (1): - udevsettle: read udev not kernel seqnum first - - -Summary of changes from v106 to v107 -============================================ - -Jean Tourrilhes (1): - udevtest: export UDEV_LOG if we changed it - -Kay Sievers (33): - man: add missing options to various man pages - man: fix typo - create_floppy_devices: apply specified mode without umask - man: spelling fixes - udevmonitor: add switch for kernel and udev events - default rules: wait for 0:0:0:0 scsi devices only - update Fedora rules - delete dasd_id, it moved to s390-tools - update Gentoo rules - encode db-file names, instead of just replacing '/' - update internal variables if we see $DEVPATH during IMPORT - increase /proc/stat buffer - maintain index over device-names to devpath relation - restore overwritten symlinks when the device goes away - store devpath with the usual leading slash - add link_priority to rule options, and store it in database - pick actual valid device in udev_db_lookup_name - cleanup already existing db-entries and db-index on device update - selinux: move selinux_exit() to the main programs - remove old error message - read list of devices from index, make index private to database - priority based symlink handling - volume_id: get rid of compiler warning - udevinfo: remove -d option - update %n on netif name change - if a node goes away, possibly restore a waiting symlink - update TODO - man: add "link_priority" option - update SUSE rules - udevtest: add --force mode - udevinfo: print link priority - usb_id: append target:lun to storage device serial - run_directory: add final warning before removal - -Marco d'Itri (1): - update Debian rules - -Matthias Schwarzott (2): - udevd: cleanup std{in,our,err} on startup - udevmonitor: fix swapped event switch descriptions - - -Summary of changes from v105 to v106 -============================================ - -A. Costa (1): - man: fix typos in scsi_id and udevd - -Andrey Borzenkov (2): - vol_id: add -L to print raw partition label - vol_id: document -L - -Jamie Wellnitz (1): - persistent device naming: tape devices and medium changers - -Kay Sievers (15): - exclude parent devices from DRIVER== match - volume_id: really fix endianess bug in linux_raid detection - release 105 - man: correct udevinfo --export-db - path_id: append LUN to iSCSI path - create_floppy_devices: add option for owner/group - update example rules - apply format chars to ATTR before writing to sysfs - add (subsystem) to udevmonitor output - update DRIVER== changes - remove --version from the udevinfo man page - add test for an attribute which contains an operator char - man: add note about parent matching behavior - scsi_id: accept tabs in /etc/scsi_id.conf - remove dead rule in persistent tape rules - -Matthias Schwarzott (4): - correct typo in extras/scsi_id/scsi_id.conf - fix retry-loop in netif-rename code - add option --version to udevd - rule_generator: fix for creating rules on read-only filesystem - -Peter Breitenlohner (1): - fix INSTALL_PROGRAM vs. INSTALL_SCRIPT - -Sergey Vlasov (3): - udevd: init signal pipe before daemonizing - unlink old database file before creating a new one - fix %c $string substitution - -Theodoros V. Kalamatianos (1): - fix udev attribute names with a colon - - -Summary of changes from v104 to v105 -============================================ - -A. Costa (1): - man: fix typos in scsi_id and udevd - -Andrey Borzenkov (2): - vol_id: add -L to print raw partition label - vol_id: document -L - -Kay Sievers (2): - exclude parent devices from DRIVER== match - volume_id: really fix endianess bug in linux_raid detection - -Matthias Schwarzott (2): - correct typo in extras/scsi_id/scsi_id.conf - fix retry-loop in netif-rename code - -Peter Breitenlohner (1): - fix INSTALL_PROGRAM vs. INSTALL_SCRIPT - -Sergey Vlasov (3): - udevd: init signal pipe before daemonizing - unlink old database file before creating a new one - fix %c $string substitution - - -Summary of changes from v103 to v104 -============================================ - -Kay Sievers (12): - update Fedora rules - update example rules - update SUSE rules - update SUSE rules - volume_id: fix endianess bug in linux_raid detection - man: fix udevmonitor text - man: recreate from xml - rename config "filename" to "dir" - remove outdated documentation - rename "udev.c" to "test-udev.c" - it is only for testing - update Fedora rules - use git-archive instead of git-tar-tree - -Kazuhiro Inaoka (1): - inotify syscall definitions for M32R - -Marco d'Itri (2): - write_cd_rules: identity-based persistence - scsi_id: remove trailing garbage from ID_SERIAL_SHORT - -Russell Coker (1): - SELinux: label created symlink instead of node - - -Summary of changes from v102 to v103 -============================================ - -Kay Sievers: - persistent storage rules: skip gnbd devices - volume_id: add checksum check to via_raid - volume_id: add comment about hfs uuid conversion - update SUSE rules - update Fedora rules - - -Summary of changes from v101 to v102 -============================================ - -Daniel Drake: - writing_udev_rules: fix typo in example rule - -Kay Sievers: - create missing ChangeLog for version 101 - update SUSE rules - update default rules - first try "subsystem" link at a parent device, before guessing - if /sys/subsystem exists, skip class, bus, block scanning - scsi_id: export ID_SERIAL_SHORT without vendor/product - update SUSE rules - -MUNEDA Takahiro: - path_id: fix SAS disk handling - - -Summary of changes from v100 to v101 -============================================ - -Arjan Opmeer: - fix udevinfo help text typo - -Bryan Kadzban: - cleanup default rules - add IMPORT operations to the udev man page - -Kay Sievers: - remove Makefile magic for leading '0' in version - udevd: use getopt_long() - udevd: add --verbose option to log also to stdout - udevd: add --debug-trace option - rule_generator: improve net rule comment generation - volume_id: correct iso9660 high sierra header - warn if a PHYSEDV* key, the "device" link, or a parent attribute is used - don't print PHYSDEV* warnings for old WAIT_FOR_SYSFS rules - udevinfo: print error in --attribute-walk - udev_sysfs: unify symlink resolving - udevtrigger: trigger devices sorted by their dependency - fix spelling in deprecation warning - release 101 - -Michał Bartoszkiewicz: - udevtrigger: fix typo that prevents partition events - -Miles Lane: - clarify "specified user/group unknown" error - -Piter PUNK: - update slackware rules - -VMiklos: - update Frugalware rules - - -Summary of changes from v099 to v100 -============================================ - -Kay Sievers: - update SUSE rules - fix messed up ChangeLog from release 099 - man: add $attr{} section about symlinks - revert persistent-storage ata-serial '_' '-' replacement - - -Summary of changes from v098 to v099 -============================================ - -Greg KH: - update Gentoo rules - -Kay Sievers: - udev_db.c: include - use fnmatch() instead of our own pattern match code - rename major/minor variable to maj/min to avoid warning - update source file headers - udevtest: print header that ENV{} can't work - update TODO - udevtrigger: options to filter by subsystem and sysfs attribute - udevtrigger: remove unused longindex - udevinfo: use long options - udevd: use files instead of symlinks for /dev/.udev/queue,failed - udevtrigger: fix pattern match - reorder options in udevinfo man page - udevinfo: fix SUBSYTEMS spelling error - fix ENV{TEST}="Test: $env{TEST}" - let $attr{symlink} return the last element of the path - cdrom_id: add rules file to call cdrom_id - udevinfo: do not show symlinks as attributes in --attribute-walk - remove broken name_cdrom.pl - -Marco d'Itri: - update Debian rules - run_program: close pipe fd's which are connected to child process - add persistent rules generator for net devices and optical drives - -MUNEDA Takahiro: - changes rules for ata disk from '_' to '-' - -Sergey Vlasov: - make struct option arrays static const - fix "subsytem" typo - - -Summary of changes from v097 to v098 -============================================ - -Alex Merry: - udevtest: allow /sys in the devpath paramter - -Harald Hoyer: - selinux: init once in the daemon, not in every event process - -Kay Sievers: - udevd: remove huge socket buffer on the control socket - man page: fix typo - rename udev_libc_wrapper -> udev_sysdeps - db: store devpath - node relationship for all devices - udevinfo: allow -a -n - udevinfo, udevtest: simplify '/sys' stripping from devpath argument - lookup_user, lookup_group: report "unknown user" and "lookup failed" - consistent key naming to match only the event device or include all parent devices - skip rule, if too may keys of the same type are used - introduce ATTR{file}="value" to set sysfs attributes - update SUSE rules - update default rules - export DRIVER for older kernels as a replacement for PHYSDEVDRIVER - fix typo in SUBSYSTEMS key parsing - udevtrigger: add --retry-failed - volume_id: add suspend partition detection - vol_id: use primary group of 'nobody' instead of 'nogroup' - remove built-in /etc/passwd /etc/group parser - always expect KEY{value} on ATTR, ATTRS, ENV keys - use new key names in test programs - cleanup commandline argument handling - db: don't create a db file for only a node name to store - man: add ATTR{file}="value" assignment - -Lennart Poettering: - volume_id: fix fat32 cluster chain traversal - -Marco d'Itri: - fix 'unknow user' error from getpwnam/getgrnam - fix rc when using udev --daemon - update Debian rules - -Michał Bartoszkiewicz: - man pages: fix typos - - -Summary of changes from v096 to v097 -============================================ - -Anssi Hannula: - add joystick support to persistent input rules - -Kay Sievers: - firmware.sh: remove needless '/' - vol_id: add --skip-raid and --probe-all option - switch uevent netlink socket to group 1 only - increase /proc/stat read buffer - use "change" instead of "online" events - remove 'static' from local variable - libvolume_id: add parameter 'size' to all probe functions - man pages: replace 'device-path' by 'devpath' - man pages: work around xmlto which tries to be smart - refresh vol_id man page - udevinfo: add DRIVER== - Makefile: fix dependency - libvolume_id: read ufs2 label - switch ifdef __KLIBC__ to ifndef __GLIBC__ - report failing getpwnam/getgrnam as error - rename udevcontrol message types and variables - initialize unused sockets to -1 - udevd: remove useless udevinitsend parameter - update README - udevd: autotune max_childs/max_childs_running - update frugalware rules - update SUSE rules - move default rules to etc/udev/rules.d/ - add 'crypto' devices to persistent storage rules - add late.rules to default rules - update Fedora rules - don't report an error on overlong comment lines - update SUSE rules - udevd: read DRIVER from the environment - -Marco d'Itri: - make rename_netif() error messages useful - path_id: fix an harmless syntax error - -Piter PUNK: - update slackware rules - -Richard Purdie: - Fix inotify syscalls on ARM - - -Summary of changes from v095 to v096 -============================================ - -Kay Sievers: - Makefiles: fix .PHONY for man page target - allow longer devpath values - path_id: prepare for new sysfs layout - - -Summary of changes from v094 to v095 -============================================ - -Kay Sievers: - update SUSE rules - don't remove symlinks if they are already there - allow "online" events to create/update symlinks - udevinfo: clarify parent device attribute use - update SUSE rules - netif rename: optimistic loop for the name to become free - remove broken %e enumeration - -Tobias Klauser: - print usage of udevcontrol when no or invalid command is given - - -Summary of changes from v093 to v094 -============================================ - -Daniel Drake: - update "writing udev rules" - -Kay Sievers: - libvolume_id: gfs + gfs2 support - remove MODALIAS key and substitution - add persistent-input.rules - -Marco d'Itri: - update Debian rules - - -Summary of changes from v092 to v093 -============================================ - -Hannes Reinecke: - path_id: add support for iSCSI devices - -Kay Sievers: - libvolume_id: fat - check for signature at end of sector - libvolume_id: add more software raid signatures - update Fedora rules - path_id: prevent endless loop for SAS devices on older kernels - remove udevsend - replace binary firmware helper with shell script - skip device mapper devices for persistent links - - -Summary of changes from v091 to v092 -============================================ - -Kay Sievers: - don't include stropts.h, some libc's don't like it - udevd: create leading directories for /dev/.udev/uevent_seqnum - vol_id: fix logging from libvolume_id's log function - update SUSE rules - update SUSE rules - add more warnings for invalid key operations - fix offsetof() build issue with recent glibc - selinux: fix typo in block device node selection - vol_id: add NetWare volume detection - edd_id: fix "(null)" output if "mbr_signature" does not exist - update Fedora rules - libvolume_id: nss - use different uuid - -Libor Klepac: - path_id: add platform and serio support - -Marco d'Itri: - update Debian rules - path_id: fix bashism - - -Summary of changes from v090 to v091 -============================================ - -Hannes Reinecke: - path_id: fix SAS device path generation - -Kay Sievers: - udevtest: don't try to delete symlinks - persistent rules: fix typo in dm rule - allow NAME=="value" to check for already assigned value - udevd: export initial sequence number on startup - - -Summary of changes from v089 to v090 -============================================ - -Kay Sievers: - udevd: export current seqnum and add udevsettle - volume_id: fix endianess conversion typo for FAT32 - merge device event handling and make database content available on "remove" - set default udevsettle timeout to 3 minutes - export INTERFACE_OLD if we renamed a netif - let udevmonitor show the possibly renamed devpath - volume_id: move some debug to info level - udevtrigger: fix event order - usb_id: remove uneeded code - remove old symlinks before creating current ones - path_id: fix loop for SAS devices - apply format char to variables exported by ENV - -Marco d'Itri: - add inotify support for hppa and MIPS and log if inotify is not available - -Matt Kraai: - fix typo in error message - - -Summary of changes from v088 to v089 -============================================ - -Hannes Reinecke: - path_id: add bus to USB path - -Kay Sievers: - change rule to skip removable IDE devices - don't create uuid/label links for raid members - volume_id: provide library - fix rule order for persistent tape links - update man page - volume_id: provide a custom debug function - volume_id: rename subdirectory - volume_id: use shared library by default - because is better than cause - volume_id: remove some global symbols - volume_id: define exported symbols - remove all stripping code - man pages: mention udev(7) not udev(8) - update Debian rules - move all *_id programs to /lib/udev/ - update Red Hat rules - update SUSE rules - pass CROSS_COMPILE to AR and RANLIB down to extras/ - volume_id: update README - volume_id: generate man page from xml source - update README - fix symlink targets in Makefiles - - -Summary of changes from v087 to v088 -============================================ - -Hannes Reinecke: - persistent links: add scsi tape links and usb path support - -Kay Sievers: - volume_id: add squashfs detection - reset signal handler in event process - correct use of fcntl() - add udevtrigger to request events for coldplug - add ',' to trusted chars - volume_id: remove partition table parsing code - volume_id: remove all partition table support - fix spelling error in debug string - rename "persistent disk" to "persistent storage" - fix output for USB path - - -Summary of changes from v086 to v087 -============================================ - -Hannes Reinecke: - path_id: support SAS devices - -Kay Sievers: - fix persistent disk rules to exclude removable IDE drives - warn about %e, MODALIAS, $modalias - remove devfs rules and scripts - -Masatake YAMATO: - typo in debug text in udev_run_hotplugd.c - - -Summary of changes from v085 to v086 -============================================ - -Kay Sievers: - volume_id: replace __packed__ by PACKED macro - volume_id: split raid and filesystem detection - volume_id: add missing return - udevd: fix queue export for multiple events for the same device - -Kyle McMartin: - workaround missing kernel headers for some architectures - -Nix: - update to udev-084/doc/writing_udev_rules - - -Summary of changes from v084 to v085 -============================================ - -Andrey Borzenkov: - Fix trivial spelling errors in RELEASE-NOTES - -Jeroen Roovers: - fix typo in parisc support to path_id - -Kay Sievers: - make WAIT_FOR_SYSFS usable in non "wait-only" rules - fix typo in man page - include sys/socket.h for klibc build - cramfs detection for bigendian - exit WAIT_FOR_SYSFS if the whole device goes away - update SUSE rules - update Red Hat rules - update Gentoo rules - include errno.h in udev_libc_wrapper.c - - -Summary of changes from v083 to v084 -============================================ - -Kay Sievers: - update SUSE rules - switch CROSS to CROSS_COMPILE - replace fancy silent build program by simple kernel build like logic - move manpages to top level - remove UDEVD_UEVENT_INITSEND - whitespace fixes - scsi_id: remove dead files - optimize sysfs device and attribute cache - let SYSFS{} look at the device, not only the parent device - add debug output to sysfs operations - - -Summary of changes from v082 to v083 -============================================ - -Andrey Borzenkov: - man page: document when substitutions are applied for RUN and other keys - check for ignore_device in loop looks redundant - -Kay Sievers: - udevstart: fix NAME="" which prevents RUN from being executed - find programs in /lib/udev for IMPORT if {program} is not given - don't add $SUBSYSTEM automatically as $1 to programs - remove redundant substitution of RUN key - - -Summary of changes from v081 to v082 -============================================ - -Andrey Borzenkov: - substitute format chars in RUN after rule matching - -Kay Sievers: - scsi_id, usb_id: request device parent by subsystem - path_id: work with "all devices in /sys/devices" - ignore all messages with missing devpath or action - Makefile: remove dynamic config file generation - path_id: handle fiber channel (Hannes Reinecke ) - usb_id: don't fail on other subsytems than "scsi" - don't do RUN if "ignore_device" is given - increase kernel uevent buffer size - move udev(8) manpage to udev(7) - recreate man pages from xml source - remove udev, udevstart, udevsend from the default installation - update SUSE rules - rename apply_format() cause it is public now - udevtest: add udev_rules_apply_format() to RUN keys - let "ignore_device" always return the event successfully - -Olivier Blin: - fixes udev build with -fpie - - -Summary of changes from v080 to v081 -============================================ - -Kay Sievers: - add DEVLINKS to "remove" event - better log text and comments - vol_id: probe volume as user nobody - fix BUS, ID, $id usage - prepare moving of /sys/class devices to /sys/devices - - -Summary of changes from v079 to v080 -============================================ - -Brent Cook: - fix dependency for make -j2 - -coly: - fix man page typos - -Kay Sievers: - update RELEASE-NOTES + TODO - fix typo in man page - update TODO - update SUSE rules - path_id: fix invalid character class - replace libsysfs - -Marco d'Itri: - udev_selinux.c: include udev.h - - -Summary of changes from v078 to v079 -============================================ - -Kay Sievers: - don't log error if database does not exist - use udev_root instead of "/dev"in selinux matchpathcon_init_prefix() - scsi_id: read page 0x80 with libata drives - update SUSE rules - remove %e from man page - - -Summary of changes from v077 to v078 -============================================ - -Greg Kroah-Hartman: - Update Gentoo udev main rule file. - add parisc support to path_id - -Hannes Reinecke: - scsi_id: -u fold multiple consecutive whitespace chars into single '_' - -Harald Hoyer: - optimize SELinux path match - -Kay Sievers: - update README - allow C99 statements - fix segfaulting create_floppy_devices - update SUSE rules - remove unused variables - remove default settings in udev.conf - clearenv() is now part of klibc - add DEVLINKS to the event environment - -Kurt Garloff: - scsi_id: support pre-SPC3 page 83 format - - -Summary of changes from v076 to v077 -============================================ - -Kay Sievers: - merge two consecutive static strlcat's - don't return an error, if "ignore_device" is used - remove outdated and misleading stuff - move SEQNUM event skipping to udevsend - update RELEASE-NOTES - update SUSE rules - allow programs in /lib/udev called without the path - update SUSE rules - add target to to generate ChangeLog section - update Red Hat rules - -Marco d'Itri: - allow to overwrite the configured udev_root by exporting UDEV_ROOT - let udevsend ignore events with SEQNUM set - update Debian rules - - -Summary of changes from v75 to v076 -============================================ - -Kay Sievers: - fix typo in eventrecorder - volume_id: include stddef.h header - remove misleading install instructions - remove all built-in wait_for_sysfs logic - add linux/types.h back, old glibc-kernel-headers want it - volume_id: use glibc's byteswap - udevd: ignore all messages without DEVPATH - udevd: track exit status of event process - udevd: export event queue and event state - remove "udev_db" option from config file - Makefile: remove exec_prefix and srcdir - update README and RELEASE-NOTES - udevd: track killed event processes as failed - update README - don't start udevd from udevsend - udevd: add a missing return - libvolume_id: fix weird fat volume recognition - move some helpers from extras to /lib/udev - -Scott James Remnant: - move delete_path() to utils - clean-up empty queue directories - Makefile: fail, if submake fails - - -Summary of changes from v74 to v075 -============================================ - -Greg Kroah-Hartman: - Make run_directory.c stat the place it is going to try to run. - -Kay Sievers: - forgot the ChangeLog for 074 - volume_id: provide libvolume_id.a file - remove our own copy of klibc - remove outdated HOWTO - update TODO - update SUSE rules - remove completely useless start script - fix tests and remove no longer useful stuff - replace udeveventrecorder by a shell script - - -Summary of changes from v73 to v074 -============================================ - -Kay Sievers: - never queue events with TIMEOUT set - let NAME="" supress node creation, but do RUN keys - remove udevinitsend - update .gitignore - -Marco d'Itri: - add strerror() to error logs - move some logging from dbg() to info() - - -Summary of changes from v72 to v073 -============================================ - -Kay Sievers: - udevd: depend on netlink and remove all sequence reorder logic - print useconds in udevmonitor - add RELEASE-NOTES, update TODO - - -Summary of changes from v71 to v072 -============================================ - -Ananth N Mavinakayanahalli: - libsysfs: translate devpath of the symlinked class devices to its real path - -Jan Luebbe: - add man pages for *_id programs - -Kay Sievers: - volume_id: add OCFS Version 1 - volume_id: add Veritas fs - volume_id: check ext fs for valid blocksize, cause magic is only 2 bytes - volume_id: move blocksize validation to fix jbd recognition - volume_id: fix typo in ocfs - volume_id: add vxfs include - volume_id: make FAT32 recognition more robust - volume_id: Version 051 - volume_id: fix typo in ext blocksize check - volume_id: Version 052 - FAQ: remove confusing statement about module loading - cleanup compiler/linker flags - use DESTDIR on uninstall, no need to pass prefix to submake - allow to pass STRIPCMD, to skip stripping of binaries - cleanup make release - fix the new warnings I asked for - move rules parsing into daemon - "make STRIPCMD=" will disable the stripping of binaries - remove no longer working udevd-test program - "STRIPCMD=" for the EXTRAS - add dummy inotify syscalls on unsupported architecture - remove no longer needed waiting for "dev" file - revert the "read symlink as device patch" - use libsysfs to translate the class linke to the device path - libsysfs: remove brute-force "bus", "driver" searching for old kernels - test: add "driver" and "bus" links to test sysfs tree - update RELEASE-NOTES - udevd: don't daemonize before initialization - log to console if syslog is not available - udevd: disable OOM - remove precompiled rules option - export DEVNAME on "remove" only if we really got a node to remove - fix typo in umask() - - -Summary of changes from v70 to v071 -============================================ - -Greg Kroah-Hartman: - Remove the udev.spec file as no one uses it anymore - -John Hull: - edd_id: check that EDD id is unique - -Kay Sievers: - ata_id: open volume O_NONBLOCK - add "Persistent Device Naming" rules file for disks - scsi_id: switch temporary node creation to /dev - volume_id: set reiser instead of reiserfs for filesystem type - update devfs rules header - update Debian rules - update Fedora rules - update Debian rules - remove no longer needed includes - switch tools and volume_id from LGPL to GPLv2 - add edd-*-part%n to the persistent.rules - update Debian persistent rules - clarify README - udevd: fix initial timeout handling - force event socket buffer size to 16MB - udevd: move logging from err to info for non-hotplug uevent - fix selinux compilation - libsysfs: accept sysmlinks to directories instead of real directories - -Marco d'Itri: - run_directory: fix typo in "make install" - - -Summary of changes from v069 to v070 -============================================ - -Amir Shalem: - udevd: fix udevd read() calls to leave room for null byte - -Edward Goggin: - scsi_id: derive a UID for a SCSI-2 not compliant with the page 83 - -Greg Kroah-Hartman: - fix nbd error messages with a gentoo rule hack - fix scsi_id rule in gentoo config file - -Jürg Billeter: - EXTRAS/Makefile: fix install targets to match main Makefile - -Kay Sievers: - volume_id: fix error handling with failing read() - EXTRAS: cleanup and sync all Makefiles - add install test to 'make buildtest' - update RELEASE-NOTES - -Olivier Blin: - fix a debug text typo in udev_rules.c - - -Summary of changes from v068 to v069 -============================================ - -Amir Shalem: - fix typo in firmware_helper - -Duncan Sands: - firmware_helper: fix write count - -Kay Sievers: - *_id: fix zero length in set_str() - add program name to logged error - fix exit code of udevinitsend and udevmonitor - udevd: keep the right order for messages without SEQNUM - volume_id: don't probe for mac_partition_maps - udevmonitor: cleanup on exit - path_id: remove SUSE specific PATH - update SUSE rules - add pci_express to bus list - update SUSE rules - store ENV{key}="value" exported keys in the database - fix lookup for name in the udevdb, it should return the devpath - prepare for new HAL udevdb dump - print persistent data with "udevinfo -q all" - change parameter order of udev_db_search_name() - add and use name_list_cleanup() for cleaning up the string lists - don't store devpath in udevdb, we don't need it - add uft8 validation for safe volume label exporting - start to enforce plain ascii or valid utf8 - use WRITE_END/READ_END for the pipe index - remove not needed sig_flag for state of signal_pipe - don't reenter get_udevd_msg() if message is ignored - rename ...trailing_char() to ...trailing_chars() - vol_id: ID_LABEL_SAFE will no longer contain fancy characters - udevd: move some logging to "info" and "err" - remove special TIMEOUT handling from incoming queue - udev_test.pl: we replace untrusted chars with '_' - check the udevdb before assigning a new %e - update RELEASE-NOTES - udevinfo: add database export - write man page masters in DocBook XML - udevinfo: rename dump() to export() - test the automatic man page rebuild and checkin - Makefile: remove all the duplicated rules - all man pages rewritten to use DocBook XML - add missing udevsend man page - also forgot udevmonitor.8 - udevinfo: restore -d option - scsi_id: rename SYSFS to LIBSYSFS - add edd_id tool to match BIOS EDD disk information - move and update libsysfs.txt - klibc: update to version 1.1.1 - delete cdromsymlinks* - obsoleted by cdrom_id and IMPORT rules - delete docs/persistent_naming - obsoleted by persistent disk names - delete old Fedora html page - add "totally outdated" header to docs/overview :) - update SUSE rules - fix useless but funny name_cdrom.pl script to work again - update TODO - Makefile: fix prerequisits for $(PROGRAMS) - Makefile: cleanup install targets - remove chassis_id program - fic gcov use and move it into the Makefile - FAQ: update things that have changed - -Thierry Vignaud: - switch to '==' in raid-devfs.sh - - -Summary of changes from v067 to v068 -============================================ - -Greg Kroah-Hartman: - add EXTRAS documentation to the README file. - Always open the cdrom drive in non-blocking mode in cdrom_id - cdrom_id: change err() to info() to help with debugging problems - -Kay Sievers: - cleanup some debug output and move to info level + unify select() loops - move udevmonitor to /usr/sbin - ENV{TEST}=="1" compares and ENV{TEST}="1" sets the environment - vol_id: fix sloppy error handling - fix typo in cdrom_id syslog - bring std(in|out|err) fd's in a sane state - fix printed udevmonitor header - - -Summary of changes from v066 to v067 -============================================ - -Greg Kroah-Hartman: - added the cdrom.h #defines directly into the cdrom_id.c file - -Kay Sievers: - update SUSE rules - fix make install, as we don't provide a default rule set anymore - fix more compiler warnings ... - fix udevstart event ordering, we want /dev/null very early - don't fail too bad, if /dev/null does not exist - - -Summary of changes from v065 to v066 -============================================ - -Greg Kroah-Hartman: - update gentoo rule file. - Created cdrom_id program to make it easier to determine cdrom types - added cdrom_id to the build check - updated gentoo rule file to handle removable ide devices. - changed cdrom_id exports to be easier to understand and consistant with other _id programs. - fix klibc build issue in cdrom_id.c - Change the gentoo rules to use cdrom_id instead of cdsymlink.sh - changed location of gentoo helper apps to be /sbin instead of in scripts dir - tweak the gentoo rules some more. - -Kay Sievers: - add NETLINK define for the lazy distros - read sysfs attribute also from parent class device - switch some strlcpy's to memcpy - allow clean shutdown of udevd - add flag for reading of precompiled rules - update distro rules files - add SUSE rules - update SUSE rules - add firmware_helper to load firmware - more distro rules updates - update README - remove example rules and put the dev.d stuff into the run_directory folder - trivial text cleanups - update SUSE rules - split udev_util in several files - update SUSE rules - allow logging of all output from executed tools - add Usage: to udevmonitor and udevcontrol - move some logging to the info level - -Thierry Vignaud: - fix udevinfo output - - -Summary of changes from v064 to v065 -============================================ - -Greg Kroah-Hartman: - Added persistent name rules for block devices to gentoo rule file. - Added horrible (but fun) path_id script to extras. - Update gentoo rules file. - -Kay Sievers: - update release notes for next version - add udevmonitor, to debug netlink+udev events at the same time - allow RUN to send the environment to a local socket - fix GGC signed pointer warnings and switch volume_id to stdint - - -Summary of changes from v063 to v064 -============================================ - -Andre Masella: - volume_id: add OCFS (Oracle Cluster File System) support - -Hannes Reinecke: - usb_id: fix typo - add ID_BUS to *_id programs - create_floppy_devices: add tool to create floppy nodes based on sysfs info - -Kay Sievers: - move code to its own files - make SYSFS{} usable for all devices - add padding to rules structure - allow rules to have labels and skip to next label - thread unknown ENV{key} match as empty value - - -Summary of changes from v062 to v063 -============================================ - -Anton Farygin: - fix typo in GROUP value application - -Greg Kroah-Hartman: - add 'make tests' as I'm always typing that one wrong... - Really commit the udev_run_devd changes... - Fixed udev_run_devd to run the /etc/dev.d/DEVNAME/ files too - fix position of raw rules in gentoo config file - -Hannes Reinecke: - dasd_id: add s390 disk-label prober - fix usb_id and let scsi_id ignore "illegal request" - -Kay Sievers: - volume_id: remove s390 dasd handling, it is dasd_id now - trivial fixes for *_id programs - IMPORT: add {parent} to import the persistent data of the parent device - allow multiple values to be matched with KEY=="value1|value2" - udevd: set incoming socket buffer SO_RCVBUF to maximum - remember mapped rules state - ata_id: check for empty serial number - compile dasd only on s390 - -Ville Skyttä: - correct default mode documentation in udev - - -Summary of changes from v061 to v062 -============================================ - -Kay Sievers: - fix symlink values separated by multiple spaces - update RELEASE-NOTES - fix typo in group assignment - fix default-name handling and NAME="" rules - add WAIT_FOR_SYSFS key to loop until a file in sysfs arrives - fix unquoted strings in udevinitsend - -Summary of changes from v060 to v061 -============================================ - -Greg Kroah-Hartman: - Sync up the Debian rules files - fix cdrom symlink problem in gentoo rules - Fix ChangeLog titles - -Kay Sievers: - update RELEASE-NOTES - we want to provide OPTFLAGS - rename ALARM_TIMEOUT to UDEV_ALARM_TIMEOUT - udevd: optimize env-key parsing - don't resolve OWNER, GROUP on precompile if string contains %, $ - set default device node to /dev - create udevdb files only if somehting interesting happened - pack parsed rules list - replace useless defines by inline text - move rule matches to function - add usb_id program to generate usb-storage device identifiers - add IEEE1394 rules to the gentoo rule file - fake also kernel-name if we renamed a netif - allow OPTIONS to be recognized for /sys/modules /sys/devices events - switch gentoo rules to new operators - - -Summary of changes from v059 to v060 -============================================ - -Greg Kroah-Hartman: - Fix the gentoo udev rules to allow the box to boot properly - -Gustavo Zacarias: - Udev doesn't properly build with $CROSS - -Kay Sievers: - Keep udevstart from skipping devices without a 'dev' file - -Marco d'Itri: - #define NETLINK_KOBJECT_UEVENT - - -Summary of changes from v058 to v059 -============================================ - -Greg Kroah-Hartman: - Update the gentoo rule file - Fix udevinfo for empty sysfs directories - Fix makefile to allow 'make release' to work with git - -Hannes Reinecke: - udev: fix netdev RUN handling - udevcontrol: fix exit code - -Kay Sievers: - prepare RELEASE-NOTES - add ID_TYPE to the id probers - add -x to scsi_id to export the queried values in env format - store the imported device information in the udevdb - rename udev_volume_id to vol_id and add --export option - add ata_id to read serial numbers from ATA drives - IMPORT allow to import program returned keys into the env - unify execute_command() and execute_program() - IMPORT= allow to import a shell-var style config-file - allow rules to be compiled to one binary file - fix the fix and change the file to wait for to the "bus" link - fix udevstart and let all events trvel trough udev - prepare for module loading rules and add MODALIAS key - remove device node, when type block/char has changed - Makefile: remove dev.d/ hotplug.d/ from install target - udevcontrol: add max_childs command - udevd: control log-priority of the running daemon with udevcontrol - udeveventrecorder: add small program that writes an event to disk - klibc: add missing files - udevinitsend: handle replay messages correctly - udev man page: add operators - udevd: allow starting of udevd with stopped exec-queue - klibc: version 1.0.14 - udev: handle all events - not only class and block devices - volume_id: use udev-provided log-level - udev: clear lists if a new value is assigned - udev: move dev.d/ handling to external helper - udev: allow final assignments := - udevd: improve timeout handling - Makefile: fix DESTDIR - udevd: add initsend - udevd: add udevcontrol - udevd: listen for netlink events - -Stefan Schweizer: - Dialout group fix for capi devices in the gentoo rules file - -Summary of changes from v057 to v058 -============================================ - -Daniel Drake: - o Writing udev rules docs update - -Darren Salt: - o update cdsymlinks to latest version - -Greg Kroah-Hartman: - o remove detach_state files from the sysfs test tree - o Update permissions on test scripts so they will run properly now - o hopefully fix up the symlinks in the test directory - o Removed klibc/klibc.spec as it is autogenerated - o Added symlinks thanks to Kay's script and git hacking - o add Red Hat/Fedora html documenation - o Update Red Hat default udev rules - -Kay Sievers: - o selinux: fix handling during creation of symlinks - o Fedora udev.rules update - o libsysfs: version 2.0 - o klibc: version 1.0.7 - -Masanao Igarashi: - o Fix libsysfs issue with relying on the detach_state file to be - -Summary of changes from v056 to v057 -============================================ - -: - o fix stupid all_partitions bug - -Kay Sievers: - o add test for make -j4 to build-check - o klibc: version 1.0.6 - o update Debian rules - o apply default permissions only for devices that will need it - o adapt RELEASE-NOTES - o udev_volume_id: fix endianess macros - o udev-test.pl: add test for DEVNAME export to RUN environment - o update the man page to reflect the recent changes - o export DEVNAME to RUN-key executed programs - o fix make -j4 and the local klibc-install - o update RELEASE-NOTES - o add RUN key to be able to run rule based notification - o fix udevtest to print the error if logging is disabled - o move execute_program to utils + add action to init_device - o correct correction for error path for PROGRAM execution - o correct error path for PROGRAM execution - o klibc: version 1.0.5 - o check for strlen()==0 before accessing strlen()-1 - o allow to match against empty key values - o read %s{}-sysfs values at any device in the chain - o udev_rules.c: don't change sysfs_device while walking up the device chain - o klibc: strlcpy/strlcat - don't alter destination if size == 0 - o fix klibc's broken strlcpy/strlcat - o udevinfo: print SYSFS attribute the same way we match it - o remove untrusted chars read from sysfs-values or returned by PROGRAM - o udevinfo: print errors to stderr instead of stdout - o klibc: version 1.0.4 - o support log-priority levels in udev.conf - o test-suite: remove UDEV_TEST, it's not needed anymore - o libsysfs: remove trailing slash on SYSFS_PATH override - - -Summary of changes from v055 to v056 -============================================ - -: - o fix header paths in udev_libc_wrapper.c - -Kay Sievers: - o udev-test.pl: use more common user/group names - o klibc: remove SCCS directories from the temporary klibc install - o udev-test.pl: add a test where the group cannot be found in /etc/passwd - o udev-test.pl: add check for textual uid/gid - o fix bad typo that prevents the GROUP to be applied - o udevd: don't delay events with TIMEOUT in the environment - o klibc: use klcc wrapper instead of our own Makefile - o change call_foreach_file to return a list - - -Summary of changes from v054 to v055 -============================================ - -: - o This patch causes the remove handler to check that each symlink actually points to the correct devnode and skip it if it does not. - -: - o udev selinux fix - -: - o The following patch fixes some warnings when compiling volume_id from udev with the -Wall compiler flag. Define _GNU_SOURCE for strnlen() and correct the path to logging.h - o The following patch fixes a warning when compiling chassis_id from udev with the -Wall compiler flag. There are too much conversions in the format string of sscanf(). One %d can be dropped. - -Greg Kroah-Hartman: - o fix raid rules - o added frugalware udev ruleset - o merge selinux and Kay's symlink fixes together - -Hannes Reinecke: - o volume_id: Fix label/uuid reading for reiserfs - -Kay Sievers: - o add udevstart to the RELEASE-NOTES - o volume_id: version 43 - o clarify the shortcomings of %e - o correct rule match for devices without a physical device - o remove unneeded code, libsysfs does this for us - o add final release note - o add ENV{} key to match agains environment variables - o simplify sysfs_pair handling - o add a test and simplify debug statement - o support =, ==, !=, += for the key match and assignment - o add OPTION="last_rule" to skip any later rule - o rename namedev_dev to udev_rule - o correct enum device_type - o remove udevstart on make clean - o volume_id: version 42 - o volume_id: version 41 - o remove unneeded include - o The path to dlist.h is not correct - o udevinfo -d: use '=' as separator, cause ':' may be a part of the devpath - o klibc: version 1.0.3 - o add RELEASE-NOTES file - o test suite: move "driver" link to physical device - o remove PLACE key match - o don't lookup "root" in the userdb - o fix ia64 compile - o fix segfaulting udev while DRIVER matching - o cleanup list.h - o klibc: version 0.214 - o rename device_list->list to device_list->node - o replace strncpy()/strncat() by strlcpy()/strlcat() - o split udev and udevstart - o udev_volume_id: version 39 - o rename LOG to USE_LOG in all places - o remove Makefile magic for klibc integration - o klibc_fixups: remove no longer needed stuff - o udev_volume_id: volume_id v38 - o use numeric owner/group as default values to avoid parsing userdb - o fix up segfaulting binaries with new klibc - o udevinfo -d: speed-up device dump - o klibc: version 0.211 - o klibc_fixups: remove unneeded stuff - o replace weird defines by real code - o udev-test.pl: remove useless tests - o allow unlimitied count of symlinks - o unmap db-file after use - o remove typedef for call_foreach_file() handler function - o correct udev_init_device - o rename attributes to options - o kill stupid gcc4 warning - o trivial clenaup of namedev code - o klibc: check for gcc4 - o klibc: update v0.205 - -Thierry Vignaud: - o gentoo rule update for raid devices - - -Summary of changes from v053 to v054 -============================================ - -: - o udev_volume_id: add Reiser4 support - -Kay Sievers: - o namedev: skip backslashes only if followed by newline - o wait_for_sysfs: add joydev - o udevinfo: print devpath -> node relationship for all devices - o trivial rename of some variables - o klibc v0.199 - o big libsysfs diet (pre 2.0 version) - o udev_volume_id: volume_id v35 - o add "serio" to bus list - o determine device type in udev_init_device() - o move kernel name/number evaluation into udev_init_device() - o detect NAME="" as ignore_device rule - o trivial namedev cleanup - o cleanup db functions - o clean up match_place() - o switch device type to enum - o switch major/minor to dev_t - o remove the device node only if the major/minor number matches - o libsysfs: work around a klibc bug - o introduce OPTIONS=ignore_device, ignore_remove, all_partitions" key - o namedev: execute PROGRAM only once and not possibly for every physical device - -Patrick Mansfield: - o update scsi_id to work with libsysfs changes - - -Summary of changes from v052 to v053 -============================================ - -Greg Kroah-Hartman: - o fix gentoo fb permission issue - o allow simple-build-check.sh to go faster if MAKEOPTS is set - o make the release tarballs have writable files in them - o remove gentoo permission file as it's not valid anymore - -Kay Sievers: - o fix special file mode mask for temporary device node - o udevstart: simplify "dev" file searching - o udev_volume_id: remove temporary node creation and parent handling - o add %P modifier to query the node name of the parent device - o udev_volume_id: remove __packed__ from dasd structure as it does not work - o create /block/*/range count of partitons for all_partitions - -Patrick Mansfield: - o scsi_id changes for use with udev %N and %p - - -Summary of changes from v051 to v052 -============================================ - -: - o debian: update rules files - o raid-devfs.sh: devfs names for hardware RAID controllers - o scsi_id: when udevstart is started, /tmp is not writeable - o cdsymlinks.sh: trivial fix, the variable is initialized to '', not 0 - -: - o gentoo/udev.rules: add default permissions for sound devices - -Greg Kroah-Hartman: - o fix example comment in ide-devfs.sh - o Add infiniband to gentoo rules - o Another gentoo fix, adding dvb support - o Fix gentoo bug #76056 (fb device group permissions.) - o Fix gentoo bug #81102, device nodes for the pktcdvd device - -Kay Sievers: - o provide temporary device node for callouts to access the device - o udev_volume_id: fix dasd disklabel reading with -l option - o udev_volume_id: volume_id version 034 - o udev_volume_id: rename probe_ibm into probe_dasd - o udev_volume_id: volume_id version 032 - o Makefile: add some more warnings and prepare for clean gcc4 compile - o Makefile: cleanup conditional config option sections - o fix -Wsign-compare warnings - o chassis_id: clean compilation and fix bad function parameter passing - o simple_build_check: make it possible to pass KERNEL_DIR - o selinux: cleanup udev integration - -Michael Buesch: - o trivial: remove _all_ trailing slashes with no_trailing_slash() - o trivial: fix signedness - o namdev: allow symlink-only rules to specify node permissions - o udevd: fix valgrind warning - - -Summary of changes from v050 to v051 -============================================ - -: - o This fixes a silly mistake in how udevinfo prints the major and minor numbers (right now it prints the minor next to "MAJOR" and the major next to "MINOR" ;) - -: - o I tried to compile udev 050plus with the GCC 4.0 snapshot 200412119 and got two errors about possibly uninitialized structs, so I fixed this. - -Christian Bornträger: - o udev_volume_id: fix -d option - -Greg Kroah-Hartman: - o gentoo fb permission fix - o fix gcc 2.96 issue in libsysfs - o remove the lfs startup script on request of the author - o clean up the aoe char device rules, and delete the block one as it's not needed - o add aoe block and char device rules to the gentoo rule file - o fix udev_volume_id build error - -Hannes Reinecke: - o rearrange link order in Makefile - -Kay Sievers: - o udev_volume_id: new version of volume_id - o klibc: update to version 0.198 - o udev_volume_id: fix FAT label reading - o klibc: update to version 0.196 - o udevd: throttle the forking of processes - o udevd: add possible initialization of expected_seqnum - o udevd: it's obviously not the brightest idea to exit a device node manager if it doesn't find /dev/null - o udevd: separate socket handling to prepare for other event sources - o udevd: support -d switch to become a daemon - o udev_volume_id: version 27 - o udevd: split up message receiving an queueing - o remove useless warning if udev.conf contains keys not read by udev itself - o improve event sequence serialization - o remove udevsend syslog noise on udevd startup - o limit the initial timeout of the udevd event handling - o correct detection of hotplug.d/ udevsend loop - o correct log statement - o remove default_* permissions from udev.conf file - o update Fedora config files and add some more tests - o allow permissions only rules - o add SUBSYSTEM rule to catch all block devices and apply the disk permissions - o update Fedora config files - o handle renamed network interfaces properly if we manage hotplug.d/ - o allow multiline rules by backslash at the end of the line - o add OnStream tape drive rules - o simplify rules file by setting default mode to 0660 - o simplify permission application - o I broke the extras/ again. Add simple build test script now - o Merge vrfy.org:/home/kay/src/udev into vrfy.org:/home/kay/src/udev.kay - o initial merge of fedora udev.permissions into udev.rules - o remove permissions file mentioning from the udev man page - o fix some typos in gentoo's udev.rules introduced by the merge - -Michael Buesch: - o The attached patch fixes the code path if namedev_name_device() fails - -Summary of changes from v049 to v050 -============================================ - -: - o selinux patch - -: - o I made some more changes to the manpage of udev including - -Kay Sievers: - o update libsysfs to CVS version and fix segfaulting attribute reading - o klibc supports LOG_PID now, so remove our own implementation - o avoid building klibc test programs and pass SUBDIRS= to klibc clean - - -Summary of changes from v048 to v049 -============================================ - -Greg Kroah-Hartman: - o fix 'make clean' error in klibc - -Kay Sievers: - o update klibc to 0.194 - o export DEVNAME regardless of the state of udev_dev_d - o add class specific files for class/spi_transport and class/spi_host - o udevd-test.pl: remove wrong date calculation - o check earlier if we should run as udevstart - o remove double initialization - o include missing header to udevtest.c - o add -V option to udev to print the version number - o prevent udev node creatinon for "class" registration - o udevd: serialization of the event sequence of a chain of devices - o add a class/fc_host file to the list of what to wait for - o udev_volume_id: links sysfs.a instead of all objects - -Martin Schlemmer: - o remove leftover from udevinfo's -d option - - -Summary of changes from v047 to v048 -============================================ - -Greg Kroah-Hartman: - o fix udev_volume_id so it will now build properly - o fix scsi_id build errors due to changes in the main udev makefile - - -Summary of changes from v046 to v047 -============================================ - -: - o Various typos and other litte errors in udev.8.in - -: - o DEVNAME on device removal - -: - o Allow GROUP to have modifiers in it - -Greg Kroah-Hartman: - o add more debian rules files - o move distro specific config files into their own directories - o update debian rules files - o added asterix rules to the gentoo file - o use udevstart for udev.init.* files - o delete a bunch of files no longer needed - o fix gentoo scsi cdrom rule - o Fix the multithreaded build again - o merge - o comment out ability to run udev-test.pl with valgrind - o fix spurious valgrind warning in udev - o fix udevinfo '-q path' option as it was not working - o merge - o fix parallel build error - -Kay Sievers: - o update Fedora dev.d/ example and remove unused conf.d/ directory - o don't install distribution specific init script on "make install" - o restore OWNER/GROUP assignment in rule coming from RESULT - o make gcov compile scripts working with recent gcc - o fix udev-test/udev-test.pl to work with again - o add net/atml and class/ppdev to the wait_for_sysfs exception list - o add net/nlv* devices to the exception list - o add "pcmcia" and "fc_transport" to the wait_for_sysfs lists - o remove unused timestamp field - o simplify permission handling - o handle /etc/hotplug.d/ only if the event comes from udevd - o trivial cleanups and change some comments - o remove unused variables - o udevsend/udevd handle events without a subsystem - o use blacklist on device "remove" and remove dev.d/ call code duplication - o update the man pages and correct Usage: hints - o don't call the hotplug scripts with a test run - o don't call dev.d/ scripts twice, if directory = subsystem - o remove archive file if we changed something - o link archive insted of objects - o rename udev_lib to udev_utils and dev_d to udev_multiplex - o handle whole hotplug event with udevd/udev - o integrate wait_for_sysfs in udev - o make the searched multiplex directories conditionally - o add MANAGED_EVENT to the forked udev environment - o export DEVNAME on remove event - o export udev_log flag to the environment - o remove my test code - o add support for /devices-devices without any file to wait for - o Patch from Alex Riesen - o add a bunch of busses to the list of what to wait for - o close connection to syslog in forked udevd child - o udevd exit path cleanup - o fix network device naming bug - - -Summary of changes from v045 to v046 -============================================ - -Greg Kroah-Hartman: - o make spotless for releases - -Kay Sievers: - o Don't try to print major/minor for devices without a dev file - o remove get_device_type and merge that into udev_set_values() - o prevent udevd crash if DEVPATH is not set - o add ippp and bcrypt to the exception lists of wait_for_sysfs - o let klibc add the trailing newline to syslog conditionally - o disable logging for udevstart - o add NAME{ignore_remove} attribute - o remove historical SYSFS_attr="value" format - o don't wait for sysfs if the kernel(2.6.10-rc2) tells us what not to expect - o change key names in udevinfo sysfs walk to match the kernel - o support DRIVER as a rule key - o support SUBSYSTEM as a rule key - o rename udevdb* to udev_db* - o Make dev.d/ handling a separate processing stage - o make the udev object available to more processing stages - o remove udev_lib dependency from udevsend, which makes it smaller - o add ACTION to udev object to expose it to the whole process - o make udevinfo's -r option also workimg for symlink queries - o let udev act as udevstart if argv[1] == "udevstart" - o improve udevinfo sysfs info walk - o add sysfs info walk to udevinfo - o pass the whole event environment to udevd - o replace tdb database by simple lockless file database - - -Summary of changes from v044 to v045 -============================================ - -Martin Schlemmer: - o Some updates for Gentoo's udev rules - - -Summary of changes from v043 to v044 -============================================ - -Greg Kroah-Hartman: - o add cdsymlinks.sh support to gentoo rules file - o fix gentoo legacy tty rule - o remove 'sudo' usage from the Makefile - o make udev-test.pl test for root permissions before running - -Kay Sievers: - o reduce syslog noise of udevsend if multiple instances try to start udevd - o add i2c-dev to the list of devices without a bus - - -Summary of changes from v042 to v043 -============================================ - -Greg Kroah-Hartman: - o add test target to makefile - o add dumb script to show all sysfs devices in the system - -Kay Sievers: - o Shut up wait_for_sysfs class/net failure messages, as it's not possible to - get that right for all net devices. Kernels later than 2.6.10-rc1 will - handle that by carrying the neccessary information in the hotplug event. - o wait() for specific pid to return from fork() - o Don't use any syslog() in signal handler, cause it may deadlock - o Add support for highpoint ataraid to volume_id to suppress label reading on raid set members. - o Add a bunch of devices without "device" symlinks - o Exit, if udevtest cannot open the device (segfault) - o Patches from Harald Hoyer - o Apply the default permissions even if we found a entry in the permissions - file. Correct one test, as the default is applied correctly now and the - mode will no longer be 0000. - o add test for format chars in multiple symlinks to replace - o Add net/vmnet and class/zaptel to the list of devices without physical device - - -Summary of changes from v040 to v042 -============================================ - -Greg Kroah-Hartman: - o add inotify to the rules for gentoo - -Kay Sievers: - o skip waiting for device if we get a bad event for class creation and not for a device underneath it - o add net/pan and net/bnep handling - o switch wait for bus_file to stat() instead of open() add net/tun device handling add ieee1394 device handling - o Remove the last klibc specific line from the main udev code Move _KLIBC_HAS_ARCH_SIG_ATOMIC_T to the fixup file which is automatically included by the Makefile is we build with klibc - o ignore *.rej files from failed patches - o update to libsysfs 1.2.0 and add some stuff klib_fixup Now we have only the sysfs.h file different from the upstream version to map our dbg() macro. - o improve klibc fixup integration - o cleanup udevd/udevstart - o expose sysfs functions for sharing it - - -Summary of changes from v039 to v040 -============================================ - -: - o wait_for_sysfs update for dm devices - -Greg Kroah-Hartman: - o sparse cleanups on the tree - o fix stupid cut-and-paste error for msr devices on gentoo boxes - o add *~ to bk ignore list - o delete udevruler.c as per Kay's request - o fix up the wait_for_sysfs_test script a bit - -Kay Sievers: - o fix debug in volume id / fix clashing global var name - o volume_id fix - o $local user - o cleanup netif handling and netif-dev.d/ events - o big cleanup of internal udev api - o don't wait for dummy devices - o close the syslog - o Fix ppp net devices in wait_for_sysfs - o Fix wait_for_sysfs messages (more debugging info) - - -Summary of changes from v038 to v039 -============================================ - -Greg Kroah-Hartman: - o Hopefully fix the vcs issue in wait_for_sysfs - o take out & from wait_for_sysfs_test that I previously missed - o add very nice cdsymlinks scripts - o add some helper scripts for dvb and input devices - o add debian config files - o let the extras/ programs build "pretty" also - o tweak the ccdv program to handle files in subdirectories being built - o crap, I messed up the 'sed' instances pretty badly, this fixes the config and man page mess - o fix broken 'make -j5' functionality - -Kay Sievers: - o swich attribute open() to simple stat() - o wait_for_sysfs update for /class/firmware and /class/net/irda devices - o fix unusual sysfs behavior for pcmcia_socket - o remove sleeps from udev as it is external now - o delete udevruler? - o Makefile fix - -Patrick Mansfield: - o update udev to scsi_id 0.7 - o pass SYSFS setting down for extras builds - o move assignments past local variables - - -Summary of changes from v037 to v038 -============================================ - -: - o Re: Problem parsing %s in udev rules - -Greg Kroah-Hartman: - o fix up error in building extras and libsysfs - -Summary of changes from v036 to v037 -============================================ - -: - o small udev patch - -Greg Kroah-Hartman: - o fix compilation warning in tdb log message - o Fix build error with klibc due to recent changes - o merge - o add wait_for_sysfs test script to the tarball to help people debug their boxes - o add ipsec to wait_for_sysfs ignore list - o added ccdv to bk ignore list - o a few more Makefile tweaks for the quiet feature - o Make the build silent, thanks to a helper program from ncftp - o rename files to have '_' instead of '-' in them - o change max time to wait in wait_for_sysfs to 10 seconds to hopefully handle some slow machines - o add support for class/raw/ to wait_for_sysfs - o fix up Makefile for wait_for_sysfs udev_version.h dependancy - o remove the debian specific file, as they don't want to share with the rest of the world :( - -Kay Sievers: - o prevent deadlocks on an corrupt udev database - o wait_for_sysfs_update - -Michael Buesch: - o fix asmlinkage - o fix incompatible pointer type warning - - -Summary of changes from v035 to v036 -============================================ - -Greg Kroah-Hartman: - o add the error number to the error message in wait_for_sysfs to help out in debugging problems - -Summary of changes from v034 to v035 -============================================ - -Greg Kroah-Hartman: - o added ieee1394 support to wait_for_sysfs - o update wait_for_sysfs with a bunch more devices thanks to user reports - -Summary of changes from v033 to v034 -============================================ - -Kay Sievers: - o wait_for_sysfs bluetooth class update - -Greg Kroah-Hartman: - o add comment in wait_for_sysfs to explain the structure better - o Revert previous dev_d.c change, it's not what is causing HAL problems - o hm, somethings odd with DEVPATH, see if this fixes it - o 33_bk mark for the makefile - o wait_for_sysfs: clean up the logic for the list of devices that we do not expect device symlinks for - o get rid of annoying extra lines in the syslog for some libsysfs debug messages - o added support for i2c devices in wait_for_sysfs.c - o add support for i2c-adapter devices to wait_for_sysfs.c - -Summary of changes from v032 to v033 -============================================ - -: - o udev close on exec - o some cleanups and security fixes - o some cleanups and security fixes - o selinux for udev - o cleanup PATCH for extras/chassis_id/Makefile - -: - o respect prefix= setting in built udev.conf (updated) - -Greg Kroah-Hartman: - o add support for usb interfaces to wait_for_sysfs to keep it quiet - o enable native tdb spinlocks on i386 platforms - o delete extras/multipath-tools as per the author's request - o be paranoid in dev_d.c - o add USE_SELINUX to README documentation so people have a chance to see what is going on - o update the selinux.h file to start to look sane - o update bk ignore list for the wait_for_sysfs binary - o kdetv wants to see device nodes in /dev - o update comments in scsi-devfs.sh - o fix up Makefiles to get the klibc build working properly - o update bk ignore list for new klibc generated files - o oops forgot to add the new klibc/include directory - o update klibc to version 0.181 - -Kay Sievers: - o fix problems with dev.d and udevstart - o wait_for_sysfs debug cleanup - o fix problems using scsi_id with udevstart - o update volume_id - o finally solve the bad sysfs-timing for all of us - o volume-id build fix and update - o switch udev's seqnum to u64 - o add enum tests - o fix udev segfaults with bad permissions file - -Patrick Mansfield: - o update udev to include scsi_id 0.6 - - -Summary of changes from v031 to v032 -============================================ - -: - o udev parse bug - -Kay Sievers: - o handle only block and class devices - o fix udevstart badly broken in udev 031 - - -Summary of changes from v030 to v031 -============================================ - -: - o udev - read long lines from config files overflow fix - -: - o Update the FAQ with info about hardlink security - -: - o compatibility symlinks for udev - -David Weinehall: - o Minor POSIX-fixes for udev - -Greg Kroah-Hartman: - o add symlink for video rule - o add a "first" list to udevstart and make it contain the class/mem/ devices - o fix compiler warning in udevtest.c - o Fix old-style pty breakage in rules file for tty device - o add rules for i386 cpu devices - o add permission for legotower usb devices - -Kay Sievers: - o Fix naming ethernet devices in udevstart - o update udev_volume_id - o let /sbin/hotplug execute udev earlier - o pass SEQNUM trough udevd - o fix manpages based on esr's spambot - -Martin Schlemmer: - o add microcode rule to permissions.gentoo file - -Michael Buesch: - o Try to provide a bit of security for hardlinks to /dev entries - -Olaf Hering: - o udevsend depends on udev_lib.o - -Tom Rini: - o fix UDEV_NO_SLEEP - o clean up start_udev a bit - o Make udev/udevstart be one binary - o Add 'asmlinkage' to udev-030 - - -Summary of changes from v029 to v030 -============================================ - -Greg Kroah-Hartman: - o fix stupid off-by-one bug that caused udevstart to die on x86-64 boxes - - -Summary of changes from v028 to v029 -============================================ - -Greg Kroah-Hartman: - o add permission rule for jogdial device - o fix dumb bug I added to udevstart - o make a "last list" of devices for udevstart to operate on last - o fix permission problem with input event and ts nodes for gentoo - o change default perms of misc/rtc to be readable by anyone - -Olaf Hering: - o allow NAME_SIZE > SYSFS_PATH_MAX - - -Summary of changes from v027 to v028 -============================================ - -: - o Patch for chassis_id exras module - -Daniel Drake: - o Writing udev rules doc update - -Greg Kroah-Hartman: - o clean up block whitelist search logic a bit - o reverse order of scanning of udevstart to look at class before block - -Kay Sievers: - o update udev_volume_id - -Leann Ogasawara: - o udevstart performance increase - -Patrick Mansfield: - o update udev scsi_id to scsi_id 0.5 - - -Summary of changes from v026 to v027 -============================================ - -: - o fix handle leak in udev_lib.c - -Greg Kroah-Hartman: - o tweak the gentoo default permission rules as they are wrong for tty and misc devices - - -Summary of changes from v025 to v026 -============================================ - -Arnd Bergmann: - o udev rpm fix - -Greg Kroah-Hartman: - o add test for ! in partition name - o 025_bk mark - o Update to version 117 of klibc (from version 108) - o add volume_id ignore rule for bk - o add volume_id support to the udev.spec file - o remove dbus and selinux stuff from the udev.spec file - o delete udev_selinux as it doesn't work properly and is the wrong way to do it - o Deleted the udev_dbus extra as it didn't really work properly and HAL has a real solution now - o add udev.permissions.slackware file - o udevstart: close open directories - -Kay Sievers: - o fix udevd zombies - o catchup with recent klibc - o Re: udevsend fallback - o udev_volume_id update - o udev callout for reading filesystem labels - o udev callout for reading filesystem labels - o udev default config layout changes - -Leann Ogasawara: - o evaluate getenv() return value for udev_config.c - -Summary of changes from v024 to v025 -============================================ - -: - o devfs.sh-ide-floppy - -: - o DEVNODE -> DEVNAME transition fixes - -Daniel Drake: - o Update writing udev rules docs - -Greg Kroah-Hartman: - o make dev.d call each directory in the directory chain of the device name, instead of just the whole name - o add devd_test script - o add more permissions based on SuSE's recommendations - o added rules for tun and raw devices - o add udev conf.d file - o Switch the default config to point to a directory for the rules and permission files - o update the Red Hat .dev files to work on other distros - o add dbus.dev, pam_console.dev and selinux.dev files for /etc/dev.d/default/ usage - o add hints for red hat users from Leann Ogasawara - o add scripts to run gcov for udev from Leann Ogasawara - o change permissions on udevd test scripts - o Fix build process for users who have LC_ALL set to a non-english language - o Added expanded tests to the test framework from Leann Ogasawara - o added execelent "writing udev rules" document from Daniel Drake - o added rule to put USB printers in their proper places - o added rules for CAPI devices - o added a dev.d alsa script to help people out - -Kay Sievers: - o fix test regressions - o udev_selinux changes - o udevd test script - o udev_dbus changes - o fix devpath for netdev - -Leann Ogasawara: - o gcov for udev - - -Summary of changes from v023 to v024 -============================================ - -: - o Add README for chassis_id - o Add chassis_id program to extras directory - -: - o udevd race conditions and performance, assorted cleanups - -: - o fix SEGV in libsysfs/dlist.c - -: - o add OSDL documentation for persistent naming - -: - o small ide-devfs.sh fix - -Greg Kroah-Hartman: - o remove compiler warning from udevd.c - o only generate udev.8 on the fly, not all other man pages - o update bk ignore list some more - o update bk ignore list - o switch to generate the man pages during the normal build, not during the install - o convert udev.8.in to use @udevdir@ macro for make install - o first step of making man pages dynamically generated - o add install and uninstall the etc/dev.d/net/hotplug.dev file to the Makefile - o tweak net_test a bit - o fix some segfaults when running udevtest for network devices - o make a net_test test script using udevtest - o handle the subsytem if provided in udevtest - o add hotplug.dev script to handle renamed network devices - o add a bunch of network class devices to the test sysfs tree - o add udevruler to the bk ignore list - o update RFC-dev.d docs due to DEVNODE to DEVNAME change - o clean up chassis_id coding style - o clean up the OSDL document formatting a bit - o add netlink rules to devfs and gentoo rules files - o added USB device rules to rules files - o clean up the gentoo rules file a bit more, adding dri rules - o fix up udev.rules to handle oss rules better - o 023_bk mark - o fix udev.spec file for where udevtest should be placed - -Kay Sievers: - o tweak node unlink handling - o switch udevd's msg_dump() to #define - o handle netdev in udevruler - o man page cleanup - o put config info in db for netdev - o increase udevd event timeout - o udevstart fix - o put netdev handling and dev.d/ in manpages - o DEVPATH for netdev - o netdev - udevdb+dev.d changes - o udevd race conditions and performance, assorted cleanups - take 2 - o udevinfo patch - o dev_d.c file sorting and cleanup - o apply all_partitions rule to main block device only - - -Summary of changes from v022 to v023 -============================================ - -Kay Sievers: - o hmm, handle net devices with udev? - o correct apply_format() for symlink only rules - o don't init namedev on remove - o first stupid try for a rule compose gui - o replace fgets() with mmap() and introduce udev_lib.[hc] - o make udevtest a real program :) - -Daniel E. F. Stekloff: - o udevinfo patch - -Greg Kroah-Hartman: - o create the /etc/dev.d/ directories in 'make install' - o actually have udev run files ending in .dev in the /etc/dev.d/ directory as documented - o added RFC-dev.d document detailing how /etc/dev.d/ works - o fixed up udev.spec to handle selinux stuff properly now - o remove USE_DBUS and USE_SELINUX flags from the README as they are no longer present - o remove selinux stuff from the main Makefile - o move udev_selinux into extras/selinux - o fix dbus build in the udev.spec file - o remove dbus stuff from main Makefile - o move udev_dbus to extras/dbus - o udev_dbus can now compile properly, but linnking is another story - o remove udev_dbus.h from Makefile - o first cut at standalone udev_selinux program - o remove selinux support from udev core as it's no longer needed - o first cut at standalone udev_dbus program - o add get_devnode() helper to udev_lib for udev_dbus program - o remove dbus code from core udev code as it's no longer needed to be there - o add /etc/dev.d/ support for udev add and remove events - o fix build error in namedev.c caused by previous patch - o 022_bk tag - o fix 'make spotless' to really do that in klibc - o add a question/answer about automounting usb devices to the FAQ - o mark scsi-devfs.sh as executable - o Increase the name size as requested by Richard Gooch - o fix udevtest to build properly after the big udev_lib change - -Olaf Hering: - o uninitialized variable for mknod and friend - -Richard Gooch: - o SCSI logical and physical names for udev - -Theodore Y. T'so: - o Trivial man page typo fixes to udev - - -Summary of changes from v021 to v022 -============================================ - -: - o more Libsysfs updates - o Libsysfs updates - -: - o fix HOWTO-udev_for_dev for udevdir - -Kay Sievers: - o udev-test.pl cleanup - o add dev node test to udev-test.pl - o add permission tests - o "symlink only" test - o callout part selector tweak - o cleanup callout fork - o allow to specify node permissions in the rule - o man page beauty - o put symlink only rules to the man page - o rename strn*() macros to strmax - o conditional remove of trailing sysfs whitespace - o clarify udevinfo text - o better fix for NAME="foo-%c{N}" gets a truncated name - o overall trivial trivial cleanup - o fix NAME="foo-%c{N}" gets a truncated name - o cleanup mult field string handling - -: - o fix a type in docs/libsysfs.txt - o Added line to udev.permissions.redhat - o Include more examples in the docs area for gentoo and redhat - -: - o udevstart fixes - -Greg Kroah-Hartman: - o add big major tests to udev-test.pl - o add a test for a minor over 255 - o udev-test.pl: print out major:minor and perm test "ok" if is ok - o make perm and major:minor test errors be reported properly - o remove extra ; in namedev_parse.c - o Added multipath-tools 0.1.1 release - o deleted current extras/multipath directory - o 021_bk mark - o fix the build for older versions of gcc - -Hanna V. Linder: - o Small fix to remove extra "will" in man page - -Olaf Hering: - o make spotless - o udev* segfaults with new klibc - -Patrick Mansfield: - o add tests for NAME="foo-%c{N}" - -Summary of changes from v020 to v021 -============================================ - -Kay Sievers: - o install udevinfo in /usr/bin - o blacklist pcmcia_socket - -Greg Kroah-Hartman: - o fix udev.spec to find udevinfo now that it has moved to /usr/bin - o Fix another problem with Makefile installing initscript - o fix the Makefile to install the init script into the proper directory - o make spec file turn off selinux support by default - - -Summary of changes from v019 to v020 -============================================ - -: - o multipath update - -Kay Sievers: - o man page udevstart - o cleanup udevstart - o bugfix for local user - o unlink bugfix - o TODO update - o clarify udevinfo device walk - o udevinfo symlink reverse query - o fix stroul endptr use - o add $local user spport for permissions - o udev - man page update - o udev - fix debug info for multiple rule file config - o udev - kill udevd on install - o udev - activate formt length attribute - o udev - safer sprintf() use - -: - o no error on enoent - o escape dashes in man pages - o remove usage of expr in ide-devfs.sh - -: - o automatically install correct initscript - o update documetation for $local - -Andrey Borzenkov: - o Add symlink only rules support - -Greg Kroah-Hartman: - o update the TODO list as we already have a devfs config file - o make start_udev use udevstart binary - o install udevstart - o Remove Debian permission files as the Debian maintainer doesn't seem to want to share :( - o update the Gentoo rules files - o Add Red Hat rules and permissions files - o add udevstart to the ignore list - o add udevstart program based on a old patch from Harald Hoyer - o unlink the file before we try to create it - o Merge greg@bucket:/home/greg/src/udev into kroah.com:/home/greg/src/udev - - -Summary of changes from v018 to v019 -============================================ - -Kay Sievers: - o TODO update - o udev - correct relative symlink - o udev - safer string handling - part four - o udev - safer string handling - part three - o udev - safer string handling - part two - o udev - man page update - o udev - safer string handling all over the place - o manpage update - o udev - allow all files in a directory as the config - o udev - simple klibc textual uid/gid handling - -Andrey Borzenkov: - o do not remove real .udev.tdb during RPM build - -Greg Kroah-Hartman: - o add new TODO item about local user permissions - o Add initial SELinux support for udev - o fix build for very old versions of make - o remove limit of the number of args passed to PROGRAM - o force udev to include the internal version of libsysfs and never the external one - o fix up libsysfs header file usage to fix bug reports from users that have sysfsutils installed already - o remove udevtest on 'make clean' - o remove udevd priority TODO item, as it's not needed at all - -Patrick Mansfield: - o update udev scsi_id to scsi_id 0.4 - - -Summary of changes from v017 to v018 -============================================ - -: - o [PATCH] symlink dm-[0-9]* rule - o update extras/multipath - -: - o init.d debian patch - -Kay Sievers: - o udev - TODO update - o udev - add %s{filename} to man page - o udev - udevd/udevsend man page - o udev - switch callout part selector to {attribute} - o udev - switch SYSFS_file to SYSFS{file} - o udev - create all partitions of blockdevice - o allow SYSFS{file} - o Adding '%s' format specifier to NAME and SYMLINK - -Greg Kroah-Hartman: - o added some scsi_id files to the bk ignore file - o added scsi_id and some more documentation to the udev.spec file - o update udev.rules.gentoo with new config file format - o Update the Gentoo udev.rules and udev.permissions files - o Create a udev.rules.examples file to hold odd udev.rules - o add udevd priority issue to the TODO list - o more HOWTO cleanups - o add HOWTO detailing how to use udev to manage /dev - o mv libsysfs/libsysfs.h to libsysfs/sysfs/libsysfs.h to make it easier to use - o add start_udev init script - o add support for UDEV_NO_SLEEP env variable so Gentoo people will be happy - o start up udevd ourselves in the init script to give it some good priorities - o update the red hat init script to handle nodes that are not present - o add a "old style" SYSFS_attribute test to udev-test.pl - o Have udevsend report more info in debug mode - o Have udevd report it's version in debug mode - o fix up bug created for udevtest in previous partition creation patch - o update the udev.spec to add udevtest and make some more Red Hat suggested changes - o add ability to install udevtest to Makefile - o 017_bk mark - o Add another test to udev-test.pl and fix a bug when only running 1 test - o Fix bug where we did not use the "converted" kernel name if we had no rule - -Patrick Mansfield: - o udev use new libsysfs header file location - o udev add some ID tests - - -Summary of changes from v016 to v017 -============================================ - -: - o make logging a config option - -: - o more udev-016/extras/multipath - o more udev-016/extras/multipath - o update extras/multipath - -Kay Sievers: - o udev - keep private data out of the database? - o better credential patch - o udevd - client access authorization - o compile udevd with klibc - o udev - fix "ignore method" - o udev - fix cdrom symlink rule - o convert udevsend/udevd to DGRAM and single-threaded - o udevd - kill the lockfile - o udevd - fix socket path length - o udevd - switch socket path to abstract namespace - o udevd - allow to bypass sequence number - o include used function - -Greg Kroah-Hartman: - o add udev_log to the documentation - o fix offsetof() define in klibc - o add some .spec file changes from Red Hat - o update the init.d udev script based on a patch from Red Hat - o remove the .udev.tdb when installing or uninstalling to be safe - o remove the database at startup - o fix bug in permission handling - o update klibc to version .107 - o update the bitkeeper ignore file list - o add udevtest program to build - o fix problem where usb devices can be either the main device or the interface - o more logging.h cleanups to be a bit more flexible - o stop using mode_t as different libcs define it in different ways :( - o remove some more KLIBC fixups that are no longer needed - o let udev-test.pl run an individual test if you ask it to - o Handle the '!' character that some block devices have - o add a block device with a ! in the name, and a test for this - o fix up 'make release' to use bk to build the export tree - o fix log option code so that it actually works for all udev programs - o finish syncing up with klibc - o sync with latest version of klibc (0.107) - o fix up Makefile dependancies for udev_version.h - -Patrick Mansfield: - o udev add wild card compare for ID - o udev kill extra bus_id compares in match_id - - -Summary of changes from v015 to v016 -============================================ - -: - o get_dev_number() in extras/ide-devfs.sh - -: - o FAQ udev.rules.devfs - -Greg Kroah-Hartman: - o add udevd and udevsend to the spec file - o make /etc/hotplug.d/default/udev.hotplug symlink point to udevsend now - o add KERNEL_DIR option so that the distros will be happy - o make udevsend binary even smaller - o udevsend now almost compiles with klibc, struct sockaddr_un is only problem now - o fix up logging code so that it can be built without it being enabled - o rework the logging code so that each program logs with the proper name in the syslog - o remove logging.c as it's no longer needed - o kill the last examples that contained the %D option - o remove a __KLIBC__ tests in libsysfs, as klibc now supports getpagesize() - o udevd - remove stupid locking error I wrote - o update to klibc version 0.101, fixing the stdin bug - o fix Makefile typo for USE_LSB install - o allow dbus code to actually build again - -Kay Sievers: - o let udevsend build with klibc - o udevd - config cleanup - o udevd - cleanup and better timeout handling - o fix possible buffer overflow - o udevd - next round of fixes - o udevinfo - missing options for man page - o udev - trivial style cleanup - - -Summary of changes from v014 to v015 -============================================ - -: - o LFS init script update - -Greg Kroah-Hartman: - o update klibc to version 0.98 - o clean up udevinfo on 'make clean' - o add udevinfo man page to spec file - o remove command line documentation from udev man page - o create initial version of udevinfo man page - o added URL to spec file - o add udevinfo to udev.spec file - o add udevinfo to install target of Makefile - o rip out command line code from udev, now that we have udevinfo - o udevinfo doesn't need to declare main_envp - o move get_pair to udev_config.c because udevinfo doesn't need all of namedev.o - o more makefile cleanups - o move udevinfo into the main build and clean up the main Makefile a bit - o clean up compiler warnings if building using klibc - o make udevd only have one instance running at a time - o new testd.block script for debugging - o udevsnd : clean up message creation logic a bit - o make bk ignore udevd and udevsend binaries - o whitespace cleanups - o remove TODO item about BUS value, as it is now done - o add support for figuring out which device on the sysfs "chain" the rule applies to - -Kay Sievers: - o udevinfo - now a real program :) - o udevd - cleanup and better timeout handling - o udev - next round of udev event order daemon - o fix udevd exec - o udev - udevinfo with device chain walk - o spilt udev into pieces - - -Summary of changes from v013 to v014 -============================================ - -: - o libsysfs update for refresh + namedev.c changes - -: - o udev-013/extras/multipath update - -: - o minor patch for devfs rules - -Kay Sievers: - o udev - program to query all device attributes to build a rule - o set default owner/group in db - update - o udev - reverse user query options - o udev - kill %D from udev-test.pl - o add udev logging to info log - o udev - mention format string escape char in man page - -Greg Kroah-Hartman: - o misc code cleanups - o fixup logging.h to handle different logging options properly - o clean up the logging patch a bit to make the option more like the other options - o remove the %D modifier as it is not longer needed - o remove unneeded keyboard rule - o add usb_host and pci_bus to the class blacklist - o added input device rules to udev.rules and udev.rules.devfs - o 013_bk mark - -Hanna V. Linder: - o set default owner/group in db - o small cut n paste error fix - -Patrick Mansfield: - o update udev scsi_id to scsi_id 0.3 - - -Summary of changes from v012 to v013 -============================================ - -: - o LSB init script and other stuff - -: - o fix udev directory for Debian init script - -: - o udev 012 old gcc fixup - -Christophe Saout: - o add IGNORE rule type - o small cleanup - -Greg Kroah-Hartman: - o update TODO with some new, small items - o Cset exclude: greg@kroah.com|ChangeSet|20040113010256|48515 - o update the README in a few places - o fix -d typo in the manpage update - o Fix stupid gcc "optimization" of 1 character printk() calls.... Ick - o oops, forgot to fix up the PROGRAM result from ID to RESULT in the config files - o Add alsa device rules and a few other devfs rules - o fix a few stale comments in namedev.c - o convert the default rules files to the new format - o convert the test shell scripts to the config file format - o add bus test for usb-serial bus - o Add some helpful messages if the user uses the older config file format - o added dri rule to the default config file - o added init.d udev script for debian - o add a script that tests the IGNORE rule - o add silly script that names cdrom drives based on the cd in them - o add cdrom rule for ide cdrom - o replace list_for_each with list_for_each_entry, saving a few lines of code - o add a blacklist of class devices we do not want to look at - -Kay Sievers: - o fix klibc with printf() and gcc - o udev - small script optimization - o udev - introduce format escape char - o udev - more CALLOUT is PROGRAM now - o udev - CALLOUT is PROGRAM now - o update documentation for new config file format - o more advanced user query options - o udev - simple debug tweak - o udev - drop all methods :) - o udev - advanced user query options - o udev - Makefile error - o udev - make exec_callout() reusable - o udev - exec status fix for klibc - o fix Silly udev script - - -Summary of changes from v011 to v012 -============================================ - -: - o make symlink work properly if there is already a file in its place - o Fix udev gcc-2.95.4 compat - -: - o extras multipath update - o extras multipath update - -Kay Sievers: - o mention user callable udev + options in man page - o make udev user callable to query the database - o depend on all .h files - o cleanup namedev_parse debug text - o extend exec_program[] - o ide-devfs.sh update - o fix for apply_format() - o check for empty symlink string - o 'ide' missing in bus_files[] - o small trivial cleanup of latest changes - -: - o introduce signal handler - -: - o udev spec file update - -Greg Kroah-Hartman: - o minor grammer fixes for the udev_vs_devfs document - o move the dbus config file to etc/dbus-1/system.d/ - o move the config files to etc/udev to clean up main directory a bit - o add Gentoo versions of the rules and permissions files - o if using glibc, link dynamically, as no one like 500Kb udev binaries - o minor change to udev_vs_devfs document - o added udev vs devfs supid document to the tree - o move the signal handling registration to after we have initialized enough stuff - o make ide-devfs.sh executable in the tree - o udev.permissions.debian - forgot the dm nodes - o update the udev.permissions.debian file with new entries - o added udev.init script for the Linux From Scratch project - - - -Summary of changes from v010 to v011 -============================================ - -: - o proper cleanup on udevdb_init() failure - -: - o patch udev 009-010 rpm spec file - -: - o fix udev sed Makefile usage - -Greg Kroah-Hartman: - o add documentation about the BUS key being optional for the LABEL rule - o add tests for LABEL rule with a device that has no bus - o Don't require the BUS value for the LABEL rule - o If a LABEL rule has a BUS id, then we must check to see if the device is on a bus - o add documentation about the BUS key being optional for the CALLOUT rule - o If a CALLOUT rule has a BUS id, then we must check to see if the device is on a bus - o Don't require the BUS value for the CALLOUT rule - o add test for callout rule with a device that has no bus - o 010_bk stamp - o added different build options to the rpm udev.spec file - o add pci to the bus_files list - o check for empty line a bit better in the parser - o more init script cleanups, the stop target now calls udev to cleanup instead of just removing the whole /udev directory - o make udev init script run udev in the background to let startup go much faster - o fix long delay for all devices in namedev - - -Summary of changes from v009 to v010 -============================================ - -: - o change pgsize - -: - o extras multipath update - o extras multipath update - o extras multipath update - o extras multipath update - -Kay Sievers: - o fix udev-test.pl - o small cleanup udev-remove.c - o experimental CALLOUT script for devfs ide node creation with cd, disc, part - o add any valid device - o introduce format char 'k' for kernel-name - o trivial make fixes - o don't overwrite old config on install - o udev-remove.c cleanups - o bug in udev-remove.c - o trivial cleanup parser changes - -: - o fix comment and whitespace handling in config files - -Adam Kropelin: - o Allow build with empty EXTRAS - -Daniel E. F. Stekloff: - o libsysfs 0.4.0 patch - o fix scsi_id segfault with udev-009 - o add libsysfs docs - -David T. Hollis: - o mark config files as such in the rpm spec file - -Greg Kroah-Hartman: - o fix complier warning in namedev.c - o add documentation for the new '%k' modifier (kernel name replacement) - o add documentation about the multiple sysfs values that are now allowed for the LABEL rule - o add tests for multi-file LABEL rules - o add ability to have up to 5 SYSFS_ file/value pairs for the LABEL rule - o Just live with a sleep(1) in namedev for now until libsysfs is fixed up - o try to wait until the proper device file shows up in sysfs - o remove unneeded TODO and FIXME entry - o clean up the stand-alone tests to work properly on other people's machines - o add tests to catch whitespace and comment config file parsing errors - - -Summary of changes from v008 to v009 -============================================ - -: - o more extras/multipath changes - o and more extras/multipath updates - o more extras/multipath updates - o yet more extras/multipath - o more extras/multipath updates - o extras/multipath update - -: - o D-BUS patch for udev-008 - -: - o add init.d/udev to "make install" - o add init.d/udev to the spec file - -Kay Sievers: - o don't rely on field order in namedev_parse - o get part of callout return string - o remove '\n' from end of callout return - o man-page mention multiple symlinks - o allow multiple symlinks - o cleanup man & remove symlink comment - o experimental (very simple) SYMLINK creation - o man page beauty - o pattern match for label method - o a bug in linefeed removal - -: - o remove udev from runlevels on uninstall - o install initscript in udev rpm - -Daniel E. F. Stekloff: - o pre-libsysfs-0.4.0 patch - -Greg Kroah-Hartman: - o signal fixes due to klibc update - o sync klibc with release 0.95 - o add mol permissions to the debian permissions file - o update the FAQ with info about bad modprobe events from the devfs scheme - o some cleanups due to the need for LABEL rules to use "SYSFS_" now - o Add restart target to the etc/init.d/udev script - o tweak the config file generation portion of the Makefile a bit - o change devfs disk name rule from 'disk' to 'disc' - o add vc support to udev.rules.devfs - o added a devfs udev config file from Marco d'Itri - o set default mode to 0600 to be safer - o Makefile tweaks for the DBUS build - o update the FAQ due to the latest devfs mess on lkml and also due to symlinks now working - o document the different Makefile config options that we have - o change USE_DBUS to DBUS in Makefile, and disable it by default as it's still to hard to build on all systems - o fix formatting of udev_dbus.c to use tabs. Also get it to build properly now - o move all of the DBUS logic into one file and remove all of the #ifdef crud from the main code - -Olaf Hering: - o dump latest klibc into the udev build tree - o use udevdir in udev.conf - -Patrick Mansfield: - o better allow builds of extras programs under udev - o update udev extras/scsi_id to version 0.2 - - -Summary of changes from v007 to v008 -============================================ - -: - o more config file parsing robustness - -: - o udev-007/extras/multipath update - -Arnd Bergmann: - o Build failure - missing linux/limits.h include? - o Add format modifier for devfs like naming - o klibc makefile fixes - -Daniel E. F. Stekloff: - o another patch for path problem - o quick fix for libsysfs bus - o libsysfs changes for sysfsutils 0.3.0 - -Greg Kroah-Hartman: - o fix up some duplicated function compiler warnings in libsysfs - o fix some compiler warnings in the tdb code - o Added Kay's name to the man page - o update the wildcard documentation in the man page to show the new styles supported - o fix permission handling logic - o enable default_mode ability to actually build - o add support for the default_mode variable, as it is documented - o show permissions and groups in the label_test - o remove some items off of the TODO list, as they are now done - o fix up the tests to work without all of the environ variables - o get rid of the majority of the debug environment variables - o Update the man page to show the new config file, it's format, and how to use it - o fix up the tests to support the rules file name change - o add support for a main udev config file, udev.conf - o turn debugging messages off by default - o split out the namedev config parsing logic to namedev_parse.c - o rename namedev's get_attr() to be main namedev_name_device() as that's what it really is - o add devfs like tty rules as an example in the default config file - o operate on the rules in the order they are in the config file (within the rule type) instead of operating on them backwards. - o Cset exclude: dsteklof@us.ibm.com|ChangeSet|20031126173159|56255 - o add test for checking the BUS value - o fix problem where we were not looking at the BUS value - o add scsi and pci bus links in the test sysfs tree - o add test and documentation for new %D devfs format modifier - o changed the default location of the database to /udev/.udev.tdb to be LSB compliant - o get rid of functions in klibc_fixups that are now in klibc - o sync up with the 0.84 version of klibc - o fix udev init.d script to handle all class devices in sysfs - o fix the test.block and test.tty scripts due to their moveing. Also add a test.all script - o 007_bk version change to Makefile - -Kay Sievers: - o pattern matching for namedev - o catch replace device by wildcard - o udev.8 tweak numeric id text - o udev-test.pl add subdir test - o namedev.c strcat tweak - o overall whitespace + debug text conditioning - o udev-test.pl - tweaks - -Martin Hicks: - o Add -nodefaultlibs while compiling against klibc - -Olaf Hering: - o ARCH detection for ppc - -Patrick Mansfield: - o fix udev parallel builds with klibc - - -Summary of changes from v006 to v007 -============================================ - -: - o fix segfault in parsing bad udev.permissions file - -Greg Kroah-Hartman: - o update default config file with a CALLOUT rule, and more documentation - o updated the man page with the latest format specifier changes - o added ability to put format specifiers in the CALLOUT program string - o tweak udev-test.pl to report '0' errors if that's what happened - o only build klibc_fixups.c if we are actually using klibc - o add support for string group and string user names in udev.permissions - o add getgrnam and getpwnam to klibc_fixups files - o remove Makefile.klibc - o add udev-test perl script from Kay Sievers which blows away my puny shell scripts - o added debian's version of udev.permissions - o change to 006_bk version - -Kay Sievers: - o format char for CALLOUT output - o more namedev whitespace cleanups - o support arguments in callout exec - o namedev.c - change order of fields in CALLOUT - o namedev.c whitespace + debug text cleanup - o man page with udev.permissions wildcard - -Olaf Hering: - o static klibc udev does not link against crt0.o - -Summary of changes from v005 to v006 -============================================ - -: - o faster test scripts - -Arnd Bergmann: - o more robust config file parsing in namedev.c - o add bus id modifier - -Daniel E. F. Stekloff: - o patch for libsysfs sysfs directory handling - -Greg Kroah-Hartman: - o add another line to udev.permissions in the proper format - o tweak replace_test - o fix permissions to work properly now - o add real udev.permissions file to test directory - o fix namedev.c to build with older version of gcc - o add dumb test for all of the different modifiers - o update the TODO list with more items that people can easily do - o move the test.block and test.tty scripts to the test/ directory - o add remove actions to the test scripts - o turn DEBUG_PARSER off by default - o add some documentation for the %b modifier to the default config file - o fix make install rule for when the udev symlink is already there - o change release target in makefile - o change debug level on printf values for now - o updated demo config file - o add some documentation of the modifiers to the default config file - o add demo config file - o updated bk ignore list for klibc generated files - o add printf option to label test to verify it works - o fix up printf-like functionality due to previous changes - o get the major/minor number before we name the device - o add scsi_id "extra" program from Patrick Mansfield - o Add multipath "extra" program from Christophe Varoqui, - o trailing whitespace cleanups - o splig LABEL and NUMBER into separate functions - o add TOPO regression test - o move TOPOLOGY rule to it's own function - o fix bug where NUMBER and TOPOLOGY would not work for partitions - o clean up the way we find the sysdevice for a block device for namedev - o updated label test script (tests for partitions now.) - o split REPLACE and CALLOUT into separate functions - o add debug line for REPLACE call - o add replace test - o add more sysfs test tree files - o change UDEV_SYSFS_PATH environment variable due to libsysfs change - o fix bug in klibc's isspace function - o fix udev-add.c to build properly with older versions of gcc - o add prototype for ftruncate to klibc - o Remove a few items from the TODO list that are already done - o version number to 005_bk - o pull some klibc stuff into the make Makefile to try to stay in sync - o klibc build fixes - -Kay Sievers: - o apply permissions.conf support for wildcard and default name - o man page with included placeholder list - o implement printf-like placeholder support for NAME - o more manpage tweaks - o add support for subdirs - o add uid/gid to nodes - -Olaf Hering: - o DESTDIR for udev - -Paul Mundt: - o Fixup path for kernel includes when building with klibc - -Robert Love: - o udev init script - - -Summary of changes from v004 to v005 -============================================ - -: - o namedev.c comments + debug patch - o man page update - -Greg Kroah-Hartman: - o ignore the klibc/linux symlink - o add klibc linux symlink info to the README - o get 'make release' to work properly again - o added README info for how to build using klibc - o turn off debugging if we are building with klibc - o turn off debugging in namedev - o added vsyslog support to klibc - o add ftruncate to klibc - o klibc specific tweaks - o libsysfs does not need mntent.h in it's header file - o udev build tweaks to tdb's spinlock code - o klibc makefile changes - o build tdb and libsysfs from the same makefile as udev - o udev-add build cleanups for other libc versions - o tweak tdb to build within udev better - o make libsysfs spit debug messages to the same place as the rest of udev - o make libsysfs build cleanly - o updated bk ignore list - o added klibc version 0.82 (cvs tree) to the udev tree - o makefile fix for now - o Merge greg@bucket:/home/greg/src/udev into kroah.com:/home/greg/src/udev - o hm, makefile bug with so many files... will fix later - o regression tests starting to be added - o fix LABEL bug for device files (not class files.) - o more warning flags to the build - o got rid of struct device_attr - o rename namedev.permissions and namedev.config to udev.permissions and udev.config - o fix dbg line in namedev.c - o more overrides of config info with env variables if in test mode - o Fix bug causing udev to sleep forever waiting for dev file to show up - o change version to 004_bk - o make config files, sysfs root, and udev root configurable from config variables - -Robert Love: - o udev: sleep_for_dev() bits - o udev: another canidate for static - - -Summary of changes from v003 to v004 -============================================ - -Daniel E. F. Stekloff: - o new version of libsysfs patch - -Greg Kroah-Hartman: - o 004 release - o major database cleanups - o Changed test.block and test.tty to take ACTION from the command line - o don't sleep if 'dev' file is already present on device add - o fix comment about how the "dev" file is made up - o more database work. Now we only store the info we really need right now - o add BUS= bug to TODO list so it will not get forgotten - o spec file changes - o test.block changes - o ok, rpm likes the "_" character instead of "-" better - o change the version to 003-bk to keep things sane with people using the bk tree - o got "remove of named devices" working - o fix segfaults when dealing with partitions - -Kay Sievers: - o man file update - o man page update - -Robert Love: - o udev: mode should be mode_t - o udev: trivial trivialities - o udev: cool test scripts again - o udev spec file symlink support - o udev: cool test scripts - o udev spec file bits - - -Summary of changes from v0.2 to v003 -============================================ - -Daniel E. F. Stekloff: - o udevdb patch - o udevdb prototype - -Greg Kroah-Hartman: - o update the spec file for the new version and install process - o fix makefile release rule to not drop tdb.h file - o Add FAQ for udev - o removed AUTHORS and INSTALL files as they were pretty pointless - o copyright updates - o Add AUTHORS and INSTALL files - o TODO updates - o Updatd the README - o updated the TODO list - o add udev man page (basically just a place holder for now.) - o added uninstall support - o added install target for makefile so people don't have to do it by hand anymore - o add version to debug log on startup - o tell the user what mknod() we are trying to do - o add dbg_parse() to cut down on parse file debugging statements - o put config files and database in /etc/udev by default - o add ols 2003 udev paper to docs/ - o clean up some debugging stuff in namedev.c - o do not build the tdb binary programs, only the objects - o merge tdb into the build process - o Added tdb code from latest cvs version in the samba tree - o added my name to the .spec file - o minor cleanups - o cleanup the mknod code a bit - o remove mknod callout - o handle new major:minor format of dev files that showed up in 2.6.0-test2-bk3 or so - o oops, everything was getting created as 000 mode, try to fix this up, but fail... - o more test stuff - -Olaf Hering: - o print udev pid - -Patrick Mansfield: - o add callout config type to udev - -Paul Mundt: - o Fix TDB cross compilation - o udev spec file - o udev/libsysfs cross compile fixes - - -Summary of changes from v0.1 to v0.2 -============================================ - -Greg Kroah-Hartman: - o more test stuff - o removed unneeded stuff from udev.h - o added 0.2 change log info - o start working on label support, and fix some segfaults for block devices - o test config file changes - o add NUMBER support (basically same logic as TOPOLOGY, perhaps we should - merge this...) - o added topology support - o got REPLACE to work properly - o make struct config_device contain a struct device_attr instead of - duplicating the mess - o block test - o split the tests up into different files - o split udev main logic into udev-add and udev-remove - o Clean up the namedev interface a bit, making the code smaller - o bk: update ignore list - o update the tests to handle block devices too - o add initial libsysfs support - o added libsysfs to the build - o added libsysfs code from sysutils-0.1.1-071803 release - o namedev config files are fully parsed - o more permission tests - o make log_message spit out warnings so I don't have to spend forever - chasing down stupid bugs that aren't there... - o added klibc makefile - o Initial namedev parsing of config files - o sleep for 2 seconds to give the kernel a chance to actually create the - files we need - o pick a better default UDEV_ROOT - o fix up the test to actually work - o added more documentation in README and TODO files - - -Summary of changes up to v0.1 -============================================ - -Greg Kroah-Hartman: - o added more documentation in README and TODO files - o updated the documentation - o cleaned up the makefile a bit - o remove now works! - o restructure code to be able to actually get remove_node() to work - o Creating nodes actually works - o added stupid test script for debugging - o added initial documentation and gpl license - o enabled debugging - o updated ignore list - o added initial files - o fixed up config - o Initial repository create - o BitKeeper file /home/greg/src/udev/udev/ChangeSet - diff --git a/src/udev/INSTALL b/src/udev/INSTALL deleted file mode 100644 index 0a34e77df2..0000000000 --- a/src/udev/INSTALL +++ /dev/null @@ -1,44 +0,0 @@ -The options used usually look like: - %configure \ - --prefix=/usr \ - --sysconfdir=/etc \ - --bindir=/usr/bin \ - --libdir=/usr/lib64 \ - --libexecdir=/usr/lib \ - --with-systemdsystemunitdir=/usr/lib/systemd/system \ - --with-selinux - -The options used in a RPM spec file look like: - %configure \ - --prefix=%{_prefix} \ - --sysconfdir=%{_sysconfdir} \ - --bindir=%{_bindir} \ - --libdir=%{_libdir} \ - --libexecdir=%{_prefix}/lib \ - --with-systemdsystemunitdir=%{_prefix}/lib/systemd/system \ - --with-selinux - -The options to install udev in the rootfs instead of /usr, -and udevadm in /sbin: - --prefix=%{_prefix} \ - --with-rootprefix= \ - --sysconfdir=%{_sysconfdir} \ - --bindir=/sbin \ - --libdir=%{_libdir} \ - --with-rootlibdir=/lib64 \ - --libexecdir=/lib \ - --with-systemdsystemunitdir=/lib/systemd/system \ - --with-selinux - -Some tools expect udevadm in 'sbin'. A symlink to udevadm in 'bin' -needs to be manually created if needed. - -The defined location for scripts and binaries which are called -from rules is (/usr)/lib/udev/ on all systems and architectures. Any -other location will break other packages, who rightfully expect -the (/usr)/lib/udev/ directory, to install their rule helper and udev -rule files. - -Default udev rules and persistent device naming rules may be required -by other software that depends on the data udev collects from the -devices. diff --git a/src/udev/Makefile.am b/src/udev/Makefile.am deleted file mode 100644 index 1c7f86b081..0000000000 --- a/src/udev/Makefile.am +++ /dev/null @@ -1,712 +0,0 @@ -# Copyright (C) 2008-2012 Kay Sievers -# Copyright (C) 2009 Diego Elio 'Flameeyes' Pettenò - -SUBDIRS = . - -ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} - -AM_MAKEFLAGS = --no-print-directory - -LIBUDEV_CURRENT=13 -LIBUDEV_REVISION=2 -LIBUDEV_AGE=13 - -LIBGUDEV_CURRENT=1 -LIBGUDEV_REVISION=1 -LIBGUDEV_AGE=1 - -AM_CPPFLAGS = \ - -include $(top_builddir)/config.h \ - -I$(top_srcdir)/src \ - -DSYSCONFDIR=\""$(sysconfdir)"\" \ - -DPKGLIBEXECDIR=\""$(libexecdir)/udev"\" - -AM_CFLAGS = \ - ${my_CFLAGS} \ - -fvisibility=hidden \ - -ffunction-sections \ - -fdata-sections - -AM_LDFLAGS = \ - -Wl,--gc-sections \ - -Wl,--as-needed - -DISTCHECK_CONFIGURE_FLAGS = \ - --enable-debug \ - --enable-rule_generator \ - --enable-floppy \ - --with-selinux \ - --enable-gtk-doc \ - --with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir) - -BUILT_SOURCES = -EXTRA_DIST = -CLEANFILES = -INSTALL_EXEC_HOOKS = -INSTALL_DATA_HOOKS = -UNINSTALL_EXEC_HOOKS = -DISTCHECK_HOOKS = -DISTCLEAN_LOCAL_HOOKS = - -udevhomedir = $(libexecdir)/udev -udevhome_SCRIPTS = -dist_udevhome_SCRIPTS = -dist_udevhome_DATA = -dist_man_MANS = - -SED_PROCESS = \ - $(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(SED) \ - -e 's,@VERSION\@,$(VERSION),g' \ - -e 's,@prefix\@,$(prefix),g' \ - -e 's,@rootprefix\@,$(rootprefix),g' \ - -e 's,@exec_prefix\@,$(exec_prefix),g' \ - -e 's,@libdir\@,$(libdir),g' \ - -e 's,@includedir\@,$(includedir),g' \ - -e 's,@bindir\@,$(bindir),g' \ - -e 's,@pkglibexecdir\@,$(libexecdir)/udev,g' \ - < $< > $@ || rm $@ - -%.pc: %.pc.in Makefile - $(SED_PROCESS) - -%.rules: %.rules.in Makefile - $(SED_PROCESS) - -%.service: %.service.in Makefile - $(SED_PROCESS) - -%.sh: %.sh.in Makefile - $(SED_PROCESS) - $(AM_V_GEN)chmod +x $@ - -%.pl: %.pl.in Makefile - $(SED_PROCESS) - $(AM_V_GEN)chmod +x $@ - -# ------------------------------------------------------------------------------ -SUBDIRS += src/docs - -include_HEADERS = src/libudev.h -lib_LTLIBRARIES = libudev.la -noinst_LTLIBRARIES = libudev-private.la - -libudev_la_SOURCES =\ - src/libudev-private.h \ - src/libudev.c \ - src/libudev-list.c \ - src/libudev-util.c \ - src/libudev-device.c \ - src/libudev-enumerate.c \ - src/libudev-monitor.c \ - src/libudev-queue.c - -libudev_la_LDFLAGS = \ - $(AM_LDFLAGS) \ - -version-info $(LIBUDEV_CURRENT):$(LIBUDEV_REVISION):$(LIBUDEV_AGE) - -libudev_private_la_SOURCES =\ - $(libudev_la_SOURCES) \ - src/libudev-util-private.c \ - src/libudev-device-private.c \ - src/libudev-queue-private.c - -if WITH_SELINUX -libudev_private_la_SOURCES += src/libudev-selinux-private.c -libudev_private_la_LIBADD = $(SELINUX_LIBS) -endif - -pkgconfigdir = $(libdir)/pkgconfig -pkgconfig_DATA = src/libudev.pc -EXTRA_DIST += src/libudev.pc.in -CLEANFILES += src/libudev.pc - -EXTRA_DIST += src/COPYING -# move lib from $(libdir) to $(rootlib_execdir) and update devel link, if needed -libudev-install-move-hook: - if test "$(libdir)" != "$(rootlib_execdir)"; then \ - mkdir -p $(DESTDIR)$(rootlib_execdir) && \ - so_img_name=$$(readlink $(DESTDIR)$(libdir)/libudev.so) && \ - so_img_rel_target_prefix=$$(echo $(libdir) | sed 's,\(^/\|\)[^/][^/]*,..,g') && \ - ln -sf $$so_img_rel_target_prefix$(rootlib_execdir)/$$so_img_name $(DESTDIR)$(libdir)/libudev.so && \ - mv $(DESTDIR)$(libdir)/libudev.so.* $(DESTDIR)$(rootlib_execdir); \ - fi - -libudev-uninstall-move-hook: - rm -f $(DESTDIR)$(rootlib_execdir)/libudev.so* - -INSTALL_EXEC_HOOKS += libudev-install-move-hook -UNINSTALL_EXEC_HOOKS += libudev-uninstall-move-hook - -# ------------------------------------------------------------------------------ -udev-confdirs: - -mkdir -p $(DESTDIR)$(sysconfdir)/udev/rules.d - -mkdir -p $(DESTDIR)$(libexecdir)/udev/devices - -INSTALL_DATA_HOOKS += udev-confdirs - -udevrulesdir = $(libexecdir)/udev/rules.d -dist_udevrules_DATA = \ - rules/42-usb-hid-pm.rules \ - rules/50-udev-default.rules \ - rules/60-persistent-storage-tape.rules \ - rules/60-persistent-serial.rules \ - rules/60-persistent-input.rules \ - rules/60-persistent-alsa.rules \ - rules/60-persistent-storage.rules \ - rules/75-net-description.rules \ - rules/75-tty-description.rules \ - rules/78-sound-card.rules \ - rules/80-drivers.rules \ - rules/95-udev-late.rules - -udevconfdir = $(sysconfdir)/udev -dist_udevconf_DATA = src/udev.conf - -sharepkgconfigdir = $(datadir)/pkgconfig -sharepkgconfig_DATA = src/udev.pc -EXTRA_DIST += src/udev.pc.in -CLEANFILES += src/udev.pc - -if WITH_SYSTEMD -dist_systemdsystemunit_DATA = \ - src/udev-control.socket \ - src/udev-kernel.socket - -systemdsystemunit_DATA = \ - src/udev.service \ - src/udev-trigger.service \ - src/udev-settle.service - -EXTRA_DIST += \ - src/udev.service.in \ - src/udev-trigger.service.in \ - src/udev-settle.service.in - -CLEANFILES += \ - src/udev.service \ - src/udev-trigger.service \ - src/udev-settle.service - -systemd-install-hook: - mkdir -p $(DESTDIR)$(systemdsystemunitdir)/sockets.target.wants - ln -sf ../udev-control.socket $(DESTDIR)$(systemdsystemunitdir)/sockets.target.wants/udev-control.socket - ln -sf ../udev-kernel.socket $(DESTDIR)$(systemdsystemunitdir)/sockets.target.wants/udev-kernel.socket - mkdir -p $(DESTDIR)$(systemdsystemunitdir)/basic.target.wants - ln -sf ../udev.service $(DESTDIR)$(systemdsystemunitdir)/basic.target.wants/udev.service - ln -sf ../udev-trigger.service $(DESTDIR)$(systemdsystemunitdir)/basic.target.wants/udev-trigger.service - -INSTALL_DATA_HOOKS += systemd-install-hook -endif - -bin_PROGRAMS = \ - udevadm - -pkglibexec_PROGRAMS = \ - udevd - -udev_common_sources = \ - src/udev.h \ - src/udev-event.c \ - src/udev-watch.c \ - src/udev-node.c \ - src/udev-rules.c \ - src/udev-ctrl.c \ - src/udev-builtin.c \ - src/udev-builtin-blkid.c \ - src/udev-builtin-firmware.c \ - src/udev-builtin-hwdb.c \ - src/udev-builtin-input_id.c \ - src/udev-builtin-kmod.c \ - src/udev-builtin-path_id.c \ - src/udev-builtin-usb_id.c - -udev_common_CFLAGS = \ - $(BLKID_CFLAGS) \ - $(KMOD_CFLAGS) - -udev_common_LDADD = \ - libudev-private.la \ - $(BLKID_LIBS) \ - $(KMOD_LIBS) - -udev_common_CPPFLAGS = \ - $(AM_CPPFLAGS) \ - -DFIRMWARE_PATH="$(FIRMWARE_PATH)" \ - -DUSB_DATABASE=\"$(USB_DATABASE)\" -DPCI_DATABASE=\"$(PCI_DATABASE)\" - -udevd_SOURCES = \ - $(udev_common_sources) \ - src/udevd.c \ - src/sd-daemon.h \ - src/sd-daemon.c -udevd_CFLAGS = $(udev_common_CFLAGS) -udevd_LDADD = $(udev_common_LDADD) -udevd_CPPFLAGS = $(udev_common_CPPFLAGS) - -udevadm_SOURCES = \ - $(udev_common_sources) \ - src/udevadm.c \ - src/udevadm-info.c \ - src/udevadm-control.c \ - src/udevadm-monitor.c \ - src/udevadm-settle.c \ - src/udevadm-trigger.c \ - src/udevadm-test.c \ - src/udevadm-test-builtin.c -udevadm_CFLAGS = $(udev_common_CFLAGS) -udevadm_LDADD = $(udev_common_LDADD) -udevadm_CPPFLAGS = $(udev_common_CPPFLAGS) - -# ------------------------------------------------------------------------------ -if ENABLE_MANPAGES -dist_man_MANS += \ - src/udev.7 \ - src/udevadm.8 \ - src/udevd.8 -endif - -EXTRA_DIST += \ - src/udev.xml \ - src/udevadm.xml \ - src/udevd.xml - -if HAVE_XSLTPROC -dist_noinst_DATA = \ - src/udev.html \ - src/udevadm.html \ - src/udevd.html - -src/%.7 src/%.8 : src/%.xml - $(AM_V_GEN)$(XSLTPROC) -o $@ -nonet http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $< - -src/%.html : src/%.xml - $(AM_V_GEN)$(XSLTPROC) -o $@ -nonet http://docbook.sourceforge.net/release/xsl/current/xhtml-1_1/docbook.xsl $< -endif - -# ------------------------------------------------------------------------------ -TESTS = \ - test/udev-test.pl \ - test/rules-test.sh - -check_PROGRAMS = \ - test-libudev \ - test-udev - -test_libudev_SOURCES = src/test-libudev.c -test_libudev_LDADD = libudev.la - -test_udev_SOURCES = \ - $(udev_common_sources) \ - src/test-udev.c -test_udev_CFLAGS = $(udev_common_CFLAGS) -test_udev_LDADD = $(udev_common_LDADD) -test_udev_CPPFLAGS = $(udev_common_CPPFLAGS) -test_udev_DEPENDENCIES = test/sys - -# packed sysfs test tree -test/sys: - $(AM_V_GEN)mkdir -p test && tar -C test/ -xJf $(top_srcdir)/test/sys.tar.xz - -test-sys-distclean: - -rm -rf test/sys -DISTCLEAN_LOCAL_HOOKS += test-sys-distclean - -EXTRA_DIST += test/sys.tar.xz - -# ------------------------------------------------------------------------------ -ata_id_SOURCES = src/ata_id/ata_id.c -ata_id_LDADD = libudev-private.la -pkglibexec_PROGRAMS += ata_id - -# ------------------------------------------------------------------------------ -cdrom_id_SOURCES = src/cdrom_id/cdrom_id.c -cdrom_id_LDADD = libudev-private.la -pkglibexec_PROGRAMS += cdrom_id -dist_udevrules_DATA += src/cdrom_id/60-cdrom_id.rules - -# ------------------------------------------------------------------------------ -collect_SOURCES = src/collect/collect.c -collect_LDADD = libudev-private.la -pkglibexec_PROGRAMS += collect - -# ------------------------------------------------------------------------------ -scsi_id_SOURCES =\ - src/scsi_id/scsi_id.c \ - src/scsi_id/scsi_serial.c \ - src/scsi_id/scsi.h \ - src/scsi_id/scsi_id.h -scsi_id_LDADD = libudev-private.la -pkglibexec_PROGRAMS += scsi_id -dist_man_MANS += src/scsi_id/scsi_id.8 -EXTRA_DIST += src/scsi_id/README - -# ------------------------------------------------------------------------------ -v4l_id_SOURCES = src/v4l_id/v4l_id.c -v4l_id_LDADD = libudev-private.la -pkglibexec_PROGRAMS += v4l_id -dist_udevrules_DATA += src/v4l_id/60-persistent-v4l.rules - -# ------------------------------------------------------------------------------ -accelerometer_SOURCES = src/accelerometer/accelerometer.c -accelerometer_LDADD = libudev-private.la -lm -pkglibexec_PROGRAMS += accelerometer -dist_udevrules_DATA += src/accelerometer/61-accelerometer.rules - -# ------------------------------------------------------------------------------ -if ENABLE_GUDEV -SUBDIRS += src/gudev/docs - -libgudev_includedir=$(includedir)/gudev-1.0/gudev -libgudev_include_HEADERS = \ - src/gudev/gudev.h \ - src/gudev/gudevenums.h \ - src/gudev/gudevenumtypes.h \ - src/gudev/gudevtypes.h \ - src/gudev/gudevclient.h \ - src/gudev/gudevdevice.h \ - src/gudev/gudevenumerator.h - -lib_LTLIBRARIES += libgudev-1.0.la - -pkgconfig_DATA += src/gudev/gudev-1.0.pc -EXTRA_DIST += src/gudev/gudev-1.0.pc.in -CLEANFILES += src/gudev/gudev-1.0.pc - -libgudev_1_0_la_SOURCES = \ - src/gudev/gudevenums.h \ - src/gudev/gudevenumtypes.h \ - src/gudev/gudevenumtypes.h\ - src/gudev/gudevtypes.h \ - src/gudev/gudevclient.h \ - src/gudev/gudevclient.c \ - src/gudev/gudevdevice.h \ - src/gudev/gudevdevice.c \ - src/gudev/gudevenumerator.h \ - src/gudev/gudevenumerator.c \ - src/gudev/gudevprivate.h - -nodist_libgudev_1_0_la_SOURCES = \ - src/gudev/gudevmarshal.h \ - src/gudev/gudevmarshal.c \ - src/gudev/gudevenumtypes.h \ - src/gudev/gudevenumtypes.c -BUILT_SOURCES += $(nodist_libgudev_1_0_la_SOURCES) - -libgudev_1_0_la_CPPFLAGS = \ - $(AM_CPPFLAGS) \ - -I$(top_builddir)/src\ - -I$(top_srcdir)/src\ - -I$(top_builddir)/src/gudev \ - -I$(top_srcdir)/src/gudev \ - -D_POSIX_PTHREAD_SEMANTICS -D_REENTRANT \ - -D_GUDEV_COMPILATION \ - -DG_LOG_DOMAIN=\"GUdev\" - -libgudev_1_0_la_CFLAGS = \ - -fvisibility=default \ - $(GLIB_CFLAGS) - -libgudev_1_0_la_LIBADD = libudev.la $(GLIB_LIBS) - -libgudev_1_0_la_LDFLAGS = \ - -version-info $(LIBGUDEV_CURRENT):$(LIBGUDEV_REVISION):$(LIBGUDEV_AGE) \ - -export-dynamic -no-undefined \ - -export-symbols-regex '^g_udev_.*' - -EXTRA_DIST += \ - src/gudev/COPYING \ - src/gudev/gudevmarshal.list \ - src/gudev/gudevenumtypes.h.template \ - src/gudev/gudevenumtypes.c.template \ - src/gudev/gjs-example.js \ - src/gudev/seed-example-enum.js \ - src/gudev/seed-example.js - -src/gudev/gudevmarshal.h: src/gudev/gudevmarshal.list - $(AM_V_GEN)glib-genmarshal $< --prefix=g_udev_marshal --header > $@ - -src/gudev/gudevmarshal.c: src/gudev/gudevmarshal.list - $(AM_V_GEN)echo "#include \"gudevmarshal.h\"" > $@ && \ - glib-genmarshal $< --prefix=g_udev_marshal --body >> $@ - -src/gudev/gudevenumtypes.h: src/gudev/gudevenumtypes.h.template src/gudev/gudevenums.h - $(AM_V_GEN)glib-mkenums --template $^ > \ - $@.tmp && mv $@.tmp $@ - -src/gudev/gudevenumtypes.c: src/gudev/gudevenumtypes.c.template src/gudev/gudevenums.h - $(AM_V_GEN)glib-mkenums --template $^ > \ - $@.tmp && mv $@.tmp $@ - -if ENABLE_INTROSPECTION -src/gudev/GUdev-1.0.gir: libgudev-1.0.la $(G_IR_SCANNER) - $(AM_V_GEN)$(G_IR_SCANNER) -v \ - --warn-all \ - --namespace GUdev \ - --nsversion=1.0 \ - --include=GObject-2.0 \ - --library=gudev-1.0 \ - --library-path=$(top_builddir)/src \ - --library-path=$(top_builddir)/src/gudev \ - --output $@ \ - --pkg=glib-2.0 \ - --pkg=gobject-2.0 \ - --pkg-export=gudev-1.0 \ - --c-include=gudev/gudev.h \ - -I$(top_srcdir)/src/\ - -I$(top_builddir)/src/\ - -D_GUDEV_COMPILATION \ - -D_GUDEV_WORK_AROUND_DEV_T_BUG \ - $(top_srcdir)/src/gudev/gudev.h \ - $(top_srcdir)/src/gudev/gudevtypes.h \ - $(top_srcdir)/src/gudev/gudevenums.h \ - $(or $(wildcard $(top_builddir)/src/gudev/gudevenumtypes.h),$(top_srcdir)/src/gudev/gudevenumtypes.h) \ - $(top_srcdir)/src/gudev/gudevclient.h \ - $(top_srcdir)/src/gudev/gudevdevice.h \ - $(top_srcdir)/src/gudev/gudevenumerator.h \ - $(top_srcdir)/src/gudev/gudevclient.c \ - $(top_srcdir)/src/gudev/gudevdevice.c \ - $(top_srcdir)/src/gudev/gudevenumerator.c - -src/gudev/GUdev-1.0.typelib: src/gudev/GUdev-1.0.gir $(G_IR_COMPILER) - $(AM_V_GEN)g-ir-compiler $< -o $@ - -girdir = $(GIRDIR) -gir_DATA = src/gudev/GUdev-1.0.gir - -typelibsdir = $(GIRTYPELIBDIR) -typelibs_DATA = src/gudev/GUdev-1.0.typelib - -CLEANFILES += $(gir_DATA) $(typelibs_DATA) -endif # ENABLE_INTROSPECTION - -# move lib from $(libdir) to $(rootlib_execdir) and update devel link, if needed -libgudev-install-move-hook: - if test "$(libdir)" != "$(rootlib_execdir)"; then \ - mkdir -p $(DESTDIR)$(rootlib_execdir) && \ - so_img_name=$$(readlink $(DESTDIR)$(libdir)/libgudev-1.0.so) && \ - so_img_rel_target_prefix=$$(echo $(libdir) | sed 's,\(^/\|\)[^/][^/]*,..,g') && \ - ln -sf $$so_img_rel_target_prefix$(rootlib_execdir)/$$so_img_name $(DESTDIR)$(libdir)/libgudev-1.0.so && \ - mv $(DESTDIR)$(libdir)/libgudev-1.0.so.* $(DESTDIR)$(rootlib_execdir); \ - fi - -libgudev-uninstall-move-hook: - rm -f $(DESTDIR)$(rootlib_execdir)/libgudev-1.0.so* - -INSTALL_EXEC_HOOKS += libgudev-install-move-hook -UNINSTALL_EXEC_HOOKS += libgudev-uninstall-move-hook -endif - -# ------------------------------------------------------------------------------ -if ENABLE_KEYMAP -keymap_SOURCES = src/keymap/keymap.c -keymap_CPPFLAGS = $(AM_CPPFLAGS) -I src/keymap -nodist_keymap_SOURCES = \ - src/keymap/keys-from-name.h \ - src/keymap/keys-to-name.h -BUILT_SOURCES += $(nodist_keymap_SOURCES) - -pkglibexec_PROGRAMS += keymap -dist_doc_DATA = src/keymap/README.keymap.txt - -dist_udevrules_DATA += \ - src/keymap/95-keymap.rules \ - src/keymap/95-keyboard-force-release.rules - -dist_udevhome_SCRIPTS += src/keymap/findkeyboards -udevhome_SCRIPTS += src/keymap/keyboard-force-release.sh - -EXTRA_DIST += \ - src/keymap/check-keymaps.sh \ - src/keymap/keyboard-force-release.sh.in - -CLEANFILES += \ - src/keymap/keys.txt \ - src/keymap/keys-from-name.gperf \ - src/keymap/keyboard-force-release.sh - -udevkeymapdir = $(libexecdir)/udev/keymaps -dist_udevkeymap_DATA = \ - src/keymap/keymaps/acer \ - src/keymap/keymaps/acer-aspire_5720 \ - src/keymap/keymaps/acer-aspire_8930 \ - src/keymap/keymaps/acer-aspire_5920g \ - src/keymap/keymaps/acer-aspire_6920 \ - src/keymap/keymaps/acer-travelmate_c300 \ - src/keymap/keymaps/asus \ - src/keymap/keymaps/compaq-e_evo \ - src/keymap/keymaps/dell \ - src/keymap/keymaps/dell-latitude-xt2 \ - src/keymap/keymaps/everex-xt5000 \ - src/keymap/keymaps/fujitsu-amilo_li_2732 \ - src/keymap/keymaps/fujitsu-amilo_pa_2548 \ - src/keymap/keymaps/fujitsu-amilo_pro_edition_v3505 \ - src/keymap/keymaps/fujitsu-amilo_pro_v3205 \ - src/keymap/keymaps/fujitsu-amilo_si_1520 \ - src/keymap/keymaps/fujitsu-esprimo_mobile_v5 \ - src/keymap/keymaps/fujitsu-esprimo_mobile_v6 \ - src/keymap/keymaps/genius-slimstar-320 \ - src/keymap/keymaps/hewlett-packard \ - src/keymap/keymaps/hewlett-packard-2510p_2530p \ - src/keymap/keymaps/hewlett-packard-compaq_elitebook \ - src/keymap/keymaps/hewlett-packard-pavilion \ - src/keymap/keymaps/hewlett-packard-presario-2100 \ - src/keymap/keymaps/hewlett-packard-tablet \ - src/keymap/keymaps/hewlett-packard-tx2 \ - src/keymap/keymaps/ibm-thinkpad-usb-keyboard-trackpoint \ - src/keymap/keymaps/inventec-symphony_6.0_7.0 \ - src/keymap/keymaps/lenovo-3000 \ - src/keymap/keymaps/lenovo-ideapad \ - src/keymap/keymaps/lenovo-thinkpad-usb-keyboard-trackpoint \ - src/keymap/keymaps/lenovo-thinkpad_x6_tablet \ - src/keymap/keymaps/lenovo-thinkpad_x200_tablet \ - src/keymap/keymaps/lg-x110 \ - src/keymap/keymaps/logitech-wave \ - src/keymap/keymaps/logitech-wave-cordless \ - src/keymap/keymaps/logitech-wave-pro-cordless \ - src/keymap/keymaps/maxdata-pro_7000 \ - src/keymap/keymaps/medion-fid2060 \ - src/keymap/keymaps/medionnb-a555 \ - src/keymap/keymaps/micro-star \ - src/keymap/keymaps/module-asus-w3j \ - src/keymap/keymaps/module-ibm \ - src/keymap/keymaps/module-lenovo \ - src/keymap/keymaps/module-sony \ - src/keymap/keymaps/module-sony-old \ - src/keymap/keymaps/module-sony-vgn \ - src/keymap/keymaps/olpc-xo \ - src/keymap/keymaps/onkyo \ - src/keymap/keymaps/oqo-model2 \ - src/keymap/keymaps/samsung-other \ - src/keymap/keymaps/samsung-90x3a \ - src/keymap/keymaps/samsung-sq1us \ - src/keymap/keymaps/samsung-sx20s \ - src/keymap/keymaps/toshiba-satellite_a100 \ - src/keymap/keymaps/toshiba-satellite_a110 \ - src/keymap/keymaps/toshiba-satellite_m30x \ - src/keymap/keymaps/zepto-znote - -udevkeymapforcereldir = $(libexecdir)/udev/keymaps/force-release -dist_udevkeymapforcerel_DATA = \ - src/keymap/force-release-maps/dell-touchpad \ - src/keymap/force-release-maps/hp-other \ - src/keymap/force-release-maps/samsung-other \ - src/keymap/force-release-maps/samsung-90x3a \ - src/keymap/force-release-maps/common-volume-keys - -src/keymap/keys.txt: $(INCLUDE_PREFIX)/linux/input.h - $(AM_V_at)mkdir -p src/keymap - $(AM_V_GEN)$(AWK) '/^#define.*KEY_[^ ]+[ \t]+[0-9]/ { if ($$2 != "KEY_MAX") { print $$2 } }' < $< | sed 's/^KEY_COFFEE$$/KEY_SCREENLOCK/' > $@ - -src/keymap/keys-from-name.gperf: src/keymap/keys.txt - $(AM_V_GEN)$(AWK) 'BEGIN{ print "struct key { const char* name; unsigned short id; };"; print "%null-strings"; print "%%";} { print $$1 ", " $$1 }' < $< > $@ - -src/keymap/keys-from-name.h: src/keymap/keys-from-name.gperf Makefile - $(AM_V_GEN)$(GPERF) -L ANSI-C -t --ignore-case -N lookup_key -H hash_key_name -p -C < $< > $@ - -src/keymap/keys-to-name.h: src/keymap/keys.txt Makefile - $(AM_V_GEN)$(AWK) 'BEGIN{ print "const char* const key_names[KEY_CNT] = { "} { print "[" $$1 "] = \"" $$1 "\"," } END{print "};"}' < $< > $@ - -keymaps-distcheck-hook: src/keymap/keys.txt - $(top_srcdir)/src/keymap/check-keymaps.sh $(top_srcdir) $^ -DISTCHECK_HOOKS += keymaps-distcheck-hook -endif - -if ENABLE_MTD_PROBE -# ------------------------------------------------------------------------------ -mtd_probe_SOURCES = \ - src/mtd_probe/mtd_probe.c \ - src/mtd_probe/mtd_probe.h \ - src/mtd_probe/probe_smartmedia.c -mtd_probe_CPPFLAGS = $(AM_CPPFLAGS) -dist_udevrules_DATA += src/mtd_probe/75-probe_mtd.rules -pkglibexec_PROGRAMS += mtd_probe -endif - -# ------------------------------------------------------------------------------ -if ENABLE_RULE_GENERATOR -dist_udevhome_SCRIPTS += \ - src/rule_generator/write_cd_rules \ - src/rule_generator/write_net_rules - -dist_udevhome_DATA += \ - src/rule_generator/rule_generator.functions - -dist_udevrules_DATA += \ - src/rule_generator/75-cd-aliases-generator.rules \ - src/rule_generator/75-persistent-net-generator.rules -endif - -# ------------------------------------------------------------------------------ -if ENABLE_FLOPPY -create_floppy_devices_SOURCES = src/floppy/create_floppy_devices.c -create_floppy_devices_LDADD = libudev-private.la -pkglibexec_PROGRAMS += create_floppy_devices -dist_udevrules_DATA += src/floppy/60-floppy.rules -endif - -# ------------------------------------------------------------------------------ -clean-local: - rm -rf udev-test-install - -distclean-local: - rm -rf autom4te.cache - -EXTRA_DIST += \ - $(TESTS) \ - test/rule-syntax-check.py - -CLEANFILES += \ - $(BUILT_SOURCES) - -install-exec-hook: $(INSTALL_EXEC_HOOKS) - -install-data-hook: $(INSTALL_DATA_HOOKS) - -uninstall-hook: $(UNINSTALL_EXEC_HOOKS) - -distcheck-hook: $(DISTCHECK_HOOKS) - -distclean-local: $(DISTCLEAN_LOCAL_HOOKS) - -# ------------------------------------------------------------------------------ -PREVIOUS_VERSION = `expr $(VERSION) - 1` -changelog: - @ head -1 ChangeLog | grep -q "to v$(PREVIOUS_VERSION)" - @ mv ChangeLog ChangeLog.tmp - @ echo "Summary of changes from v$(PREVIOUS_VERSION) to v$(VERSION)" >> ChangeLog - @ echo "============================================" >> ChangeLog - @ echo >> ChangeLog - @ git log --pretty=short $(PREVIOUS_VERSION)..HEAD | git shortlog >> ChangeLog - @ echo >> ChangeLog - @ cat ChangeLog - @ cat ChangeLog.tmp >> ChangeLog - @ rm ChangeLog.tmp - -test-install: - rm -rf $(PWD)/udev-test-install/ - make DESTDIR=$(PWD)/udev-test-install install - tree $(PWD)/udev-test-install/ - -git-release: - head -1 ChangeLog | grep -q "to v$(VERSION)" - head -1 NEWS | grep -q "udev $(VERSION)" - git commit -a -m "release $(VERSION)" - git tag -m "udev $(VERSION)" -s $(VERSION) - git gc --prune=0 - -git-sync: - git push - git push --tags - -tar-sync: - rm -f udev-$(VERSION).tar.sign - xz -d -c udev-$(VERSION).tar.xz | gpg --armor --detach-sign --output udev-$(VERSION).tar.sign - kup put udev-$(VERSION).tar.xz udev-$(VERSION).tar.sign /pub/linux/utils/kernel/hotplug/ - -doc-sync: - for i in src/*.html; do rm -f $$i.sign; gpg --armor --detach-sign --output=$$i.sign $$i; done - for i in src/*.html; do echo $$i; kup put $$i $$i.sign /pub/linux/utils/kernel/hotplug/udev/; done - for i in src/docs/html/*.{html,css,png}; do rm -f $$i.sign; gpg --armor --detach-sign --output=$$i.sign $$i; done - for i in src/docs/html/*.{html,css,png}; do echo $$i; kup put $$i $$i.sign /pub/linux/utils/kernel/hotplug/libudev/; done - for i in src/gudev/docs/html/*.{html,css,png}; do rm -f $$i.sign; gpg --armor --detach-sign --output=$$i.sign $$i; done - for i in src/gudev/docs/html/*.{html,css,png}; do echo $$i; kup put $$i $$i.sign /pub/linux/utils/kernel/hotplug/gudev/; done diff --git a/src/udev/NEWS b/src/udev/NEWS deleted file mode 100644 index f4f6f4e327..0000000000 --- a/src/udev/NEWS +++ /dev/null @@ -1,1735 +0,0 @@ -udev 182 -======== -Rules files in /etc/udev/rules.s/ with the same name as rules files in -/run/udev/rules.d/ now always have precedence. The stack of files is now: -/usr/lib (package), /run (runtime, auto-generated), /etc (admin), while -the later ones override the earlier ones. In other words: the admin has -always the last say. - -USB auto-suspend is now enabled by default for some built-in USB HID -devices. - -/dev/disk/by-path/ links are no longer created for ATA devices behind -an 'ATA transport class', the logic to extract predictable numbers does -not exist in the kernel at this moment. - -/dev/disk/by-id/scsi-* compatibility links are no longer created for -ATA devices, they have their own ata-* prefix. - -The s390 rule to set mode == 0666 for /dev/z90crypt is is removed from -the udev tree and will be part of s390utils (or alternatively could be -done by the kernel driver itself). - -The udev-acl tool is no longer provided, it will be part of a future -ConsoleKit release. On systemd systems, advanced ConsoleKit and udev-acl -functionality are provided by systemd. - -udev 181 -======== -Require kmod version 5. - -Provide /dev/cdrom symlink for /dev/sr0. - -udev 180 -======== -Fix for ID_PART_ENTRY_* property names, added by the blkid built-in. The -fix is needed for udisk2 to operate properly. - -Fix for skipped rule execution when the kernel has removed the device -node in /dev again, before the event was even started. The fix is needed -to run device-mapper/LVM events properly. - -Fix for the man page installation, which was skipped when xsltproc was not -installed. - -udev 179 -======== -Bugfix for $name resolution, which broke at least some keymap handling. - -udev 178 -======== -Bugfix for the firmware loading behavior with kernel modules which -try to load firmware in the module_init() path. The blocked event -runs into a timout now, which should allow the firmware to be loaded. - -Bugfix for a wrong DEVNAME= export, which breaks at least the udev-acl -tool. - -Bugfix for missing ID_ properties for GPT partitions. - -The RUN+="socket:.." option is deprecated and should not be used. A warning -during rules parsing is printed now. Services which listen to udev events, -need to subscribe to the netlink messages with libudev and not let udev block -in the rules execution until the message is delivered. - -udev 177 -======== -Bugfix for rule_generator instalation. - -udev 176 -======== -The 'devtmpfs' filesystem is required now, udev will not create or delete -device nodes anymore, it only adjusts permissions and ownership of device -nodes and maintains additional symlinks. - -A writable /run directory (ususally tmpfs) is required now for a fully -functional udev, there is no longer a fallback to /dev/.udev. - -The default 'configure' install locations have changed. Packages for systems -with the historic / vs. /usr split need to be adapted, otherwise udev will -be installed in /usr and not work properly. Example configuration options -to install things the traditional way are in INSTALL. - -The default install location of the 'udevadm' tool moved from 'sbin' -to /usr/bin. Some tools expect udevadm in 'sbin', a symlink to udevadm -needs to be manually created if needed, or --bindir=/sbin be specified. - -The expected value of '--libexecdir=' has changed and must no longer contain -the 'udev' directory. - -Kernel modules are now loaded directly by linking udev to 'libkmod'. The -'modprobe' tool is no longer executed by udev. - -The 'blkid' tool is no longer executed from udev rules. Udev links -directly to libblkid now. - -Firmware is loaded natively by udev now, the external 'firmware' binary -is no longer used. - -All built-in tools can be listed and tested with 'udevadm test-builtin'. - -The 'udevadm control --reload-rules' option has been renamed to '--reload'. -It now also reloads the kernel module configuration. - -The systemd socket files use PassCredentials=yes, which is available in -systemd version 38. - -The udev build system only creates a .xz tarball now. - -All tabs in the source code used for indentation are replaced by spaces now. :) - -udev 175 -======== -Bugfixes. - -udev 174 -======== -Bugfixes. - -The udev daemon moved to /lib/udev/udevd. Non-systemd init systems -and non-dracut initramfs image generators need to change the init -scripts. Alternatively the udev build needs to move udevd back to -/sbin or create a symlink in /sbin, which is not done by default. - -The path_id, usb_id, input_id tools are built-in commands now and -the stand-alone tools do not exist anymore. Static lists of file in -initramfs generators need to be updated. For testing, the commands -can still be executed standalone with 'udevadm test-builtin '. - -The fusectl filesystem is no longer mounted directly from udev. -Systemd systems will take care of mounting fusectl and configfs -now. Non-systemd systems need to ship their own rule if they -need these filesystems auto-mounted. - -The long deprecated keys: SYSFS=, ID=, BUS= have been removed. - -The support for 'udevadm trigger --type=failed, and the -RUN{fail_event_on_error} attribute was removed. - -The udev control socket is now created in /run/udev/control -and no longer as an abstract namespace one. - -The rules to create persistent network interface and cdrom link -rules automatically in /etc/udev/rules.d/ have been disabled by -default. Explicit configuration will be required for these use -cases, udev will no longer try to write any persistent system -configuration from a device hotplug path. - -udev 173 -======== -Bugfixes. - -The udev-acl extra is no longer enabled by default now. To enable it, ---enable-udev_acl needs to be given at ./configure time. On systemd -systems, the udev-acl rules prevent it from running as the functionality -has moved to systemd. - -udev 172 -======== -Bugfixes. - -Udev now enables kernel media-presence polling if available. Part -of udisks optical drive tray-handling moved to cdrom_id: The tray -is locked as soon as a media is detected to enable the receiving -of media-eject-request events. Media-eject-request events will -eject the media. - -Libudev enumerate is now able to enumerate a subtree of a given -device. - -The mobile-action-modeswitch modeswitch tool was deleted. The -functionality is provided by usb_modeswitch now. - -udev 171 -======== -Bugfixes. - -The systemd service files require systemd version 28. The systemd -socket activation make it possible now to start 'udevd' and 'udevadm -trigger' in parallel. - -udev 170 -======== -Fix bug in control message handling, which can lead to a failing -udevadm control --exit. Thanks to Jürg Billeter for help tracking -it down. - -udev 169 -======== -Bugfixes. - -We require at least Linux kernel 2.6.32 now. Some platforms might -require a later kernel that supports accept4() and similar, or -need to backport the trivial syscall wiring to the older kernels. - -The hid2hci tool moved to the bluez package and was removed. - -Many of the extras can be --enable/--disabled at ./configure -time. The --disable-extras option was removed. Some extras have -been disabled by default. The current options and their defaults -can be checked with './configure --help'. - -udev 168 -======== -Bugfixes. - -Udev logs a warning now if /run is not writable at udevd -startup. It will still fall back to /dev/.udev, but this is -now considered a bug. - -The running udev daemon can now cleanly shut down with: - udevadm control --exit - -Udev in initramfs should clean the state of the udev database -with: udevadm info --cleanup-db which will remove all state left -behind from events/rules in initramfs. If initramfs uses ---cleanup-db and device-mapper/LVM, the rules in initramfs need -to add OPTIONS+="db_persist" for all dm devices. This will -prevent removal of the udev database for these devices. - -Spawned programs by PROGRAM/IMPORT/RUN now have a hard timeout of -120 seconds per process. If that timeout is reached the spawned -process will be killed. The event timeout can be overwritten with -udev rules. - -If systemd is used, udev gets now activated by netlink data. -Systemd will bind the netlink socket which will buffer all data. -If needed, such setup allows a seemless update of the udev daemon, -where no event can be lost during a udevd update/restart. -Packages need to make sure to: systemctl stop udev.socket udev.service -or 'mask' udev.service during the upgrade to prevent any unwanted -auto-spawning of udevd. -This version of udev conflicts with systemd version below 25. The -unchanged service files will not wirk correctly. - -udev 167 -======== -Bugfixes. - -The udev runtime data moved from /dev/.udev/ to /run/udev/. The -/run mountpoint is supposed to be a tmpfs mounted during early boot, -available and writable to for all tools at any time during bootup, -it replaces /var/run/, which should become a symlink some day. - -If /run does not exist, or is not writable, udev will fall back using -/dev/.udev/. - -On systemd systems with initramfs and LVM used, packagers must -make sure, that the systemd and initramfs versions match. The initramfs -needs to create the /run mountpoint for udev to store the data, and -mount this tmpfs to /run in the rootfs, so the that the udev database -is preserved for the udev version started in the rootfs. - -The command 'udevadm info --convert-db' is gone. The udev daemon -itself, at startup, converts any old database version if necessary. - -The systemd services files have been reorganized. The udev control -socket is bound by systemd and passed to the started udev daemon. -The udev-settle.service is no longer active by default. Services which -can not handle hotplug setups properly need to actively pull it in, to -act like a barrier. Alternatively the settle service can be unconditionally -'systemctl'enabled, and act like a barrier for basic.target. - -The fstab_import callout is no longer built or installed. Udev -should not be used to mount, does not watch changes to fstab, and -should not mirror fstab values in the udev database. - -udev 166 -======== -Bugfixes. - -New and updated keymaps. - -udev 165 -======== -Bugfixes. - -The udev database has changed, After installation of a new udev -version, 'udevadm info --convert-db' should be called, to let the new -udev/libudev version read the already stored data. - -udevadm now supports quoting of property values, and prefixing of -key names: - $ udevadm info --export --export-prefix=MY_ --query=property -n sda - MY_MAJOR='259' - MY_MINOR='0' - MY_DEVNAME='/dev/sda' - MY_DEVTYPE='disk' - ... - -libudev now supports: - udev_device_get_is_initialized() - udev_enumerate_add_match_is_initialized() -to be able to skip devices the kernel has created , but udev has -not already handled. - -libudev now supports: - udev_device_get_usec_since_initialized() -to retrieve the "age" of a udev device record. - -GUdev supports a more generic GUdevEnumerator class, udev TAG -handling, device initialization and timestamp now. - -The counterpart of /sys/dev/{char,block}/$major:$minor, -/dev/{char,block}/$major:$minor symlinks are now unconditionally -created, even when no rule files exist. - -New and updated keymaps. - -udev 164 -======== -Bugfixes. - -GUdev moved from /usr to /. - -udev 163 -======== -Bugfixes. - -udev 162 -======== -Bugfixes. - -Persistent network naming rules are disabled inside of Qemu/KVM now. - -New and updated keymaps. - -Udev gets unconditionally enabled on systemd installations now. There -is no longer the need to to run 'systemctl enable udev.service'. - -udev 161 -======== -Bugfixes. - -udev 160 -======== -Bugfixes. - -udev 159 -======== -Bugfixes. - -New and fixed keymaps. - -Install systemd service files if applicable. - -udev 158 -======== -Bugfixes. - -All distribution specific rules are removed from the udev source tree, -most of them are no longer needed. The Gentoo rules which allow to support -older kernel versions, which are not covered by the default rules anymore -has moved to rules/misc/30-kernel-compat.rules. - -udev 157 -======== -Bugfixes. - -The option --debug-trace and the environemnt variable UDEVD_MAX_CHILDS= -was removed from udevd. - -Udevd now checks the kernel commandline for the following variables: - udev.log-priority= - udev.children-max= - udev.exec-delay= -to help debuging coldplug setups where the loading of a kernel -module crashes the system. - -The subdirectory in the source tree rules/packages has been renamed to -rules/arch, anc contains only architecture specific rules now. - -udev 156 -======== -Bugfixes. - -udev 155 -======== -Bugfixes. - -Now the udev daemon itself, does on startup: - - copy the content of /lib/udev/devices to /dev - - create the standard symlinks like /dev/std{in,out,err}, - /dev/core, /dev/fd, ... - - use static node information provided by kernel modules - and creates these nodes to allow module on-demand loading - - possibly apply permissions to all ststic nodes from udev - rules which are annotated to match a static node - -The default mode for a device node is 0600 now to match the kernel -created devtmpfs defaults. If GROUP= is specified and no MODE= is -given the default will be 0660. - -udev 154 -======== -Bugfixes. - -Udev now gradually starts to pass control over the primary device nodes -and their names to the kernel, and will in the end only manage the -permissions of the node, and possibly create additional symlinks. -As a first step NAME="" will be ignored, and NAME= setings with names -other than the kernel provided name will result in a logged warning. -Kernels that don't provide device names, or devtmpfs is not used, will -still work as they did before, but it is strongly recommended to use -only the same names for the primary device node as the recent kernel -provides for all devices. - -udev 153 -======== -Fix broken firmware loader search path. - -udev 152 -======== -Bugfixes. - -"udevadm trigger" defaults to "change" events now instead of "add" -events. The "udev boot script" might need to add "--action=add" to -the trigger command if not already there, in case the initial coldplug -events are expected as "add" events. - -The option "all_partitons" was removed from udev. This should not be -needed for usual hardware. Udev can not safely make assumptions -about non-existing partition major/minor numbers, and therefore no -longer provide this unreliable and unsafe option. - -The option "ignore_remove" was removed from udev. With devtmpfs -udev passed control over device nodes to the kernel. This option -should not be needed, or can not work as advertised. Neither -udev nor the kernel will remove device nodes which are copied from -the /lib/udev/devices/ directory. - -All "add|change" matches are replaced by "!remove" in the rules and -in the udev logic. All types of events will update possible symlinks -and permissions, only "remove" is handled special now. - -The modem modeswitch extra was removed and the external usb_modeswitch -program should be used instead. - -New and fixed keymaps. - -udev 151 -======== -Bugfixes. - -udev 150 -======== -Bugfixes. - -Kernels with SYSFS_DEPRECATED=y are not supported since a while. Many users -depend on the current sysfs layout and the information not available in the -deprecated layout. All remaining support for the deprecated sysfs layout is -removed now. - -udev 149 -======== -Fix for a possible endless loop in the new input_id program. - -udev 148 -======== -Bugfixes. - -The option "ignore_device" does no longer exist. There is no way to -ignore an event, as libudev events can not be suppressed by rules. -It only prevented RUN keys from being executed, which results in an -inconsistent behavior in current setups. - -BUS=, SYSFS{}=, ID= are long deprecated and should be SUBSYSTEM(S)=, -ATTR(S){}=, KERNEL(S)=. It will cause a warning once for every rule -file from now on. - -The support for the deprecated IDE devices has been removed from the -default set of rules. Distros who still care about non-libata drivers -need to add the rules to the compat rules file. - -The ID_CLASS property on input devices has been replaced by the more accurate -set of flags ID_INPUT_{KEYBOARD,KEY,MOUSE,TOUCHPAD,TABLET,JOYSTICK}. These are -determined by the new "input_id" prober now. Some devices, such as touchpads, -can have several classes. So if you previously had custom udev rules which e. g. -checked for ENV{ID_CLASS}=="kbd", you need to replace this with -ENV{ID_INPUT_KEYBOARD}=="?*". - -udev 147 -======== -Bugfixes. - -To support DEVPATH strings larger than the maximum file name length, the -private udev database format has changed. If some software still reads the -private files in /dev/.udev/, which it shouldn't, now it's time to fix it. -Please do not port anything to the new format again, everything in /dev/.udev -is and always was private to udev, and may and will change any time without -prior notice. - -Multiple devices claiming the same names in /dev are limited to symlinks -only now. Mixing identical symlink names and node names is not supported. -This reduces the amount of data in the database significantly. - -NAME="%k" causes a warning now. It's is and always was completely superfluous. -It will break kernel supplied DEVNAMEs and therefore it needs to be removed -from all rules. - -Most NAME= instructions got removed. Kernel 2.6.31 supplies the needed names -if they are not the default. To support older kernels, the NAME= rules need to -be added to the compat rules file. - -Symlinks to udevadm with the old command names are no longer resolved to -the udevadm commands. - -The udev-acl tool got adopted to changes in ConsoleKit. Version 0.4.1 is -required now. - -The option "last_rule" does no longer exist. Its use breaks too many -things which expect to be run from independent later rules, and is an idication -that something needs to be fixed properly instead. - -The gudev API is no longer marked as experimental, -G_UDEV_API_IS_SUBJECT_TO_CHANGE is no longer needed. The gudev introspection -is enabled by default now. Various projects already depend on introspection -information to bind dynamic languages to the gudev interfaces. - -udev 146 -======== -Bugfixes. - -The udevadm trigger "--retry-failed" option, which is replaced since quite -a while by "--type=failed" is removed. - -The failed tracking was not working at all for a few releases. The RUN -option "ignore_error" is replaced by a "fail_event_on_error" option, and the -default is not to track any failing RUN executions. - -New keymaps, new modem, hid2hci updated. - -udev 145 -======== -Fix possible crash in udevd when worker processes are busy, rules are -changed at the same time, and workers get killed to reload the rules. - -udev 144 -======== -Bugfixes. - -Properties set with ENV{.FOO}="bar" are marked private by starting the -name with a '.'. They will not be stored in the database, and not be -exported with the event. - -Firmware files are looked up in: - /lib/firmware/updates/$(uname -r) - /lib/firmware/updates - /lib/firmware/$(uname -r) - /lib/firmware" -now. - -ATA devices switched the property from ID_BUS=scsi to ID_BUS=ata. -ata_id, instead of scsi_id, is the default tool now for ATA devices. - -udev 143 -======== -Bugfixes. - -The configure options have changed because another library needs to be -installed in a different location. Instead of exec_prefix and udev_prefix, -libdir, rootlibdir and libexecdir are used. The Details are explained in -the README file. - -Event processes now get re-used after they handled an event. This reduces -the number of forks and the pressure on the CPU significantly, because -cloned event processes no longer cause page faults in the main daemon. -After the events have settled, a few worker processes stay around for -future events, all others get cleaned up. - -To be able to use signalfd(), udev depends on kernel version 2.6.25 now. -Also inotify support is mandatory now to run udev. - -The format of the queue exported by the udev damon has changed. There is -no longer a /dev/.udev/queue/ directory. The current event queue can be -accessed with udevadm settle and libudedv. - -Libudev does not have the unstable API header anymore. From now on, -incompatible changes will be handled by bumping the library major version. - -To build udev from the git tree gtk-doc is needed now. The tarballs will -build without it and contain the pre-built documentation. An online copy -is available here: - http://www.kernel.org/pub/linux/utils/kernel/hotplug/libudev/ - -The tools from the udev-extras repository have been merged into the main -udev repository. Some of the extras have larger external dependencies, and -they can be disabled with the configure switch --disable-extras. - -udev 142 -======== -Bugfixes. - -The program vol_id and the library libvolume_id are removed from the -repository. Libvolume_id is merged with libblkid from the util-linux-ng -package. Persistent disk links for label and uuid depend on the -util-linux-ng version (2.15) of blkid now. Older versions of blkid -can not be used with udev. - -Libudev allows to subscribe to udev events. To prevent unwanted messages -to be delivered, and waking up the subscribing process, a filter can be -installed, to drop messages inside a kernel socket filter. The filters -match on the : properties of the device. - This is part of the ongoing effort to replace HAL, and switch current -users over to directly use libudev. - Libudev is still marked as experimental, and its interface might -eventually change if needed, but no major changes of the currently exported -interface are expected anymore, and a first stable release should happen -soon. - -A too old kernel (2.6.21) or a kernel with CONFIG_SYSFS_DEPRECATED -is not supported since while and udevd will log an error message at -startup. It should still be able to boot-up, but advanced rules and system -services which depend on the information not available in the old sysfs -format will fail to work correctly. - -DVB device naming is supplied by the kernel now. In case older kernels -need to be supported, the old shell script should be added to a compat -rules file. - -udev 141 -======== -Bugfixes. - -The processed udev events get send back to the netlink socket. Libudev -provides access to these events. This is work-in-progress, to replace -the DeviceKit daemon functionality directly with libudev. There are -upcoming kernel changes to allow non-root users to subcribe to these -events. - -udev 140 -======== -Bugfixes. - -"udevadm settle" now optionally accepts a range of events to wait for, -instead of waiting for "all" events. - -udev 139 -======== -Bugfixes. - -The installed watch for block device metadata changes is now removed -during event hadling, because some (broken) tools may be called from udev -rules and (wrongly) open the device with write access. After the finished -event handling the watch is restored. - -udev 138 -======== -Bugfixes. - -Device nodes can be watched for changes with inotify with OPTIONS="watch". -If closed after being opened for writing, a "change" uevent will occur. -/dev/disk/by-{label,uuid}/* symlinks will be automatically updated. - -udev 137 -======== -Bugfixes. - -The udevadm test command has no longer a --force option, nodes and symlinks -are always updated with a test run now. - -The udevd daemon can be started with --resolve-names=never to avoid all user -and group lookups (e.g. in cut-down systems) or --resolve-names=late to -lookup user and groups every time events are handled. - -udev 136 -======== -Bugfixes. - -We are currently merging the Ubuntu rules in the udev default rules, -and get one step closer to provide a common Linux /dev setup, regarding -device names, symlinks, and default device permissions. On udev startup, -we now expect the following groups to be resolvable to their ids with -glibc's getgrnam(): - disk, cdrom, floppy, tape, audio, video, lp, tty, dialout, kmem. -LDAP setups need to make sure, that these groups are always resolvable at -bootup, with only the rootfs mounted, and without network access available. - -Some systems may need to add some new, currently not used groups, or need -to add some users to new groups, but the cost of this change is minimal, -compared to the pain the current, rather random, differences between the -various distributions cause for upstream projects and third-party vendors. - -In general, "normal" users who log into a machine should never be a member -of any such group, but the device-access should be managed by dynamic ACLs, -which get added and removed for the specific users on login/logout and -session activity/inactivity. These groups are only provided for custom setups, -and mainly system services, to allow proper privilege separation. -A video-streaming daemon uid would be a member of "audio" and "video", to get -access to the sound and video devices, but no "normal" user should ever belong -to the "audio" group, because he could listen to the built-in microphone with -any ssh-session established from the other side of the world. - -/dev/serial/by-{id,path}/ now contains links for ttyUSB devices, -which do not depend on the kernel device name. As usual, unique -devices - only a single one per product connected, or a real -USB serial number in the device - are always found with the same -name in the by-id/ directory. -Completely identical devices may overwrite their names in by-id/ -and can only be found reliably in the by-path/ directory. Devices -specified by by-path/ must not change their connection, like the -USB port number they are plugged in, to keep their name. - -To support some advanced features, Linux 2.6.22 is the oldest supported -version now. The kernel config with enabled SYSFS_DEPRECATED is no longer -supported. Older kernels should still work, and devices nodes should be -reliably created, but some rules and libudev will not work correctly because -the old kernels do not provide the expected information or interfaces. - -udev 135 -======== -Bugfixes. - -Fix for a possible segfault while swapping network interface names in udev -versions 131-134. - -udev 134 -======== -Bugfixes. - -The group "video" is part of the default rules now. - -udev 133 -======== -Bugfix for kernels using SYSFS_DEPRECATED* option and finding parent -block devices in some cases. No common distro uses this option anymore, -and we do not get enough testing for this and recent udev versions. If -this option is not needed to run some old distro with a new kernel, -it should be disabled in the kernel config. - -Bugfix for the $links substitution variable, which may crash if no links -are created. This should not happen in usual setups because we always -create /dev/{block,char}/ links. - -The strings of the parsed rules, which are kept in memory, no longer -contain duplicate entries, or duplicate tails of strings. This, and the -new rules parsing/matching code reduces the total in-memory size of -a huge distro rule sets to 0.08 MB, compared to the 1.2MB of udev -version 130. - -The export of DEVTYPE=disk/partition got removed from the default -rules. This value is available from the kernel. The pnp shell script -modprobe hack is removed from the default rules. ACPI devices have _proper_ -modalias support and take care of the same functionality. -Installations which support old kernels, but install current default -udev rules may want to add that to the compat rules file. - -Libvolume_id now always probes for all known filesystems, and does not -stop at the first match. Some filesystems are marked as "exclusive probe", -and if any other filesytem type matches at the same time, libvolume_id -will, by default, not return any probing result. This is intended to prevent -mis-detection with conflicting left-over signatures found from earlier -file system formats. That way, we no longer depend on the probe-order -in case of multiple competing signatures. In some setups the kernel allows -to mount a volume with just the old filesystem signature still in place. -This may damage the new filesystem and cause data-loss, just by mounting -it. Because volume_id can not decide which one the correct signature is, -the wrong signatures need to be removed manually from the volume, or the -volume needs to be reformatted, to enable filesystem detection and possible -auto-mounting. - -udev 132 -======== -Fix segfault if compiled without optimization and dbg() does not get -compiled out and uses variables which are not available. - -udev 131 -======== -Bugfixes. (And maybe new bugs. :)) - -The rule matching engine got converted from a rule list to a token -array which reduced the in-memory rules representation of a full -featured distros with thousends of udev rules from 1.2MB to 0.12 MB. -Limits like 5 ENV and ATTR matches, and one single instance for most -other keys per rule are gone. - -The NAME assignment is no longer special cased. If later rules assign -a NAME value again, the former value will be overwritten. As usual -for most other keys, the NAME value can be protected by doing a final -assignment with NAME:="". - -All udev code now uses libudev, which is also exported. The library -is still under development, marked as experimental, and its interface -may change as long as the DeviceKit integration is not finished. - -Many thanks to Alan Jenkins for his continuous help, and finding and -optimizing some of the computing expensive parts. - -udev 130 -======== -Bugfixes. - -Kernel devices and device nodes are connected now by reverse indizes in -/sys and /dev. A device number retrieved by a stat() or similar, the -kernel device directory can be found by looking up: - /sys/dev/{block,char}/: -and the device node of the same device by looking up: - /dev/{block,char}/: - -udev 129 -======== -Fix recently introduced bug, which caused a compilation without large -file support, where vol_id does not recognize raid signatures at the end -of a volume. - -Firewire disks now create both, by-id/scsi-* and by-id/ieee-* links. -Seems some kernel versions prevent the creation of the ieee-* links, -so people used the scsi-* link which disappeared now. - -More libudev work. Almost all udevadm functionality comes from libudev -now. - -udevadm trigger has a new option --type, which allows to trigger events -for "devices", for "subsystems", or "failed" devices. The old option ---retry-failed" still works, but is no longer mentioned in the man page. - -udev 128 -======== -Bugfixes. - -The udevadm info --device-id-of-file= output has changed to use -the obvious format. Possible current users should use the --export -option which is not affected. - -The old udev commands symlinks to udevadm are not installed, if -these symlinks are used, a warning is printed. - -udev 127 -======== -Bugfixes. - -Optical drive's media is no longer probed for raid signatures, -reading the end of the device causes some devices to malfunction. -Also the offset of the last session found is used now to probe -for the filesystem. - -The volume_id library got a major version number update to 1, -some deprecated functions are removed. - -A shared library "libudev" gets installed now to provide access -to udev device information. DeviceKit, the successor of HAL, will -need this library to access the udev database and search sysfs for -devices. -The library is currently in an experimental state, also the API is -expected to change, as long as the DeviceKit integration is not -finished. - -udev 126 -======== -We use ./configure now. See INSTALL for details. Current -options are: - --prefix= - "/usr" - prefix for man pages, include files - --exec-prefix= - "" - the root filesystem, prefix for libs and binaries - --sysconfdir= - "/etc" - --with-libdir-name= - "lib" - directory name for libraries, not a path name - multilib 64bit systems may use "lib64" instead of "lib" - --enable-debug - compile-in verbose debug messages - --disable-logging - disable all logging and compile-out all log strings - --with-selinux - link against SELInux libraries, to set the expected context - for created files - -In the default rules, the group "disk" gets permissions 0660 instead -of 0640. One small step closer to unify distro rules. Some day, all -distros hopefully end up with the same set of rules. - -No symlinks to udevadm are installed anymore, if they are still needed, -they should be provided by the package. - -udev 125 -======== -Bugfixes. - -Default udev rules, which are not supposed to be edited by the user, should -be placed in /lib/udev/rules.d/ now, to make it clear that they are private to -the udev package and will be replaced with an update. Udev will pick up rule -files from: - /lib/udev/rules.d/ - default installed rules - /etc/udev/rules.d/ - user rules + on-the-fly generated rules - /dev/.udev/rules.d/ - temporary non-persistent rules created after bootup -It does not matter in which directory a rule file lives, all files are sorted -in lexical order. - -To help creating /dev/root, we have now: - $ udevadm info --export --export-prefix="ROOT_" --device-id-of-file=/ - ROOT_MAJOR=8 - ROOT_MINOR=5 -In case the current --device-id-of-file is already used, please switch to -the --export format version, it saves the output parsing and the old -format will be changed to use ':' as a separator, like the format in the -sysfs 'dev' file. - -udev 124 -======== -Fix cdrom_id to properly recognize blank media. - -udev 123 -======== -Bugfixes. - -Tape drive id-data is queried from /dev/bsg/* instead of the tape -nodes. This avoids rewinding tapes on open(). - -udev 122 -======== -Bugfixes. - -The symlinks udevcontrol and udevtrigger are no longer installed by -the Makefile. - -The scsi_id program does not depend on sysfs anymore. It can speak -SGv4 now, so /dev/bsg/* device nodes can be used, to query SCSI device -data, which should solve some old problems with tape devices, where -we better do not open all tape device nodes to identify the device. - -udev 121 -======== -Many bugfixes. - -The cdrom_id program is replaced by an advanced version, which can -detect most common device types, and also properties of the inserted -media. This is part of moving some basic functionality from HAL into -udev (and the kernel). - -udev 120 -======== -Bugfixes. - -The last WAIT_FOR_SYSFS rule is removed from the default rules. - -The symlinks to udevadm for the debugging tools: udevmonitor and -udevtest are no longer created. - -The symlinks to the udevadm man page for the old tool names are -no longer created. - -Abstract namespace sockets paths in RUN+="socket:@" rules, -should be prefixed with '@' to indicate that the path is not a -real file. - -udev 119 -======== -Bugfixes. - -udev 118 -======== -Bugfixes. - -Udevstart is removed from the tree, it did not get installed for -a long time now, and is long replaced by trigger and settle. - -udev 117 -======== -Bugfixes. - -All udev tools are merged into a single binary called udevadm. -The old names of the tools are built-in commands in udevadm now. -Symlinks to udevadm, with the names of the old tools, provide -the same functionality as the standalone tools. There is also -only a single udevadm.8 man page left for all tools. - -Tools like mkinitramfs should be checked, if they need to include -udevadm in the list of files. - -udev 116 -======== -Bugfixes. - -udev 115 -======== -Bugfixes. - -The etc/udev/rules.d/ directory now contains a default set of basic -udev rules. This initial version is the result of a rules file merge -of Fedora and openSUSE. For these both distros only a few specific -rules are left in their own file, named after the distro. Rules which -are optionally installed, because they are only valid for a specific -architecture, or rules for subsystems which are not always used are -in etc/udev/packages/. - -udev 114 -======== -Bugfixes. - -Dynamic rules can be created in /dev/.udev/rules.d/ to trigger -actions by dynamically created rules. - -SYMLINK=="" matches agains the entries in the list of -currently defined symlinks. The links are not created in the -filesystem at that point in time, but the values can be matched. - -RUN{ignore_error}+="" will ignore any exit code from the -program and not record as a failed event. - -udev 113 -======== -Bugfixes. - -Final merge of patches/features from the Ubuntu package. - -udev 112 -======== -Bugfixes. - -Control characters in filesystem label strings are no longer silenty -removed, but hex-encoded, to be able to uniquely identify the device -by its symlink in /dev/disk/by-label/. -If libvolume_id is used by mount(8), LABEL= will work as expected, -if slashes or other characters are used in the label string. - -To test the existence of a file, TEST=="" and TEST!="" -can be specified now. The TEST key accepts an optional mode mask -TEST{0100}=="". - -Scsi_id now supports a mode without expecting scsi-specific sysfs -entries to allow the extraction of cciss-device persistent properties. - -udev 111 -======== -Bugfixes. - -In the future, we may see uuid's which are just simple character -strings (see the DDF Raid Specification). For that reason vol_id now -exports ID_FS_UUID_SAFE, just like ID_FS_LABEL_SAFE. For things like -the creation of symlinks, the *_SAFE values ensure, that no control -or whitespace characters are used in the filename. - -Possible users of libvolume_id, please use the volume_id_get_* functions. -The public struct will go away in a future release of the library. - -udev 110 -======== -Bugfixes. - -Removal of useless extras/eventrecorder.sh. - -udev 109 -======== -Bugfixes. - -udev 108 -======== -Bugfixes. - -The directory multiplexer for dev.d/ and hotplug.d are finally removed -from the udev package. - -udev 107 -======== -Bugfixes. - -Symlinks can have priorities now, the priority is assigned to the device -and specified with OPTIONS="link_priority=100". Devices with higher -priorities overwrite the symlinks of devices with lower priorities. -If the device that currently owns the link, goes away, the symlink -will be removed, and recreated, pointing to the next device with the -highest actual priority. This should make /dev/disk/by-{label,uuid,id} -more reliable, if multiple devices contain the same metadata and overwrite -these symlinks. - -The dasd_id program is removed from the udev tree, and dasdinfo, with the -needed rules, are part of the s390-tools now. - -Please add KERNEL=="[0-9]*:[0-9]*" to the scsi wait-for-sysfs rule, -we may get the scsi sysfs mess fixed some day, and this will only catch -the devices we are looking for. - -USB serial numbers for storage devices have the target:lun now appended, -to make it possibble to distinguish broken multi-lun devices with all -the same SCSI identifiers. - -Note: The extra "run_directory" which searches and executes stuff in -/etc/hotplug.d/ and /etc/dev.d/ is long deprecated, and will be removed -with the next release. Make sure, that you don't use it anymore, or -provides your own implementation of that inefficient stuff. -We are tired of reports about a "slow udev", because these directories -contain stuff, that runs with _every_ event, instead of using rules, -that run programs only for the matching events. - -udev 106 -======== -Bugfixes. - -udev 105 -======== -Bugfixes. - -DRIVER== will match only for devices that actually have a real -driver. DRIVERS== must be used, if parent devices should be -included in the match. - -Libvolume_id's "linux_raid" detection needed another fix. - -udev 104 -======== -Bugfixes. - -udev 103 -======== -Add additional check to volume_id detection of via_raid, cause -some company decided to put a matching pattern all over the empty -storage area of their music players. - -udev 102 -======== -Fix path_id for SAS devices. - -udev 101 -======== -The udev daemon can be started with --debug-trace now, which will -execute all events serialized to get a chance to catch a possible -action that crashes the box. - -A warning is logged, if PHYSDEV* keys, the "device" link, or a parent -device attribute like $attr{../file} is used, only WAIT_FOR_SYSFS rules -are excluded from the warning. Referencing parent attributes directly -may break when something in the kernel driver model changes. Udev will -just find the attribute by walking up the parent chain. - -Udevtrigger now sorts the list of devices depending on the device -dependency, so a "usb" device is triggered after the parent "pci" -device. - -udev 100 -======== -Revert persistent-storage ata-serial '_' '-' replacement. - -udev 099 -======== -Bugfixes. - -Udevtrigger can now filter the list of devices to be triggered. Matches -for subsystems or sysfs attributes can be specified. - -The entries in /dev/.udev/queue and /dev/.udev/failed have changed to -zero-sized files to avoid pointing to /sys and confuse broken tools which -scan the /dev directory. To retry failed events, udevtrigger --retry-failed -should be used now. - -The rules and scripts to create udev rules for persistent network -devices and optical drives are in the extras/rules_generator directory -now. If you use something similar, please consider replacing your own -version with this, to share the support effort. The rule_generator -installs its own rules into /etc/udev/rules.d. - -The cdrom_id tool installs its own rule now in /etc/udev/rules.d, cause -the rule_generator depends on cdrom_id to be called in an earlier rule. - -udev 098 -======== -Bugfixes. - -Renaming of some key names (the old names still work): -BUS -> SUBSYSTEMS, ID -> KERNELS, SYSFS -> ATTRS, DRIVER -> DRIVERS. -(The behavior of the key DRIVER will change soon in one of the next -releases, to match only the event device, please switch to DRIVERS -instead. If DRIVER is used, it will behave like DRIVERS, but an error -is logged. -With the new key names, we have a more consistent and simpler scheme. -We can match the properties of the event device only, with: KERNEL, -SUBSYSTEM, ATTR, DRIVER. Or include all the parent devices in the match, -with: KERNELS, SUBSYSTEMS, ATTRS, DRIVERS. ID, BUS, SYSFS, DRIVER are no -longer mentioned in the man page and should be switched in the rule -files. - -ATTR{file}="value" can be used now, to write to a sysfs file of the -event device. Instead of: - ..., SYSFS{type}=="0|7|14", RUN+="/bin/sh -c 'echo 60 > /sys$$DEVPATH/timeout'" -we now can do: - ..., ATTR{type}=="0|7|14", ATTR{timeout}="60" - -All the PHYSDEV* keys are deprecated and will be removed from a -future kernel: - PHYDEVPATH - is the path of a parent device and should not be - needed at all. - PHYSDEVBUS - is just a SUBSYSTEM value of a parent, and can be - matched with SUBSYSTEMS== - PHYSDEVDRIVER - for bus devices it is available as ENV{DRIVER}. - Newer kernels will have DRIVER in the environment, - for older kernels udev puts in. Class device will - no longer carry this property of a parent and - DRIVERS== can be used to match such a parent value. -Note that ENV{DRIVER} is only available for a few bus devices, where -the driver is already bound at device event time. On coldplug, the -events for a lot devices are already bound to a driver, and they will have -that value set. But on hotplug, at the time the kernel creates the device, -it can't know what driver may claim the device after that, therefore -in most cases it will be empty. - -Failed events should now be re-triggered with: - udevtrigger --retry-failed. -Please switch to this command, so we keep the details of the /dev/.udev/failed/ -files private to the udev tools. We may need to switch the current symlink -target, cause some obviously broken tools try to scan all files in /dev -including /dev/.udev/, find the links to /sys and end up stat()'ing sysfs files -million times. This takes ages on slow boxes. - -The udevinfo attribute walk (-a) now works with giving a device node -name (-n) instead of a devpath (-p). The query now always works, also when -no database file was created by udev. - -The built-in /etc/passwd /etc/group parser is removed, we always depend on -getpwnam() and getgrnam() now. One of the next releases will depend on -fnmatch() and may use getopt_long(). - -udev 097 -======== -Bugfixes and small improvements. - -udev 096 -======== -Fix path_id for recent kernels. - -udev 095 -======== -%e is finally gone. - -Added support for swapping network interface names, by temporarily -renaming the device and wait for the target name to become free. - -udev 094 -======== -The built-in MODALIAS key and substitution is removed. - -udev 093 -======== -The binary firmware helper is replaced by the usual simple -shell script. Udevsend is removed from the tree. - -udev 092 -======== -Bugfix release. - -udev 091 -======== -Some more keys require the correct use of '==' and '=' depending -on the kind of operation beeing an assignment or a match. Rules -with invalid operations are skipped and logged to syslog. Please -test with udevtest if the parsing of your rules throws errors and -fix possibly broken rules. - -udev 090 -======== -Provide "udevsettle" to wait for all current udev events to finish. -It also watches the current kernel netlink queue by comparing the -even sequence number to make sure that there are no current pending -events that have not already arrived in the daemon. - -udev 089 -======== -Fix rule to skip persistent rules for removable IDE devices, which -also skipped optical IDE drives. - -All *_id program are installed in /lib/udev/ by default now. - -No binary is stripped anymore as this should be done in the -packaging process and not at build time. - -libvolume_id is provided as a shared library now and vol_id is -linked against it. Also one of the next HAL versions will require -this library, and the HAL build process will also require the -header file to be installed. The copy of the same code in HAL will -be removed to have only a single copy left on the system. - -udev 088 -======== -Add persistent links for SCSI tapes. The rules file is renamed -to 60-persistent-storage.rules. - -Create persistent path for usb devices. Can be used for all sorts -of devices that can't be distinguished by other properties like -multiple identical keyboards and mice connected to the same box. - -Provide "udevtrigger" program to request events on coldplug. The -shell script is much too slow with thousends of devices. - -udev 087 -======== -Fix persistent disk rules to exclude removable IDE drives. - -Warn if %e, $modalias or MODALIAS is used. - -udev 086 -======== -Fix queue export, which wasn't correct for subsequent add/remove -events for the same device. - -udev 085 -======== -Fix cramfs detection on big endian. - -Make WAIT_FOR_SYSFS usable in "normal" rules and silent if the whole -device goes away. - -udev 084 -======== -If BUS== and SYSFS{}== have been used in the same rule, the sysfs -attributes were only checked at the parent device that matched the -by BUS requested subsystem. Fix it to also look at the device we -received the event for. - -Build variable CROSS has changed to CROSS_COMPILE to match the kernel -build name. - -udev 083 -======== -Fix a bug where NAME="" would prevent RUN from beeing executed. - -RUN="/bin/program" does not longer automatically add the subsystem -as the first parameter. This is from the days of /sbin/hotplug -which is dead now and it's just confusing to need to add a space at -the end of the program name to prevent this. -If you use rules that need the subsystem as the first parameter, -like the old "udev_run_hotlugd" and "udev_run_devd", add the subsystem -to the key like RUN+="/bin/program $env{SUBSYSTEM}". - -udev 082 -======== -The udev man page has moved to udev(7) as it does not describe a command -anymore. The programs udev, udevstart and udevsend are no longer installed -by default and must be copied manually, if they should be installed or -included in a package. - -Fix a bug where "ignore_device" could run earlier collected RUN keys before -the ignore rule was applied. - -More preparation for future sysfs changes. usb_id and scsi_id no longer -depend on a magic order of devices in the /devices chain. Specific devices -should be requested by their subsytem. - -This will always find the scsi parent device without depending on a specific -path position: - dev = sysfs_device_get(devpath); - dev_usb = sysfs_device_get_parent_with_subsystem(dev, "scsi"); - -The "device" link in the current sysfs layout will be automatically -_resolved_ as a parent and in the new sysfs layout it will just _be_ the -parent in the devpath. If a device is requested by it's symlink, like all -class devices in the new sysfs layout will look like, it gets automatically -resolved and substituted with the real devpath and not the symlink path. - -Note: -A similar logic must be applied to _all_ sysfs users, including -scripts, that search along parent devices in sysfs. The explicit use of -the "device" link must be avoided. With the future sysfs layout all -DEVPATH's will start with /devices/ and have a "subsystem" symlink poiting -back to the "class" or the "bus". The layout of the parent devices in -/devices is not necessarily expected to be stable across kernel releases and -searching for parents by their subsystem should make sysfs users tolerant -for changed parent chains. - -udev 081 -======== -Prepare udev to work with the experimental kernel patch, that moves -/sys/class devices to /sys/devices and /sys/block to /sys/class/block. - -Clarify BUS, ID, $id usage and fix $id behavior. This prepares for -moving the class devices to /sys/devices. - -Thanks again to Marco for help finding a hopefully nice compromise -to make %b simpler and working again. - -udev 080 -======== -Complete removal of libsysfs, replaced by simple helper functions -which are much simpler and a bit faster. The udev daemon operatesentirely -on event parameters and does not use sysfs for simple rules anymore. -Please report any new bugs/problems, that may be caused by this big -change. They will be fixed immediately. - -The enumeration format character '%e' is deprecated and will be -removed sometimes from a future udev version. It never worked correctly -outside of udevstart, so we can't use it with the new parallel -coldplug. A simple enumeration is as useless as the devfs naming -scheme, just get rid of both if you still use it. - -MODALIAS and $modalias is not needed and will be removed from one of -the next udev versions, replace it in all rules with ENV{MODALIAS} or -the sysfs "modalias" value. - -Thanks a lot to Marco for all his help on finding and fixing bugs. - -udev 079 -======== -Let scsi_id request libata drive serial numbers from page 0x80. - -Renamed etc/udev/persistent.rules to persistent-disk.rules and -added /dev/disk/by-name/* for device mapper device names. - -Removed %e from the man page. It never worked reliably outside -of udevstart and udevstart is no longer recommended to use. - -udev 078 -======== -Symlinks are now exported to the event environment. Hopefully it's no -longer needed to run udevinfo from an event process, like it was -mentioned on the hotplug list: - UDEV [1134776873.702967] add@/block/sdb - ... - DEVNAME=/dev/sdb - DEVLINKS=/dev/disk/by-id/usb-IBM_Memory_Key_0218B301030027E8 /dev/disk/by-path/usb-0218B301030027E8:0:0:0 - -udev 077 -======== -Fix a problem if udevsend is used as the hotplug handler and tries to use -syslog, which causes a "vc" event loop. 2.6.15 will make udevsend obsolete -and this kind of problems will hopefully go away soon. - -udev 076 -======== -All built-in logic to work around bad sysfs timing is removed with this -version. The need to wait for sysfs files is almost fixed with a kernel -version that doesn't work with this udev version anyway. Until we fix -the timing of the "bus" link creation, the former integrated logic should -be emulated by a rule placed before all other rules: - ACTION=="add", DEVPATH=="/devices/*", ENV{PHYSDEVBUS}=="?*", WAIT_FOR_SYSFS="bus" - -The option "udev_db" does no longer exist. All udev state will be in -/$udev_root/.udev/ now, there is no longer an option to set this -to anything else. -If the init script or something else used this value, just depend on -this hardcoded path. But remember _all_content_ of this directory is -still private to udev and can change at any time. - -Default location for rule sripts and helper programs is now: /lib/udev/. -Everything that is not useful on the commandline should go into this -directory. Some of the helpers in the extras folder are installed there -now. The rules need to be changed, to find the helpers there. - -Also /lib/udev/devices is recommended as a directory where packages or -the user can place real device nodes, which get copied over to /dev at -every boot. This should replace the various solutions with custom config -files. - -Udevsend does no longer start the udev daemon. This must be done with -the init script that prepares /dev on tmpfs and creates the initial nodes, -before starting the daemon. - -udev 075 -======== -Silent a too verbose error logging for the old hotplug.d/ dev.d/ -emulation. - -The copy of klibc is removed. A systemwide installed version of klibc -should be used to build a klibc udev now. - -udev 074 -======== -NAME="" will not create any nodes, but execute RUN keys. To completely -ignore an event the OPTION "ignore_device" should be used. - -After removal of the reorder queue, events with a TIMEOUT can be executed -without any queuing now. - -udev 073 -======== -Fixed bug in udevd, if inotify is not available. We depend on netlink -uevents now, kernels without that event source will not work with that -version of udev anymore. - -udev 072 -======== -The rule parsing happens now in the daemon once at startup, all udev -event processes inherit the already parsed rules from the daemon. -It is shipped with SUSE10.0 and reduces heavily the system load at -startup. The option to save precompiled rules and let the udev process -pick the them up is removed, as it's no longer needed. - -Kernel 2.6.15 will have symlinks at /class/input pointing to the real -device. Libsysfs is changed to "translate" the requested link into the -real device path, as it would happen with the hotplug event. Otherwise -device removal and the udev database will not work. - -Using 'make STRIPCMD=' will leave the binaries unstripped for debugging -and packaging. - -A few improvements for vol_id, the filesytem probing code. - -udev 071 -======== -Fix a stupid typo in extras/run_directory for "make install". - -scsi_id creates the temporary devnode now in /dev for usage with a -non-writable /tmp directory. - -The uevent kernel socket buffer can carry app. 50.000 events now, -let's see who can break this again. :) - -The upcoming kernel will have a new input driver core integration. -Some class devices are now symlinks to the real device. libsysfs -needs a fix for this to work correctly. Udevstart of older udev -versions will _not_ create these devices! - -udev 070 -======== -Fix a 'install' target in the Makefile, that prevents EXTRAS from -beeing installed. - -udev 069 -======== -A bunch of mostly trivial bugfixes. From now on no node name or -symlink name can contain any character than plain whitelisted ascii -characters or validated utf8 byte-streams. This is needed for the -/dev/disk/by-label/* links, because we import untrusted data and -export it to the filesystem. - -udev 068 -======== -More bugfixes. If udevd was started from the kernel, we don't -have stdin/stdout/stderr, which broke the forked tools in some -situations. - -udev 067 -======== -Bugfix. udevstart event ordering was broken for a long time. -The new run_program() uncovered it, because /dev/null was not -available while we try to run external programs. -Now udevstart should create it before we run anything. - -udev 066 -======== -Minor bugfixes and some distro rules updates. If you don't have the -persistent disk rules in /dev/disk/by-*/* on your distro, just -grab it from here. :) - -udev 065 -======== -We can use socket communication now to pass events from udev to -other programs: - RUN+="socket:/org/freedesktop/hal/udev_event" -will pass the whole udev event to the HAL daemon without the need -for a forked helper. (See ChangeLog for udevmonitor, as an example) - -udev 064 -======== -Mostly bugfixes and see ChangeLog. - -The test for the existence of an environment value should be -switched from: - ENV{KEY}=="*" to ENV{KEY}=="?*" -because "*" will not fail anymore, if the key does not exist or -is empty. - -udev 063 -======== -Bugfixes and a few tweaks described in the ChangeLog. - -udev 062 -======== -Mostly a Bugfix release. - -Added WAIT_FOR_SYSFS="" to be able to fight against the sysfs -timing with custom rules. - -udev 061 -======== -We changed the internal rule storage format. Our large rule files took -2 MB of RAM, with the change we are down to 99kB. - -If the device-node has been created with default name and no symlink or -options are to remenber, it is not longer stored in the udevdb. HAL will -need to be updated to work correctly with that change. - -To overrride optimization flags, OPTFLAGS may be used now. - -udev 060 -======== -Bugfix release. - -udev 059 -======== -Major changes happened with this release. The goal is to take over the -complete kernel-event handling and provide a more efficient way to dispatch -kernel events. Replacing most of the current shell script logic and the -kernel forked helper with a netlink-daemon and a rule-based event handling. - -o udevd listens to netlink events now. The first valid netlink event - will make udevd ignore any message from udevsend that contains a - SEQNUM, to avoid duplicate events. The forked events can be disabled - with: - echo "" > /proc/sys/kernel/hotplug - For full support, the broken input-subsytem needs to be fixed, not to - bypass the driver core. - -o /etc/dev.d/ + /etc/hotplug.d/ directory multiplexing is completely - removed from udev itself and must be emulated by calling small - helper binaries provided in the extras folder: - make EXTRAS=extras/run_directory/ - will build udev_run_devd and udev_run_hotplugd, which can be called - from a rule if needed: - RUN+="/sbin/udev_run_hotplugd" - The recommended way to handle this is to convert all the calls from - the directories to explicit udev rules and get completely rid of the - multiplexing. (To catch a ttyUSB event, you now no longer need to - fork and exit 300 tty script instances you are not interested in, it - is just one rule that matches exactly the device.) - -o udev handles now _all_ events not just events for class and block - devices, this way it is possible to control the complete event - behavior with udev rules. Especially useful for rules like: - ACTION="add", DEVPATH="/devices/*", MODALIAS=="?*", RUN+="/sbin/modprobe $modalias" - -o As used in the modalias rule, udev supports now textual - substitution placeholder along with the usual format chars. This - needs to be documented, for now it's only visible in udev_rules_parse.c. - -o The rule keys support now more operations. This is documented in the - man page. It is possible to add values to list-keys like the SYMLINK - and RUN list with KEY+="value" and to clear the list by assigning KEY="". - Also "final"-assignments are supported by using KEY:="value", which will - prevent changing the key by any later rule. - -o kernel 2.6.12 has the "detached_state" attribute removed from - sysfs, which was used to recognize sysfs population. We switched that - to wait for the "bus" link, which is only available in kernels after 2.6.11. - Running this udev version on older kernels may cause a short delay for - some events. - -o To provide infrastructure for persistent device naming, the id programs: - scsi_id, vol_id (former udev_volume_id), and ata_id (new) are able now - to export the probed data in environment key format: - pim:~ # /sbin/ata_id --export /dev/hda - ID_MODEL=HTS726060M9AT00 - ID_SERIAL=MRH401M4G6UM9B - ID_REVISION=MH4OA6BA - - The following rules: - KERNEL="hd*[!0-9]", IMPORT="/sbin/ata_id --export $tempnode" - KERNEL="hd*[!0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_MODEL}_$env{ID_SERIAL}" - - Will create: - kay@pim:~> tree /dev/disk - /dev/disk - |-- by-id - | |-- HTS726060M9AT00_MRH401M4G6UM9B -> ../../hda - | `-- IBM-Memory_Key -> ../../sda - |-- by-label - | |-- swap -> ../../hda1 - | |-- date -> ../../sda1 - | `-- home -> ../../hda3 - `-- by-uuid - |-- 2E08712B0870F2E7 -> ../../hda3 - |-- 9352cfef-7687-47bc-a2a3-34cf136f72e1 -> ../../hda1 - |-- E845-7A89 -> ../../sda1 - `-- b2a61681-3812-4f13-a4ff-920d70604299 -> ../../hda2 - - The IMPORT= operation will import these keys in the environment and make - it available for later PROGRAM= and RUN= executed programs. The keys are - also stored in the udevdb and can be queried from there with one of the - next udev versions. - -o A few binaries are silently added to the repository, which can be used - to replay kernel events from initramfs instead of using coldplug. udevd - can be instructed now to queue-up events while the stored events from - initramfs are filled into the udevd-queue. This code is still under - development and there is no documentation now besides the code itself. - The additional binaries get compiled, but are not installed by default. - -o There is also a temporary fix for a performance problem where too many - events happen in parallel and every event needs to parse the rules. - udev can now read precompiled rules stored on disk. This is likely to be - replaced by a more elegant solution in a future udev version. - -udev 058 -======== -With kernel version 2.6.12, the sysfs file "detached_state" was removed. -Fix for libsysfs not to expect this file was added. - -udev 057 -======== -All rules are applied now, but only the first matching rule with a NAME-key -will be applied. All later rules with NAME-key are completely ignored. This -way system supplied symlinks or permissions gets applied to user-defined -naming rules. - -Note: -Please check your rules setup, if you may need to add OPTIONS="last_rule" -to some rules, to keep the old behavior. - -The rules are read on "remove"-events too. That makes is possible to match -with keys that are available on remove (KERNEL, SUBSYSTEM, ID, ENV, ...) to -instruct udev to ignore an event (OPTIONS="ignore_device"). -The new ACTION-key may be used to let a rule act only at a "remove"-event. - -The new RUN-key supports rule-based execution of programs after device-node -handling. This is meant as a general replacement for the dev.d/-directories -to give fine grained control over the execution of programs. - -The %s{}-sysfs format char replacement values are searched at any of the -devices in the device chain now, not only at the class-device. - -We support log priority levels now. The value udev_log in udev.conf is used -to determine what is printed to syslog. This makes it possible to -run a version with compiled-in debug messages in a production environment -which is sometimes needed to find a bug. -It is still possible to supress the inclusion of _any_ syslog usage with -USE_LOG=false to create the smallest possible binaries if needed. -The configured udev_log value can be overridden with the environment variable -UDEV_LOG. - -udev 056 -======== -Possible use of a system-wide klibc: - make USE_KLIBC=true KLCC=/usr/bin/klcc all -will link against an external klibc and our own version will be ignored. - -udev 055 -======== -We support an unlimited count of symlinks now. - -If USE_STATIC=true is passed to a glibc build, we link statically and use -a built-in userdb parser to resolve user and group names. - -The PLACE= key is gone. It can be replaced by an ID= for a long time, because -we walk up the chain of physical devices to find a match. - -The KEY="" format supports '=', '==', '!=,' , '+=' now. This makes it -easy to skip certain attribute matches without composing rules with weird -character class negations like: - KERNEL="[!s][!c][!d]*" -this can now be replaced with: - KERNEL!="scd*" -The current simple '=' is still supported, and should work as it does today, -but existing rules should be converted if possible, to be better readable. - -We have new ENV{}== key now, to match against a maximum of 5 environment -variables. - -udevstart is its own binary again, because we don't need co carry this araound -with every forked event. diff --git a/src/udev/README b/src/udev/README deleted file mode 100644 index 38459c6b22..0000000000 --- a/src/udev/README +++ /dev/null @@ -1,101 +0,0 @@ -udev - Linux userspace device management - -Integrating udev in the system has complex dependencies and may differ from -distribution to distribution. A system may not be able to boot up or work -reliably without a properly installed udev version. The upstream udev project -does not recommend replacing a distro's udev installation with the upstream -version. - -The upstream udev project's set of default rules may require a most recent -kernel release to work properly. - -Tools and rules shipped by udev are not public API and may change at any time. -Never call any private tool in /usr/lib/udev from any external application; it -might just go away in the next release. Access to udev information is only offered -by udevadm and libudev. Tools and rules in /usr/lib/udev and the entire contents -of the /run/udev directory are private to udev and do change whenever needed. - -Requirements: - - Version 2.6.34 of the Linux kernel with sysfs, procfs, signalfd, inotify, - unix domain sockets, networking and hotplug enabled - - - Some architectures might need a later kernel, that supports accept4(), - or need to backport the accept4() syscall wiring in the kernel. - - - These options are required: - CONFIG_DEVTMPFS=y - CONFIG_HOTPLUG=y - CONFIG_INOTIFY_USER=y - CONFIG_NET=y - CONFIG_PROC_FS=y - CONFIG_SIGNALFD=y - CONFIG_SYSFS=y - CONFIG_SYSFS_DEPRECATED*=n - CONFIG_UEVENT_HELPER_PATH="" - - - These options might be needed: - CONFIG_BLK_DEV_BSG=y (SCSI devices) - CONFIG_TMPFS_POSIX_ACL=y (user ACLs for device nodes) - - - The /dev directory needs the 'devtmpfs' filesystem mounted. - Udev only manages the permissions and ownership of the - kernel-provided device nodes, and possibly creates additional symlinks. - - - Udev requires /run to be writable, which is usually done by mounting a - 'tmpfs' filesystem. - - - This version of udev does not work properly with the CONFIG_SYSFS_DEPRECATED* - option enabled. - - - The deprecated hotplug helper /sbin/hotplug should be disabled in the - kernel configuration, it is not needed today, and may render the system - unusable because the kernel may create too many processes in parallel - so that the system runs out-of-memory. - - - The proc filesystem must be mounted on /proc, and the sysfs filesystem must - be mounted at /sys. No other locations are supported by a standard - udev installation. - - - The default rule sset requires the following group names resolvable at udev startup: - disk, cdrom, floppy, tape, audio, video, lp, tty, dialout, and kmem. - Especially in LDAP setups, it is required that getgrnam() be able to resolve - these group names with only the rootfs mounted and while no network is - available. - - - Some udev extras have external dependencies like: - libglib2, usbutils, pciutils, and gperf. - All these extras can be disabled with configure options. - -Setup: - - The udev daemon should be started to handle device events sent by the kernel. - During bootup, the events for already existing devices can be replayed, so - that they are configured by udev. The systemd service files contain the - needed commands to start the udev daemon and the coldplug sequence. - - - Restarting the daemon never applies any rules to existing devices. - - - New/changed rule files are picked up automatically; there is usually no - daemon restart or signal needed. - -Operation: - - Based on events the kernel sends out on device creation/removal, udev - creates/removes device nodes and symlinks in the /dev directory. - - - All kernel events are matched against a set of specified rules, which - possibly hook into the event processing and load required kernel - modules to set up devices. For all devices, the kernel exports a major/minor - number; if needed, udev creates a device node with the default kernel - device name. If specified, udev applies permissions/ownership to the device - node, creates additional symlinks pointing to the node, and executes - programs to handle the device. - - - The events udev handles, and the information udev merges into its device - database, can be accessed with libudev: - http://www.kernel.org/pub/linux/utils/kernel/hotplug/libudev/ - http://www.kernel.org/pub/linux/utils/kernel/hotplug/gudev/ - -For more details about udev and udev rules, see the udev man pages: - http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev/ - -Please direct any comment/question to the linux-hotplug mailing list at: - linux-hotplug@vger.kernel.org diff --git a/src/udev/TODO b/src/udev/TODO deleted file mode 100644 index 8b8b9c8f8c..0000000000 --- a/src/udev/TODO +++ /dev/null @@ -1,22 +0,0 @@ - - find a way to tell udev to not cancel firmware - requests in initramfs - - - scsi_id -> sg3_utils? - - - make gtk-doc optional like kmod - - - move /usr/lib/udev/devices/ to tmpfiles - - - trigger --subsystem-match=usb/usb_device - - - kill rules_generator - - - have a $attrs{} ? - - - remove RUN+="socket:" - - - libudev.so.1 - - symbol versioning - - return object with *_unref() - - udev_monitor_from_socket() - - udev_queue_get_failed_list_entry() diff --git a/src/udev/accelerometer/61-accelerometer.rules b/src/udev/accelerometer/61-accelerometer.rules new file mode 100644 index 0000000000..a6a2bfd088 --- /dev/null +++ b/src/udev/accelerometer/61-accelerometer.rules @@ -0,0 +1,3 @@ +# do not edit this file, it will be overwritten on update + +SUBSYSTEM=="input", ACTION!="remove", ENV{ID_INPUT_ACCELEROMETER}=="1", IMPORT{program}="accelerometer %p" diff --git a/src/udev/accelerometer/accelerometer.c b/src/udev/accelerometer/accelerometer.c new file mode 100644 index 0000000000..bc9715b264 --- /dev/null +++ b/src/udev/accelerometer/accelerometer.c @@ -0,0 +1,357 @@ +/* + * accelerometer - exports device orientation through property + * + * When an "change" event is received on an accelerometer, + * open its device node, and from the value, as well as the previous + * value of the property, calculate the device's new orientation, + * and export it as ID_INPUT_ACCELEROMETER_ORIENTATION. + * + * Possible values are: + * undefined + * * normal + * * bottom-up + * * left-up + * * right-up + * + * The property will be persistent across sessions, and the new + * orientations can be deducted from the previous one (it allows + * for a threshold for switching between opposite ends of the + * orientation). + * + * Copyright (C) 2011 Red Hat, Inc. + * Author: + * Bastien Nocera + * + * orientation_calc() from the sensorfw package + * Copyright (C) 2009-2010 Nokia Corporation + * Authors: + * Üstün Ergenoglu + * Timo Rongas + * Lihan Guo + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with keymap; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libudev.h" +#include "libudev-private.h" + +/* we must use this kernel-compatible implementation */ +#define BITS_PER_LONG (sizeof(unsigned long) * 8) +#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) +#define OFF(x) ((x)%BITS_PER_LONG) +#define BIT(x) (1UL<> OFF(bit)) & 1) + +static int debug = 0; + +static void log_fn(struct udev *udev, int priority, + const char *file, int line, const char *fn, + const char *format, va_list args) +{ + if (debug) { + fprintf(stderr, "%s: ", fn); + vfprintf(stderr, format, args); + } else { + vsyslog(priority, format, args); + } +} + +typedef enum { + ORIENTATION_UNDEFINED, + ORIENTATION_NORMAL, + ORIENTATION_BOTTOM_UP, + ORIENTATION_LEFT_UP, + ORIENTATION_RIGHT_UP +} OrientationUp; + +static const char *orientations[] = { + "undefined", + "normal", + "bottom-up", + "left-up", + "right-up", + NULL +}; + +#define ORIENTATION_UP_UP ORIENTATION_NORMAL + +#define DEFAULT_THRESHOLD 250 +#define RADIANS_TO_DEGREES 180.0/M_PI +#define SAME_AXIS_LIMIT 5 + +#define THRESHOLD_LANDSCAPE 25 +#define THRESHOLD_PORTRAIT 20 + +static const char * +orientation_to_string (OrientationUp o) +{ + return orientations[o]; +} + +static OrientationUp +string_to_orientation (const char *orientation) +{ + int i; + + if (orientation == NULL) + return ORIENTATION_UNDEFINED; + for (i = 0; orientations[i] != NULL; i++) { + if (strcmp (orientation, orientations[i]) == 0) + return i; + } + return ORIENTATION_UNDEFINED; +} + +static OrientationUp +orientation_calc (OrientationUp prev, + int x, int y, int z) +{ + int rotation; + OrientationUp ret = prev; + + /* Portrait check */ + rotation = round(atan((double) x / sqrt(y * y + z * z)) * RADIANS_TO_DEGREES); + + if (abs(rotation) > THRESHOLD_PORTRAIT) { + ret = (rotation < 0) ? ORIENTATION_LEFT_UP : ORIENTATION_RIGHT_UP; + + /* Some threshold to switching between portrait modes */ + if (prev == ORIENTATION_LEFT_UP || prev == ORIENTATION_RIGHT_UP) { + if (abs(rotation) < SAME_AXIS_LIMIT) { + ret = prev; + } + } + + } else { + /* Landscape check */ + rotation = round(atan((double) y / sqrt(x * x + z * z)) * RADIANS_TO_DEGREES); + + if (abs(rotation) > THRESHOLD_LANDSCAPE) { + ret = (rotation < 0) ? ORIENTATION_BOTTOM_UP : ORIENTATION_NORMAL; + + /* Some threshold to switching between landscape modes */ + if (prev == ORIENTATION_BOTTOM_UP || prev == ORIENTATION_NORMAL) { + if (abs(rotation) < SAME_AXIS_LIMIT) { + ret = prev; + } + } + } + } + + return ret; +} + +static OrientationUp +get_prev_orientation(struct udev_device *dev) +{ + const char *value; + + value = udev_device_get_property_value(dev, "ID_INPUT_ACCELEROMETER_ORIENTATION"); + if (value == NULL) + return ORIENTATION_UNDEFINED; + return string_to_orientation(value); +} + +#define SET_AXIS(axis, code_) if (ev[i].code == code_) { if (got_##axis == 0) { axis = ev[i].value; got_##axis = 1; } } + +/* accelerometers */ +static void test_orientation(struct udev *udev, + struct udev_device *dev, + const char *devpath) +{ + OrientationUp old, new; + int fd, r; + struct input_event ev[64]; + int got_syn = 0; + int got_x, got_y, got_z; + int x = 0, y = 0, z = 0; + char text[64]; + + old = get_prev_orientation(dev); + + if ((fd = open(devpath, O_RDONLY)) < 0) + return; + + got_x = got_y = got_z = 0; + + while (1) { + int i; + + r = read(fd, ev, sizeof(struct input_event) * 64); + + if (r < (int) sizeof(struct input_event)) + return; + + for (i = 0; i < r / (int) sizeof(struct input_event); i++) { + if (got_syn == 1) { + if (ev[i].type == EV_ABS) { + SET_AXIS(x, ABS_X); + SET_AXIS(y, ABS_Y); + SET_AXIS(z, ABS_Z); + } + } + if (ev[i].type == EV_SYN && ev[i].code == SYN_REPORT) { + got_syn = 1; + } + if (got_x && got_y && got_z) + goto read_dev; + } + } + +read_dev: + close(fd); + + if (!got_x || !got_y || !got_z) + return; + + new = orientation_calc(old, x, y, z); + snprintf(text, sizeof(text), "ID_INPUT_ACCELEROMETER_ORIENTATION=%s", orientation_to_string(new)); + puts(text); +} + +static void help(void) +{ + printf("Usage: accelerometer [options] \n" + " --debug debug to stderr\n" + " --help print this help text\n\n"); +} + +int main (int argc, char** argv) +{ + struct udev *udev; + struct udev_device *dev; + + static const struct option options[] = { + { "debug", no_argument, NULL, 'd' }, + { "help", no_argument, NULL, 'h' }, + {} + }; + + char devpath[PATH_MAX]; + char *devnode; + const char *id_path; + struct udev_enumerate *enumerate; + struct udev_list_entry *list_entry; + + udev = udev_new(); + if (udev == NULL) + return 1; + + udev_log_init("input_id"); + udev_set_log_fn(udev, log_fn); + + /* CLI argument parsing */ + while (1) { + int option; + + option = getopt_long(argc, argv, "dxh", options, NULL); + if (option == -1) + break; + + switch (option) { + case 'd': + debug = 1; + if (udev_get_log_priority(udev) < LOG_INFO) + udev_set_log_priority(udev, LOG_INFO); + break; + case 'h': + help(); + exit(0); + default: + exit(1); + } + } + + if (argv[optind] == NULL) { + help(); + exit(1); + } + + /* get the device */ + snprintf(devpath, sizeof(devpath), "%s/%s", udev_get_sys_path(udev), argv[optind]); + dev = udev_device_new_from_syspath(udev, devpath); + if (dev == NULL) { + fprintf(stderr, "unable to access '%s'\n", devpath); + return 1; + } + + id_path = udev_device_get_property_value(dev, "ID_PATH"); + if (id_path == NULL) { + fprintf (stderr, "unable to get property ID_PATH for '%s'", devpath); + return 0; + } + + /* Get the children devices and find the devnode + * FIXME: use udev_enumerate_add_match_children() instead + * when it's available */ + devnode = NULL; + enumerate = udev_enumerate_new(udev); + udev_enumerate_add_match_property(enumerate, "ID_PATH", id_path); + udev_enumerate_add_match_subsystem(enumerate, "input"); + udev_enumerate_scan_devices(enumerate); + udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(enumerate)) { + struct udev_device *device; + const char *node; + + device = udev_device_new_from_syspath(udev_enumerate_get_udev(enumerate), + udev_list_entry_get_name(list_entry)); + if (device == NULL) + continue; + /* Already found it */ + if (devnode != NULL) { + udev_device_unref(device); + continue; + } + + node = udev_device_get_devnode(device); + if (node == NULL) { + udev_device_unref(device); + continue; + } + /* Use the event sub-device */ + if (strstr(node, "/event") == NULL) { + udev_device_unref(device); + continue; + } + + devnode = strdup(node); + udev_device_unref(device); + } + + if (devnode == NULL) { + fprintf(stderr, "unable to get device node for '%s'\n", devpath); + return 0; + } + + info(udev, "Opening accelerometer device %s\n", devnode); + test_orientation(udev, dev, devnode); + free(devnode); + + return 0; +} diff --git a/src/udev/ata_id/ata_id.c b/src/udev/ata_id/ata_id.c new file mode 100644 index 0000000000..846a73b547 --- /dev/null +++ b/src/udev/ata_id/ata_id.c @@ -0,0 +1,721 @@ +/* + * ata_id - reads product/serial number from ATA drives + * + * Copyright (C) 2005-2008 Kay Sievers + * Copyright (C) 2009 Lennart Poettering + * Copyright (C) 2009-2010 David Zeuthen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libudev.h" +#include "libudev-private.h" + +#define COMMAND_TIMEOUT_MSEC (30 * 1000) + +static int disk_scsi_inquiry_command(int fd, + void *buf, + size_t buf_len) +{ + struct sg_io_v4 io_v4; + uint8_t cdb[6]; + uint8_t sense[32]; + int ret; + + /* + * INQUIRY, see SPC-4 section 6.4 + */ + memset(cdb, 0, sizeof(cdb)); + cdb[0] = 0x12; /* OPERATION CODE: INQUIRY */ + cdb[3] = (buf_len >> 8); /* ALLOCATION LENGTH */ + cdb[4] = (buf_len & 0xff); + + memset(sense, 0, sizeof(sense)); + + memset(&io_v4, 0, sizeof(struct sg_io_v4)); + io_v4.guard = 'Q'; + io_v4.protocol = BSG_PROTOCOL_SCSI; + io_v4.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD; + io_v4.request_len = sizeof (cdb); + io_v4.request = (uintptr_t) cdb; + io_v4.max_response_len = sizeof (sense); + io_v4.response = (uintptr_t) sense; + io_v4.din_xfer_len = buf_len; + io_v4.din_xferp = (uintptr_t) buf; + io_v4.timeout = COMMAND_TIMEOUT_MSEC; + + ret = ioctl(fd, SG_IO, &io_v4); + if (ret != 0) { + /* could be that the driver doesn't do version 4, try version 3 */ + if (errno == EINVAL) { + struct sg_io_hdr io_hdr; + + memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); + io_hdr.interface_id = 'S'; + io_hdr.cmdp = (unsigned char*) cdb; + io_hdr.cmd_len = sizeof (cdb); + io_hdr.dxferp = buf; + io_hdr.dxfer_len = buf_len; + io_hdr.sbp = sense; + io_hdr.mx_sb_len = sizeof (sense); + io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; + io_hdr.timeout = COMMAND_TIMEOUT_MSEC; + + ret = ioctl(fd, SG_IO, &io_hdr); + if (ret != 0) + goto out; + + /* even if the ioctl succeeds, we need to check the return value */ + if (!(io_hdr.status == 0 && + io_hdr.host_status == 0 && + io_hdr.driver_status == 0)) { + errno = EIO; + ret = -1; + goto out; + } + } else { + goto out; + } + } + + /* even if the ioctl succeeds, we need to check the return value */ + if (!(io_v4.device_status == 0 && + io_v4.transport_status == 0 && + io_v4.driver_status == 0)) { + errno = EIO; + ret = -1; + goto out; + } + + out: + return ret; +} + +static int disk_identify_command(int fd, + void *buf, + size_t buf_len) +{ + struct sg_io_v4 io_v4; + uint8_t cdb[12]; + uint8_t sense[32]; + uint8_t *desc = sense+8; + int ret; + + /* + * ATA Pass-Through 12 byte command, as described in + * + * T10 04-262r8 ATA Command Pass-Through + * + * from http://www.t10.org/ftp/t10/document.04/04-262r8.pdf + */ + memset(cdb, 0, sizeof(cdb)); + cdb[0] = 0xa1; /* OPERATION CODE: 12 byte pass through */ + cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */ + cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */ + cdb[3] = 0; /* FEATURES */ + cdb[4] = 1; /* SECTORS */ + cdb[5] = 0; /* LBA LOW */ + cdb[6] = 0; /* LBA MID */ + cdb[7] = 0; /* LBA HIGH */ + cdb[8] = 0 & 0x4F; /* SELECT */ + cdb[9] = 0xEC; /* Command: ATA IDENTIFY DEVICE */; + memset(sense, 0, sizeof(sense)); + + memset(&io_v4, 0, sizeof(struct sg_io_v4)); + io_v4.guard = 'Q'; + io_v4.protocol = BSG_PROTOCOL_SCSI; + io_v4.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD; + io_v4.request_len = sizeof (cdb); + io_v4.request = (uintptr_t) cdb; + io_v4.max_response_len = sizeof (sense); + io_v4.response = (uintptr_t) sense; + io_v4.din_xfer_len = buf_len; + io_v4.din_xferp = (uintptr_t) buf; + io_v4.timeout = COMMAND_TIMEOUT_MSEC; + + ret = ioctl(fd, SG_IO, &io_v4); + if (ret != 0) { + /* could be that the driver doesn't do version 4, try version 3 */ + if (errno == EINVAL) { + struct sg_io_hdr io_hdr; + + memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); + io_hdr.interface_id = 'S'; + io_hdr.cmdp = (unsigned char*) cdb; + io_hdr.cmd_len = sizeof (cdb); + io_hdr.dxferp = buf; + io_hdr.dxfer_len = buf_len; + io_hdr.sbp = sense; + io_hdr.mx_sb_len = sizeof (sense); + io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; + io_hdr.timeout = COMMAND_TIMEOUT_MSEC; + + ret = ioctl(fd, SG_IO, &io_hdr); + if (ret != 0) + goto out; + } else { + goto out; + } + } + + if (!(sense[0] == 0x72 && desc[0] == 0x9 && desc[1] == 0x0c)) { + errno = EIO; + ret = -1; + goto out; + } + + out: + return ret; +} + +static int disk_identify_packet_device_command(int fd, + void *buf, + size_t buf_len) +{ + struct sg_io_v4 io_v4; + uint8_t cdb[16]; + uint8_t sense[32]; + uint8_t *desc = sense+8; + int ret; + + /* + * ATA Pass-Through 16 byte command, as described in + * + * T10 04-262r8 ATA Command Pass-Through + * + * from http://www.t10.org/ftp/t10/document.04/04-262r8.pdf + */ + memset(cdb, 0, sizeof(cdb)); + cdb[0] = 0x85; /* OPERATION CODE: 16 byte pass through */ + cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */ + cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */ + cdb[3] = 0; /* FEATURES */ + cdb[4] = 0; /* FEATURES */ + cdb[5] = 0; /* SECTORS */ + cdb[6] = 1; /* SECTORS */ + cdb[7] = 0; /* LBA LOW */ + cdb[8] = 0; /* LBA LOW */ + cdb[9] = 0; /* LBA MID */ + cdb[10] = 0; /* LBA MID */ + cdb[11] = 0; /* LBA HIGH */ + cdb[12] = 0; /* LBA HIGH */ + cdb[13] = 0; /* DEVICE */ + cdb[14] = 0xA1; /* Command: ATA IDENTIFY PACKET DEVICE */; + cdb[15] = 0; /* CONTROL */ + memset(sense, 0, sizeof(sense)); + + memset(&io_v4, 0, sizeof(struct sg_io_v4)); + io_v4.guard = 'Q'; + io_v4.protocol = BSG_PROTOCOL_SCSI; + io_v4.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD; + io_v4.request_len = sizeof (cdb); + io_v4.request = (uintptr_t) cdb; + io_v4.max_response_len = sizeof (sense); + io_v4.response = (uintptr_t) sense; + io_v4.din_xfer_len = buf_len; + io_v4.din_xferp = (uintptr_t) buf; + io_v4.timeout = COMMAND_TIMEOUT_MSEC; + + ret = ioctl(fd, SG_IO, &io_v4); + if (ret != 0) { + /* could be that the driver doesn't do version 4, try version 3 */ + if (errno == EINVAL) { + struct sg_io_hdr io_hdr; + + memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); + io_hdr.interface_id = 'S'; + io_hdr.cmdp = (unsigned char*) cdb; + io_hdr.cmd_len = sizeof (cdb); + io_hdr.dxferp = buf; + io_hdr.dxfer_len = buf_len; + io_hdr.sbp = sense; + io_hdr.mx_sb_len = sizeof (sense); + io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; + io_hdr.timeout = COMMAND_TIMEOUT_MSEC; + + ret = ioctl(fd, SG_IO, &io_hdr); + if (ret != 0) + goto out; + } else { + goto out; + } + } + + if (!(sense[0] == 0x72 && desc[0] == 0x9 && desc[1] == 0x0c)) { + errno = EIO; + ret = -1; + goto out; + } + + out: + return ret; +} + +/** + * disk_identify_get_string: + * @identify: A block of IDENTIFY data + * @offset_words: Offset of the string to get, in words. + * @dest: Destination buffer for the string. + * @dest_len: Length of destination buffer, in bytes. + * + * Copies the ATA string from @identify located at @offset_words into @dest. + */ +static void disk_identify_get_string(uint8_t identify[512], + unsigned int offset_words, + char *dest, + size_t dest_len) +{ + unsigned int c1; + unsigned int c2; + + assert(identify != NULL); + assert(dest != NULL); + assert((dest_len & 1) == 0); + + while (dest_len > 0) { + c1 = identify[offset_words * 2 + 1]; + c2 = identify[offset_words * 2]; + *dest = c1; + dest++; + *dest = c2; + dest++; + offset_words++; + dest_len -= 2; + } +} + +static void disk_identify_fixup_string(uint8_t identify[512], + unsigned int offset_words, + size_t len) +{ + disk_identify_get_string(identify, offset_words, + (char *) identify + offset_words * 2, len); +} + +static void disk_identify_fixup_uint16 (uint8_t identify[512], unsigned int offset_words) +{ + uint16_t *p; + + p = (uint16_t *) identify; + p[offset_words] = le16toh (p[offset_words]); +} + +/** + * disk_identify: + * @udev: The libudev context. + * @fd: File descriptor for the block device. + * @out_identify: Return location for IDENTIFY data. + * @out_is_packet_device: Return location for whether returned data is from a IDENTIFY PACKET DEVICE. + * + * Sends the IDENTIFY DEVICE or IDENTIFY PACKET DEVICE command to the + * device represented by @fd. If successful, then the result will be + * copied into @out_identify and @out_is_packet_device. + * + * This routine is based on code from libatasmart, Copyright 2008 + * Lennart Poettering, LGPL v2.1. + * + * Returns: 0 if the data was successfully obtained, otherwise + * non-zero with errno set. + */ +static int disk_identify(struct udev *udev, + int fd, + uint8_t out_identify[512], + int *out_is_packet_device) +{ + int ret; + uint8_t inquiry_buf[36]; + int peripheral_device_type; + int all_nul_bytes; + int n; + int is_packet_device; + + assert(out_identify != NULL); + + /* init results */ + ret = -1; + memset(out_identify, '\0', 512); + is_packet_device = 0; + + /* If we were to use ATA PASS_THROUGH (12) on an ATAPI device + * we could accidentally blank media. This is because MMC's BLANK + * command has the same op-code (0x61). + * + * To prevent this from happening we bail out if the device + * isn't a Direct Access Block Device, e.g. SCSI type 0x00 + * (CD/DVD devices are type 0x05). So we send a SCSI INQUIRY + * command first... libata is handling this via its SCSI + * emulation layer. + * + * This also ensures that we're actually dealing with a device + * that understands SCSI commands. + * + * (Yes, it is a bit perverse that we're tunneling the ATA + * command through SCSI and relying on the ATA driver + * emulating SCSI well-enough...) + * + * (See commit 160b069c25690bfb0c785994c7c3710289179107 for + * the original bug-fix and see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=556635 + * for the original bug-report.) + */ + ret = disk_scsi_inquiry_command (fd, inquiry_buf, sizeof (inquiry_buf)); + if (ret != 0) + goto out; + + /* SPC-4, section 6.4.2: Standard INQUIRY data */ + peripheral_device_type = inquiry_buf[0] & 0x1f; + if (peripheral_device_type == 0x05) + { + is_packet_device = 1; + ret = disk_identify_packet_device_command(fd, out_identify, 512); + goto check_nul_bytes; + } + if (peripheral_device_type != 0x00) { + ret = -1; + errno = EIO; + goto out; + } + + /* OK, now issue the IDENTIFY DEVICE command */ + ret = disk_identify_command(fd, out_identify, 512); + if (ret != 0) + goto out; + + check_nul_bytes: + /* Check if IDENTIFY data is all NUL bytes - if so, bail */ + all_nul_bytes = 1; + for (n = 0; n < 512; n++) { + if (out_identify[n] != '\0') { + all_nul_bytes = 0; + break; + } + } + + if (all_nul_bytes) { + ret = -1; + errno = EIO; + goto out; + } + +out: + if (out_is_packet_device != NULL) + *out_is_packet_device = is_packet_device; + return ret; +} + +static void log_fn(struct udev *udev, int priority, + const char *file, int line, const char *fn, + const char *format, va_list args) +{ + vsyslog(priority, format, args); +} + +int main(int argc, char *argv[]) +{ + struct udev *udev; + struct hd_driveid id; + uint8_t identify[512]; + uint16_t *identify_words; + char model[41]; + char model_enc[256]; + char serial[21]; + char revision[9]; + const char *node = NULL; + int export = 0; + int fd; + uint16_t word; + int rc = 0; + int is_packet_device = 0; + static const struct option options[] = { + { "export", no_argument, NULL, 'x' }, + { "help", no_argument, NULL, 'h' }, + {} + }; + + udev = udev_new(); + if (udev == NULL) + goto exit; + + udev_log_init("ata_id"); + udev_set_log_fn(udev, log_fn); + + while (1) { + int option; + + option = getopt_long(argc, argv, "xh", options, NULL); + if (option == -1) + break; + + switch (option) { + case 'x': + export = 1; + break; + case 'h': + printf("Usage: ata_id [--export] [--help] \n" + " --export print values as environment keys\n" + " --help print this help text\n\n"); + goto exit; + } + } + + node = argv[optind]; + if (node == NULL) { + err(udev, "no node specified\n"); + rc = 1; + goto exit; + } + + fd = open(node, O_RDONLY|O_NONBLOCK); + if (fd < 0) { + err(udev, "unable to open '%s'\n", node); + rc = 1; + goto exit; + } + + if (disk_identify(udev, fd, identify, &is_packet_device) == 0) { + /* + * fix up only the fields from the IDENTIFY data that we are going to + * use and copy it into the hd_driveid struct for convenience + */ + disk_identify_fixup_string (identify, 10, 20); /* serial */ + disk_identify_fixup_string (identify, 23, 6); /* fwrev */ + disk_identify_fixup_string (identify, 27, 40); /* model */ + disk_identify_fixup_uint16 (identify, 0); /* configuration */ + disk_identify_fixup_uint16 (identify, 75); /* queue depth */ + disk_identify_fixup_uint16 (identify, 75); /* SATA capabilities */ + disk_identify_fixup_uint16 (identify, 82); /* command set supported */ + disk_identify_fixup_uint16 (identify, 83); /* command set supported */ + disk_identify_fixup_uint16 (identify, 84); /* command set supported */ + disk_identify_fixup_uint16 (identify, 85); /* command set supported */ + disk_identify_fixup_uint16 (identify, 86); /* command set supported */ + disk_identify_fixup_uint16 (identify, 87); /* command set supported */ + disk_identify_fixup_uint16 (identify, 89); /* time required for SECURITY ERASE UNIT */ + disk_identify_fixup_uint16 (identify, 90); /* time required for enhanced SECURITY ERASE UNIT */ + disk_identify_fixup_uint16 (identify, 91); /* current APM values */ + disk_identify_fixup_uint16 (identify, 94); /* current AAM value */ + disk_identify_fixup_uint16 (identify, 128); /* device lock function */ + disk_identify_fixup_uint16 (identify, 217); /* nominal media rotation rate */ + memcpy(&id, identify, sizeof id); + } else { + /* If this fails, then try HDIO_GET_IDENTITY */ + if (ioctl(fd, HDIO_GET_IDENTITY, &id) != 0) { + info(udev, "HDIO_GET_IDENTITY failed for '%s': %m\n", node); + rc = 2; + goto close; + } + } + identify_words = (uint16_t *) identify; + + memcpy (model, id.model, 40); + model[40] = '\0'; + udev_util_encode_string(model, model_enc, sizeof(model_enc)); + util_replace_whitespace((char *) id.model, model, 40); + util_replace_chars(model, NULL); + util_replace_whitespace((char *) id.serial_no, serial, 20); + util_replace_chars(serial, NULL); + util_replace_whitespace((char *) id.fw_rev, revision, 8); + util_replace_chars(revision, NULL); + + if (export) { + /* Set this to convey the disk speaks the ATA protocol */ + printf("ID_ATA=1\n"); + + if ((id.config >> 8) & 0x80) { + /* This is an ATAPI device */ + switch ((id.config >> 8) & 0x1f) { + case 0: + printf("ID_TYPE=cd\n"); + break; + case 1: + printf("ID_TYPE=tape\n"); + break; + case 5: + printf("ID_TYPE=cd\n"); + break; + case 7: + printf("ID_TYPE=optical\n"); + break; + default: + printf("ID_TYPE=generic\n"); + break; + } + } else { + printf("ID_TYPE=disk\n"); + } + printf("ID_BUS=ata\n"); + printf("ID_MODEL=%s\n", model); + printf("ID_MODEL_ENC=%s\n", model_enc); + printf("ID_REVISION=%s\n", revision); + if (serial[0] != '\0') { + printf("ID_SERIAL=%s_%s\n", model, serial); + printf("ID_SERIAL_SHORT=%s\n", serial); + } else { + printf("ID_SERIAL=%s\n", model); + } + + if (id.command_set_1 & (1<<5)) { + printf ("ID_ATA_WRITE_CACHE=1\n"); + printf ("ID_ATA_WRITE_CACHE_ENABLED=%d\n", (id.cfs_enable_1 & (1<<5)) ? 1 : 0); + } + if (id.command_set_1 & (1<<10)) { + printf("ID_ATA_FEATURE_SET_HPA=1\n"); + printf("ID_ATA_FEATURE_SET_HPA_ENABLED=%d\n", (id.cfs_enable_1 & (1<<10)) ? 1 : 0); + + /* + * TODO: use the READ NATIVE MAX ADDRESS command to get the native max address + * so it is easy to check whether the protected area is in use. + */ + } + if (id.command_set_1 & (1<<3)) { + printf("ID_ATA_FEATURE_SET_PM=1\n"); + printf("ID_ATA_FEATURE_SET_PM_ENABLED=%d\n", (id.cfs_enable_1 & (1<<3)) ? 1 : 0); + } + if (id.command_set_1 & (1<<1)) { + printf("ID_ATA_FEATURE_SET_SECURITY=1\n"); + printf("ID_ATA_FEATURE_SET_SECURITY_ENABLED=%d\n", (id.cfs_enable_1 & (1<<1)) ? 1 : 0); + printf("ID_ATA_FEATURE_SET_SECURITY_ERASE_UNIT_MIN=%d\n", id.trseuc * 2); + if ((id.cfs_enable_1 & (1<<1))) /* enabled */ { + if (id.dlf & (1<<8)) + printf("ID_ATA_FEATURE_SET_SECURITY_LEVEL=maximum\n"); + else + printf("ID_ATA_FEATURE_SET_SECURITY_LEVEL=high\n"); + } + if (id.dlf & (1<<5)) + printf("ID_ATA_FEATURE_SET_SECURITY_ENHANCED_ERASE_UNIT_MIN=%d\n", id.trsEuc * 2); + if (id.dlf & (1<<4)) + printf("ID_ATA_FEATURE_SET_SECURITY_EXPIRE=1\n"); + if (id.dlf & (1<<3)) + printf("ID_ATA_FEATURE_SET_SECURITY_FROZEN=1\n"); + if (id.dlf & (1<<2)) + printf("ID_ATA_FEATURE_SET_SECURITY_LOCKED=1\n"); + } + if (id.command_set_1 & (1<<0)) { + printf("ID_ATA_FEATURE_SET_SMART=1\n"); + printf("ID_ATA_FEATURE_SET_SMART_ENABLED=%d\n", (id.cfs_enable_1 & (1<<0)) ? 1 : 0); + } + if (id.command_set_2 & (1<<9)) { + printf("ID_ATA_FEATURE_SET_AAM=1\n"); + printf("ID_ATA_FEATURE_SET_AAM_ENABLED=%d\n", (id.cfs_enable_2 & (1<<9)) ? 1 : 0); + printf("ID_ATA_FEATURE_SET_AAM_VENDOR_RECOMMENDED_VALUE=%d\n", id.acoustic >> 8); + printf("ID_ATA_FEATURE_SET_AAM_CURRENT_VALUE=%d\n", id.acoustic & 0xff); + } + if (id.command_set_2 & (1<<5)) { + printf("ID_ATA_FEATURE_SET_PUIS=1\n"); + printf("ID_ATA_FEATURE_SET_PUIS_ENABLED=%d\n", (id.cfs_enable_2 & (1<<5)) ? 1 : 0); + } + if (id.command_set_2 & (1<<3)) { + printf("ID_ATA_FEATURE_SET_APM=1\n"); + printf("ID_ATA_FEATURE_SET_APM_ENABLED=%d\n", (id.cfs_enable_2 & (1<<3)) ? 1 : 0); + if ((id.cfs_enable_2 & (1<<3))) + printf("ID_ATA_FEATURE_SET_APM_CURRENT_VALUE=%d\n", id.CurAPMvalues & 0xff); + } + if (id.command_set_2 & (1<<0)) + printf("ID_ATA_DOWNLOAD_MICROCODE=1\n"); + + /* + * Word 76 indicates the capabilities of a SATA device. A PATA device shall set + * word 76 to 0000h or FFFFh. If word 76 is set to 0000h or FFFFh, then + * the device does not claim compliance with the Serial ATA specification and words + * 76 through 79 are not valid and shall be ignored. + */ + word = *((uint16_t *) identify + 76); + if (word != 0x0000 && word != 0xffff) { + printf("ID_ATA_SATA=1\n"); + /* + * If bit 2 of word 76 is set to one, then the device supports the Gen2 + * signaling rate of 3.0 Gb/s (see SATA 2.6). + * + * If bit 1 of word 76 is set to one, then the device supports the Gen1 + * signaling rate of 1.5 Gb/s (see SATA 2.6). + */ + if (word & (1<<2)) + printf("ID_ATA_SATA_SIGNAL_RATE_GEN2=1\n"); + if (word & (1<<1)) + printf("ID_ATA_SATA_SIGNAL_RATE_GEN1=1\n"); + } + + /* Word 217 indicates the nominal media rotation rate of the device */ + word = *((uint16_t *) identify + 217); + if (word != 0x0000) { + if (word == 0x0001) { + printf ("ID_ATA_ROTATION_RATE_RPM=0\n"); /* non-rotating e.g. SSD */ + } else if (word >= 0x0401 && word <= 0xfffe) { + printf ("ID_ATA_ROTATION_RATE_RPM=%d\n", word); + } + } + + /* + * Words 108-111 contain a mandatory World Wide Name (WWN) in the NAA IEEE Registered identifier + * format. Word 108 bits (15:12) shall contain 5h, indicating that the naming authority is IEEE. + * All other values are reserved. + */ + word = *((uint16_t *) identify + 108); + if ((word & 0xf000) == 0x5000) { + uint64_t wwwn; + + wwwn = *((uint16_t *) identify + 108); + wwwn <<= 16; + wwwn |= *((uint16_t *) identify + 109); + wwwn <<= 16; + wwwn |= *((uint16_t *) identify + 110); + wwwn <<= 16; + wwwn |= *((uint16_t *) identify + 111); + printf("ID_WWN=0x%llx\n", (unsigned long long int) wwwn); + /* ATA devices have no vendor extension */ + printf("ID_WWN_WITH_EXTENSION=0x%llx\n", (unsigned long long int) wwwn); + } + + /* from Linux's include/linux/ata.h */ + if (identify_words[0] == 0x848a || identify_words[0] == 0x844a) { + printf("ID_ATA_CFA=1\n"); + } else { + if ((identify_words[83] & 0xc004) == 0x4004) { + printf("ID_ATA_CFA=1\n"); + } + } + } else { + if (serial[0] != '\0') + printf("%s_%s\n", model, serial); + else + printf("%s\n", model); + } +close: + close(fd); +exit: + udev_unref(udev); + udev_log_close(); + return rc; +} diff --git a/src/udev/autogen.sh b/src/udev/autogen.sh deleted file mode 100755 index 55ee03afd1..0000000000 --- a/src/udev/autogen.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/sh -e - -if [ -f .git/hooks/pre-commit.sample -a ! -f .git/hooks/pre-commit ] ; then - cp -p .git/hooks/pre-commit.sample .git/hooks/pre-commit && \ - chmod +x .git/hooks/pre-commit && \ - echo "Activated pre-commit hook." -fi - -gtkdocize -autoreconf --install --symlink - -libdir() { - echo $(cd $1/$(gcc -print-multi-os-directory); pwd) -} - -args="$args \ ---prefix=/usr \ ---sysconfdir=/etc \ ---libdir=$(libdir /usr/lib) \ ---with-selinux \ ---enable-gtk-doc" - -if [ -L /bin ]; then -args="$args \ ---libexecdir=/usr/lib \ ---with-systemdsystemunitdir=/usr/lib/systemd/system \ -" -else -args="$args \ ---with-rootprefix= \ ----with-rootlibdir=$(libdir /lib) \ ---bindir=/sbin \ ---libexecdir=/lib \ ---with-systemdsystemunitdir=/lib/systemd/system \ -" -fi - -echo -echo "----------------------------------------------------------------" -echo "Initialized build system. For a common configuration please run:" -echo "----------------------------------------------------------------" -echo -echo "./configure CFLAGS='-g -O1' $args" -echo diff --git a/src/udev/cdrom_id/60-cdrom_id.rules b/src/udev/cdrom_id/60-cdrom_id.rules new file mode 100644 index 0000000000..6eaf76a72c --- /dev/null +++ b/src/udev/cdrom_id/60-cdrom_id.rules @@ -0,0 +1,20 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="cdrom_end" +SUBSYSTEM!="block", GOTO="cdrom_end" +KERNEL!="sr[0-9]*|xvd*", GOTO="cdrom_end" +ENV{DEVTYPE}!="disk", GOTO="cdrom_end" + +# unconditionally tag device as CDROM +KERNEL=="sr[0-9]*", ENV{ID_CDROM}="1" + +# media eject button pressed +ENV{DISK_EJECT_REQUEST}=="?*", RUN+="cdrom_id --eject-media $devnode", GOTO="cdrom_end" + +# import device and media properties and lock tray to +# enable the receiving of media eject button events +IMPORT{program}="cdrom_id --lock-media $devnode" + +KERNEL=="sr0", SYMLINK+="cdrom", OPTIONS+="link_priority=-100" + +LABEL="cdrom_end" diff --git a/src/udev/cdrom_id/cdrom_id.c b/src/udev/cdrom_id/cdrom_id.c new file mode 100644 index 0000000000..f90d52ec9c --- /dev/null +++ b/src/udev/cdrom_id/cdrom_id.c @@ -0,0 +1,1099 @@ +/* + * cdrom_id - optical drive and media information prober + * + * Copyright (C) 2008-2010 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE 1 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libudev.h" +#include "libudev-private.h" + +static bool debug; + +static void log_fn(struct udev *udev, int priority, + const char *file, int line, const char *fn, + const char *format, va_list args) +{ + if (debug) { + fprintf(stderr, "%s: ", fn); + vfprintf(stderr, format, args); + } else { + vsyslog(priority, format, args); + } +} + +/* device info */ +static unsigned int cd_cd_rom; +static unsigned int cd_cd_r; +static unsigned int cd_cd_rw; +static unsigned int cd_dvd_rom; +static unsigned int cd_dvd_r; +static unsigned int cd_dvd_rw; +static unsigned int cd_dvd_ram; +static unsigned int cd_dvd_plus_r; +static unsigned int cd_dvd_plus_rw; +static unsigned int cd_dvd_plus_r_dl; +static unsigned int cd_dvd_plus_rw_dl; +static unsigned int cd_bd; +static unsigned int cd_bd_r; +static unsigned int cd_bd_re; +static unsigned int cd_hddvd; +static unsigned int cd_hddvd_r; +static unsigned int cd_hddvd_rw; +static unsigned int cd_mo; +static unsigned int cd_mrw; +static unsigned int cd_mrw_w; + +/* media info */ +static unsigned int cd_media; +static unsigned int cd_media_cd_rom; +static unsigned int cd_media_cd_r; +static unsigned int cd_media_cd_rw; +static unsigned int cd_media_dvd_rom; +static unsigned int cd_media_dvd_r; +static unsigned int cd_media_dvd_rw; +static unsigned int cd_media_dvd_rw_ro; /* restricted overwrite mode */ +static unsigned int cd_media_dvd_rw_seq; /* sequential mode */ +static unsigned int cd_media_dvd_ram; +static unsigned int cd_media_dvd_plus_r; +static unsigned int cd_media_dvd_plus_rw; +static unsigned int cd_media_dvd_plus_r_dl; +static unsigned int cd_media_dvd_plus_rw_dl; +static unsigned int cd_media_bd; +static unsigned int cd_media_bd_r; +static unsigned int cd_media_bd_re; +static unsigned int cd_media_hddvd; +static unsigned int cd_media_hddvd_r; +static unsigned int cd_media_hddvd_rw; +static unsigned int cd_media_mo; +static unsigned int cd_media_mrw; +static unsigned int cd_media_mrw_w; + +static const char *cd_media_state = NULL; +static unsigned int cd_media_session_next; +static unsigned int cd_media_session_count; +static unsigned int cd_media_track_count; +static unsigned int cd_media_track_count_data; +static unsigned int cd_media_track_count_audio; +static unsigned long long int cd_media_session_last_offset; + +#define ERRCODE(s) ((((s)[2] & 0x0F) << 16) | ((s)[12] << 8) | ((s)[13])) +#define SK(errcode) (((errcode) >> 16) & 0xF) +#define ASC(errcode) (((errcode) >> 8) & 0xFF) +#define ASCQ(errcode) ((errcode) & 0xFF) + +static bool is_mounted(const char *device) +{ + struct stat statbuf; + FILE *fp; + int maj, min; + bool mounted = false; + + if (stat(device, &statbuf) < 0) + return -ENODEV; + + fp = fopen("/proc/self/mountinfo", "r"); + if (fp == NULL) + return -ENOSYS; + while (fscanf(fp, "%*s %*s %i:%i %*[^\n]", &maj, &min) == 2) { + if (makedev(maj, min) == statbuf.st_rdev) { + mounted = true; + break; + } + } + fclose(fp); + return mounted; +} + +static void info_scsi_cmd_err(struct udev *udev, char *cmd, int err) +{ + if (err == -1) { + info(udev, "%s failed\n", cmd); + return; + } + info(udev, "%s failed with SK=%Xh/ASC=%02Xh/ACQ=%02Xh\n", cmd, SK(err), ASC(err), ASCQ(err)); +} + +struct scsi_cmd { + struct cdrom_generic_command cgc; + union { + struct request_sense s; + unsigned char u[18]; + } _sense; + struct sg_io_hdr sg_io; +}; + +static void scsi_cmd_init(struct udev *udev, struct scsi_cmd *cmd) +{ + memset(cmd, 0x00, sizeof(struct scsi_cmd)); + cmd->cgc.quiet = 1; + cmd->cgc.sense = &cmd->_sense.s; + cmd->sg_io.interface_id = 'S'; + cmd->sg_io.mx_sb_len = sizeof(cmd->_sense); + cmd->sg_io.cmdp = cmd->cgc.cmd; + cmd->sg_io.sbp = cmd->_sense.u; + cmd->sg_io.flags = SG_FLAG_LUN_INHIBIT | SG_FLAG_DIRECT_IO; +} + +static void scsi_cmd_set(struct udev *udev, struct scsi_cmd *cmd, size_t i, unsigned char arg) +{ + cmd->sg_io.cmd_len = i + 1; + cmd->cgc.cmd[i] = arg; +} + +#define CHECK_CONDITION 0x01 + +static int scsi_cmd_run(struct udev *udev, struct scsi_cmd *cmd, int fd, unsigned char *buf, size_t bufsize) +{ + int ret = 0; + + if (bufsize > 0) { + cmd->sg_io.dxferp = buf; + cmd->sg_io.dxfer_len = bufsize; + cmd->sg_io.dxfer_direction = SG_DXFER_FROM_DEV; + } else { + cmd->sg_io.dxfer_direction = SG_DXFER_NONE; + } + if (ioctl(fd, SG_IO, &cmd->sg_io)) + return -1; + + if ((cmd->sg_io.info & SG_INFO_OK_MASK) != SG_INFO_OK) { + errno = EIO; + ret = -1; + if (cmd->sg_io.masked_status & CHECK_CONDITION) { + ret = ERRCODE(cmd->_sense.u); + if (ret == 0) + ret = -1; + } + } + return ret; +} + +static int media_lock(struct udev *udev, int fd, bool lock) +{ + int err; + + /* disable the kernel's lock logic */ + err = ioctl(fd, CDROM_CLEAR_OPTIONS, CDO_LOCK); + if (err < 0) + info(udev, "CDROM_CLEAR_OPTIONS, CDO_LOCK failed\n"); + + err = ioctl(fd, CDROM_LOCKDOOR, lock ? 1 : 0); + if (err < 0) + info(udev, "CDROM_LOCKDOOR failed\n"); + + return err; +} + +static int media_eject(struct udev *udev, int fd) +{ + struct scsi_cmd sc; + int err; + + scsi_cmd_init(udev, &sc); + scsi_cmd_set(udev, &sc, 0, 0x1b); + scsi_cmd_set(udev, &sc, 4, 0x02); + scsi_cmd_set(udev, &sc, 5, 0); + err = scsi_cmd_run(udev, &sc, fd, NULL, 0); + if ((err != 0)) { + info_scsi_cmd_err(udev, "START_STOP_UNIT", err); + return -1; + } + return 0; +} + +static int cd_capability_compat(struct udev *udev, int fd) +{ + int capability; + + capability = ioctl(fd, CDROM_GET_CAPABILITY, NULL); + if (capability < 0) { + info(udev, "CDROM_GET_CAPABILITY failed\n"); + return -1; + } + + if (capability & CDC_CD_R) + cd_cd_r = 1; + if (capability & CDC_CD_RW) + cd_cd_rw = 1; + if (capability & CDC_DVD) + cd_dvd_rom = 1; + if (capability & CDC_DVD_R) + cd_dvd_r = 1; + if (capability & CDC_DVD_RAM) + cd_dvd_ram = 1; + if (capability & CDC_MRW) + cd_mrw = 1; + if (capability & CDC_MRW_W) + cd_mrw_w = 1; + return 0; +} + +static int cd_media_compat(struct udev *udev, int fd) +{ + if (ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT) != CDS_DISC_OK) { + info(udev, "CDROM_DRIVE_STATUS != CDS_DISC_OK\n"); + return -1; + } + cd_media = 1; + return 0; +} + +static int cd_inquiry(struct udev *udev, int fd) +{ + struct scsi_cmd sc; + unsigned char inq[128]; + int err; + + scsi_cmd_init(udev, &sc); + scsi_cmd_set(udev, &sc, 0, 0x12); + scsi_cmd_set(udev, &sc, 4, 36); + scsi_cmd_set(udev, &sc, 5, 0); + err = scsi_cmd_run(udev, &sc, fd, inq, 36); + if ((err != 0)) { + info_scsi_cmd_err(udev, "INQUIRY", err); + return -1; + } + + if ((inq[0] & 0x1F) != 5) { + info(udev, "not an MMC unit\n"); + return -1; + } + + info(udev, "INQUIRY: [%.8s][%.16s][%.4s]\n", inq + 8, inq + 16, inq + 32); + return 0; +} + +static void feature_profile_media(struct udev *udev, int cur_profile) +{ + switch (cur_profile) { + case 0x03: + case 0x04: + case 0x05: + info(udev, "profile 0x%02x \n", cur_profile); + cd_media = 1; + cd_media_mo = 1; + break; + case 0x08: + info(udev, "profile 0x%02x media_cd_rom\n", cur_profile); + cd_media = 1; + cd_media_cd_rom = 1; + break; + case 0x09: + info(udev, "profile 0x%02x media_cd_r\n", cur_profile); + cd_media = 1; + cd_media_cd_r = 1; + break; + case 0x0a: + info(udev, "profile 0x%02x media_cd_rw\n", cur_profile); + cd_media = 1; + cd_media_cd_rw = 1; + break; + case 0x10: + info(udev, "profile 0x%02x media_dvd_ro\n", cur_profile); + cd_media = 1; + cd_media_dvd_rom = 1; + break; + case 0x11: + info(udev, "profile 0x%02x media_dvd_r\n", cur_profile); + cd_media = 1; + cd_media_dvd_r = 1; + break; + case 0x12: + info(udev, "profile 0x%02x media_dvd_ram\n", cur_profile); + cd_media = 1; + cd_media_dvd_ram = 1; + break; + case 0x13: + info(udev, "profile 0x%02x media_dvd_rw_ro\n", cur_profile); + cd_media = 1; + cd_media_dvd_rw = 1; + cd_media_dvd_rw_ro = 1; + break; + case 0x14: + info(udev, "profile 0x%02x media_dvd_rw_seq\n", cur_profile); + cd_media = 1; + cd_media_dvd_rw = 1; + cd_media_dvd_rw_seq = 1; + break; + case 0x1B: + info(udev, "profile 0x%02x media_dvd_plus_r\n", cur_profile); + cd_media = 1; + cd_media_dvd_plus_r = 1; + break; + case 0x1A: + info(udev, "profile 0x%02x media_dvd_plus_rw\n", cur_profile); + cd_media = 1; + cd_media_dvd_plus_rw = 1; + break; + case 0x2A: + info(udev, "profile 0x%02x media_dvd_plus_rw_dl\n", cur_profile); + cd_media = 1; + cd_media_dvd_plus_rw_dl = 1; + break; + case 0x2B: + info(udev, "profile 0x%02x media_dvd_plus_r_dl\n", cur_profile); + cd_media = 1; + cd_media_dvd_plus_r_dl = 1; + break; + case 0x40: + info(udev, "profile 0x%02x media_bd\n", cur_profile); + cd_media = 1; + cd_media_bd = 1; + break; + case 0x41: + case 0x42: + info(udev, "profile 0x%02x media_bd_r\n", cur_profile); + cd_media = 1; + cd_media_bd_r = 1; + break; + case 0x43: + info(udev, "profile 0x%02x media_bd_re\n", cur_profile); + cd_media = 1; + cd_media_bd_re = 1; + break; + case 0x50: + info(udev, "profile 0x%02x media_hddvd\n", cur_profile); + cd_media = 1; + cd_media_hddvd = 1; + break; + case 0x51: + info(udev, "profile 0x%02x media_hddvd_r\n", cur_profile); + cd_media = 1; + cd_media_hddvd_r = 1; + break; + case 0x52: + info(udev, "profile 0x%02x media_hddvd_rw\n", cur_profile); + cd_media = 1; + cd_media_hddvd_rw = 1; + break; + default: + info(udev, "profile 0x%02x \n", cur_profile); + break; + } +} + +static int feature_profiles(struct udev *udev, const unsigned char *profiles, size_t size) +{ + unsigned int i; + + for (i = 0; i+4 <= size; i += 4) { + int profile; + + profile = profiles[i] << 8 | profiles[i+1]; + switch (profile) { + case 0x03: + case 0x04: + case 0x05: + info(udev, "profile 0x%02x mo\n", profile); + cd_mo = 1; + break; + case 0x08: + info(udev, "profile 0x%02x cd_rom\n", profile); + cd_cd_rom = 1; + break; + case 0x09: + info(udev, "profile 0x%02x cd_r\n", profile); + cd_cd_r = 1; + break; + case 0x0A: + info(udev, "profile 0x%02x cd_rw\n", profile); + cd_cd_rw = 1; + break; + case 0x10: + info(udev, "profile 0x%02x dvd_rom\n", profile); + cd_dvd_rom = 1; + break; + case 0x12: + info(udev, "profile 0x%02x dvd_ram\n", profile); + cd_dvd_ram = 1; + break; + case 0x13: + case 0x14: + info(udev, "profile 0x%02x dvd_rw\n", profile); + cd_dvd_rw = 1; + break; + case 0x1B: + info(udev, "profile 0x%02x dvd_plus_r\n", profile); + cd_dvd_plus_r = 1; + break; + case 0x1A: + info(udev, "profile 0x%02x dvd_plus_rw\n", profile); + cd_dvd_plus_rw = 1; + break; + case 0x2A: + info(udev, "profile 0x%02x dvd_plus_rw_dl\n", profile); + cd_dvd_plus_rw_dl = 1; + break; + case 0x2B: + info(udev, "profile 0x%02x dvd_plus_r_dl\n", profile); + cd_dvd_plus_r_dl = 1; + break; + case 0x40: + cd_bd = 1; + info(udev, "profile 0x%02x bd\n", profile); + break; + case 0x41: + case 0x42: + cd_bd_r = 1; + info(udev, "profile 0x%02x bd_r\n", profile); + break; + case 0x43: + cd_bd_re = 1; + info(udev, "profile 0x%02x bd_re\n", profile); + break; + case 0x50: + cd_hddvd = 1; + info(udev, "profile 0x%02x hddvd\n", profile); + break; + case 0x51: + cd_hddvd_r = 1; + info(udev, "profile 0x%02x hddvd_r\n", profile); + break; + case 0x52: + cd_hddvd_rw = 1; + info(udev, "profile 0x%02x hddvd_rw\n", profile); + break; + default: + info(udev, "profile 0x%02x \n", profile); + break; + } + } + return 0; +} + +/* returns 0 if media was detected */ +static int cd_profiles_old_mmc(struct udev *udev, int fd) +{ + struct scsi_cmd sc; + int err; + + unsigned char header[32]; + + scsi_cmd_init(udev, &sc); + scsi_cmd_set(udev, &sc, 0, 0x51); + scsi_cmd_set(udev, &sc, 8, sizeof(header)); + scsi_cmd_set(udev, &sc, 9, 0); + err = scsi_cmd_run(udev, &sc, fd, header, sizeof(header)); + if ((err != 0)) { + info_scsi_cmd_err(udev, "READ DISC INFORMATION", err); + if (cd_media == 1) { + info(udev, "no current profile, but disc is present; assuming CD-ROM\n"); + cd_media_cd_rom = 1; + return 0; + } else { + info(udev, "no current profile, assuming no media\n"); + return -1; + } + }; + + cd_media = 1; + + if (header[2] & 16) { + cd_media_cd_rw = 1; + info(udev, "profile 0x0a media_cd_rw\n"); + } else if ((header[2] & 3) < 2 && cd_cd_r) { + cd_media_cd_r = 1; + info(udev, "profile 0x09 media_cd_r\n"); + } else { + cd_media_cd_rom = 1; + info(udev, "profile 0x08 media_cd_rom\n"); + } + return 0; +} + +/* returns 0 if media was detected */ +static int cd_profiles(struct udev *udev, int fd) +{ + struct scsi_cmd sc; + unsigned char features[65530]; + unsigned int cur_profile = 0; + unsigned int len; + unsigned int i; + int err; + int ret; + + ret = -1; + + /* First query the current profile */ + scsi_cmd_init(udev, &sc); + scsi_cmd_set(udev, &sc, 0, 0x46); + scsi_cmd_set(udev, &sc, 8, 8); + scsi_cmd_set(udev, &sc, 9, 0); + err = scsi_cmd_run(udev, &sc, fd, features, 8); + if ((err != 0)) { + info_scsi_cmd_err(udev, "GET CONFIGURATION", err); + /* handle pre-MMC2 drives which do not support GET CONFIGURATION */ + if (SK(err) == 0x5 && ASC(err) == 0x20) { + info(udev, "drive is pre-MMC2 and does not support 46h get configuration command\n"); + info(udev, "trying to work around the problem\n"); + ret = cd_profiles_old_mmc(udev, fd); + } + goto out; + } + + cur_profile = features[6] << 8 | features[7]; + if (cur_profile > 0) { + info(udev, "current profile 0x%02x\n", cur_profile); + feature_profile_media (udev, cur_profile); + ret = 0; /* we have media */ + } else { + info(udev, "no current profile, assuming no media\n"); + } + + len = features[0] << 24 | features[1] << 16 | features[2] << 8 | features[3]; + info(udev, "GET CONFIGURATION: size of features buffer 0x%04x\n", len); + + if (len > sizeof(features)) { + info(udev, "can not get features in a single query, truncating\n"); + len = sizeof(features); + } else if (len <= 8) { + len = sizeof(features); + } + + /* Now get the full feature buffer */ + scsi_cmd_init(udev, &sc); + scsi_cmd_set(udev, &sc, 0, 0x46); + scsi_cmd_set(udev, &sc, 7, ( len >> 8 ) & 0xff); + scsi_cmd_set(udev, &sc, 8, len & 0xff); + scsi_cmd_set(udev, &sc, 9, 0); + err = scsi_cmd_run(udev, &sc, fd, features, len); + if ((err != 0)) { + info_scsi_cmd_err(udev, "GET CONFIGURATION", err); + return -1; + } + + /* parse the length once more, in case the drive decided to have other features suddenly :) */ + len = features[0] << 24 | features[1] << 16 | features[2] << 8 | features[3]; + info(udev, "GET CONFIGURATION: size of features buffer 0x%04x\n", len); + + if (len > sizeof(features)) { + info(udev, "can not get features in a single query, truncating\n"); + len = sizeof(features); + } + + /* device features */ + for (i = 8; i+4 < len; i += (4 + features[i+3])) { + unsigned int feature; + + feature = features[i] << 8 | features[i+1]; + + switch (feature) { + case 0x00: + info(udev, "GET CONFIGURATION: feature 'profiles', with %i entries\n", features[i+3] / 4); + feature_profiles(udev, &features[i]+4, features[i+3]); + break; + default: + info(udev, "GET CONFIGURATION: feature 0x%04x , with 0x%02x bytes\n", feature, features[i+3]); + break; + } + } +out: + return ret; +} + +static int cd_media_info(struct udev *udev, int fd) +{ + struct scsi_cmd sc; + unsigned char header[32]; + static const char *media_status[] = { + "blank", + "appendable", + "complete", + "other" + }; + int err; + + scsi_cmd_init(udev, &sc); + scsi_cmd_set(udev, &sc, 0, 0x51); + scsi_cmd_set(udev, &sc, 8, sizeof(header) & 0xff); + scsi_cmd_set(udev, &sc, 9, 0); + err = scsi_cmd_run(udev, &sc, fd, header, sizeof(header)); + if ((err != 0)) { + info_scsi_cmd_err(udev, "READ DISC INFORMATION", err); + return -1; + }; + + cd_media = 1; + info(udev, "disk type %02x\n", header[8]); + info(udev, "hardware reported media status: %s\n", media_status[header[2] & 3]); + + /* exclude plain CDROM, some fake cdroms return 0 for "blank" media here */ + if (!cd_media_cd_rom) + cd_media_state = media_status[header[2] & 3]; + + /* fresh DVD-RW in restricted overwite mode reports itself as + * "appendable"; change it to "blank" to make it consistent with what + * gets reported after blanking, and what userspace expects */ + if (cd_media_dvd_rw_ro && (header[2] & 3) == 1) + cd_media_state = media_status[0]; + + /* DVD+RW discs (and DVD-RW in restricted mode) once formatted are + * always "complete", DVD-RAM are "other" or "complete" if the disc is + * write protected; we need to check the contents if it is blank */ + if ((cd_media_dvd_rw_ro || cd_media_dvd_plus_rw || cd_media_dvd_plus_rw_dl || cd_media_dvd_ram) && (header[2] & 3) > 1) { + unsigned char buffer[32 * 2048]; + unsigned char result, len; + int block, offset; + + if (cd_media_dvd_ram) { + /* a write protected dvd-ram may report "complete" status */ + + unsigned char dvdstruct[8]; + unsigned char format[12]; + + scsi_cmd_init(udev, &sc); + scsi_cmd_set(udev, &sc, 0, 0xAD); + scsi_cmd_set(udev, &sc, 7, 0xC0); + scsi_cmd_set(udev, &sc, 9, sizeof(dvdstruct)); + scsi_cmd_set(udev, &sc, 11, 0); + err = scsi_cmd_run(udev, &sc, fd, dvdstruct, sizeof(dvdstruct)); + if ((err != 0)) { + info_scsi_cmd_err(udev, "READ DVD STRUCTURE", err); + return -1; + } + if (dvdstruct[4] & 0x02) { + cd_media_state = media_status[2]; + info(udev, "write-protected DVD-RAM media inserted\n"); + goto determined; + } + + /* let's make sure we don't try to read unformatted media */ + scsi_cmd_init(udev, &sc); + scsi_cmd_set(udev, &sc, 0, 0x23); + scsi_cmd_set(udev, &sc, 8, sizeof(format)); + scsi_cmd_set(udev, &sc, 9, 0); + err = scsi_cmd_run(udev, &sc, fd, format, sizeof(format)); + if ((err != 0)) { + info_scsi_cmd_err(udev, "READ DVD FORMAT CAPACITIES", err); + return -1; + } + + len = format[3]; + if (len & 7 || len < 16) { + info(udev, "invalid format capacities length\n"); + return -1; + } + + switch(format[8] & 3) { + case 1: + info(udev, "unformatted DVD-RAM media inserted\n"); + /* This means that last format was interrupted + * or failed, blank dvd-ram discs are factory + * formatted. Take no action here as it takes + * quite a while to reformat a dvd-ram and it's + * not automatically started */ + goto determined; + + case 2: + info(udev, "formatted DVD-RAM media inserted\n"); + break; + + case 3: + cd_media = 0; //return no media + info(udev, "format capacities returned no media\n"); + return -1; + } + } + + /* Take a closer look at formatted media (unformatted DVD+RW + * has "blank" status", DVD-RAM was examined earlier) and check + * for ISO and UDF PVDs or a fs superblock presence and do it + * in one ioctl (we need just sectors 0 and 16) */ + scsi_cmd_init(udev, &sc); + scsi_cmd_set(udev, &sc, 0, 0x28); + scsi_cmd_set(udev, &sc, 5, 0); + scsi_cmd_set(udev, &sc, 8, 32); + scsi_cmd_set(udev, &sc, 9, 0); + err = scsi_cmd_run(udev, &sc, fd, buffer, sizeof(buffer)); + if ((err != 0)) { + cd_media = 0; + info_scsi_cmd_err(udev, "READ FIRST 32 BLOCKS", err); + return -1; + } + + /* if any non-zero data is found in sector 16 (iso and udf) or + * eventually 0 (fat32 boot sector, ext2 superblock, etc), disc + * is assumed non-blank */ + result = 0; + + for (block = 32768; block >= 0 && !result; block -= 32768) { + offset = block; + while (offset < (block + 2048) && !result) { + result = buffer [offset]; + offset++; + } + } + + if (!result) { + cd_media_state = media_status[0]; + info(udev, "no data in blocks 0 or 16, assuming blank\n"); + } else { + info(udev, "data in blocks 0 or 16, assuming complete\n"); + } + } + +determined: + /* "other" is e. g. DVD-RAM, can't append sessions there; DVDs in + * restricted overwrite mode can never append, only in sequential mode */ + if ((header[2] & 3) < 2 && !cd_media_dvd_rw_ro) + cd_media_session_next = header[10] << 8 | header[5]; + cd_media_session_count = header[9] << 8 | header[4]; + cd_media_track_count = header[11] << 8 | header[6]; + + return 0; +} + +static int cd_media_toc(struct udev *udev, int fd) +{ + struct scsi_cmd sc; + unsigned char header[12]; + unsigned char toc[65536]; + unsigned int len, i, num_tracks; + unsigned char *p; + int err; + + scsi_cmd_init(udev, &sc); + scsi_cmd_set(udev, &sc, 0, 0x43); + scsi_cmd_set(udev, &sc, 6, 1); + scsi_cmd_set(udev, &sc, 8, sizeof(header) & 0xff); + scsi_cmd_set(udev, &sc, 9, 0); + err = scsi_cmd_run(udev, &sc, fd, header, sizeof(header)); + if ((err != 0)) { + info_scsi_cmd_err(udev, "READ TOC", err); + return -1; + } + + len = (header[0] << 8 | header[1]) + 2; + info(udev, "READ TOC: len: %d, start track: %d, end track: %d\n", len, header[2], header[3]); + if (len > sizeof(toc)) + return -1; + if (len < 2) + return -1; + /* 2: first track, 3: last track */ + num_tracks = header[3] - header[2] + 1; + + /* empty media has no tracks */ + if (len < 8) + return 0; + + scsi_cmd_init(udev, &sc); + scsi_cmd_set(udev, &sc, 0, 0x43); + scsi_cmd_set(udev, &sc, 6, header[2]); /* First Track/Session Number */ + scsi_cmd_set(udev, &sc, 7, (len >> 8) & 0xff); + scsi_cmd_set(udev, &sc, 8, len & 0xff); + scsi_cmd_set(udev, &sc, 9, 0); + err = scsi_cmd_run(udev, &sc, fd, toc, len); + if ((err != 0)) { + info_scsi_cmd_err(udev, "READ TOC (tracks)", err); + return -1; + } + + /* Take care to not iterate beyond the last valid track as specified in + * the TOC, but also avoid going beyond the TOC length, just in case + * the last track number is invalidly large */ + for (p = toc+4, i = 4; i < len-8 && num_tracks > 0; i += 8, p += 8, --num_tracks) { + unsigned int block; + unsigned int is_data_track; + + is_data_track = (p[1] & 0x04) != 0; + + block = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7]; + info(udev, "track=%u info=0x%x(%s) start_block=%u\n", + p[2], p[1] & 0x0f, is_data_track ? "data":"audio", block); + + if (is_data_track) + cd_media_track_count_data++; + else + cd_media_track_count_audio++; + } + + scsi_cmd_init(udev, &sc); + scsi_cmd_set(udev, &sc, 0, 0x43); + scsi_cmd_set(udev, &sc, 2, 1); /* Session Info */ + scsi_cmd_set(udev, &sc, 8, sizeof(header)); + scsi_cmd_set(udev, &sc, 9, 0); + err = scsi_cmd_run(udev, &sc, fd, header, sizeof(header)); + if ((err != 0)) { + info_scsi_cmd_err(udev, "READ TOC (multi session)", err); + return -1; + } + len = header[4+4] << 24 | header[4+5] << 16 | header[4+6] << 8 | header[4+7]; + info(udev, "last track %u starts at block %u\n", header[4+2], len); + cd_media_session_last_offset = (unsigned long long int)len * 2048; + return 0; +} + +int main(int argc, char *argv[]) +{ + struct udev *udev; + static const struct option options[] = { + { "lock-media", no_argument, NULL, 'l' }, + { "unlock-media", no_argument, NULL, 'u' }, + { "eject-media", no_argument, NULL, 'e' }, + { "debug", no_argument, NULL, 'd' }, + { "help", no_argument, NULL, 'h' }, + {} + }; + bool eject = false; + bool lock = false; + bool unlock = false; + const char *node = NULL; + int fd = -1; + int cnt; + int rc = 0; + + udev = udev_new(); + if (udev == NULL) + goto exit; + + udev_log_init("cdrom_id"); + udev_set_log_fn(udev, log_fn); + + while (1) { + int option; + + option = getopt_long(argc, argv, "deluh", options, NULL); + if (option == -1) + break; + + switch (option) { + case 'l': + lock = true; + break; + case 'u': + unlock = true; + break; + case 'e': + eject = true; + break; + case 'd': + debug = true; + if (udev_get_log_priority(udev) < LOG_INFO) + udev_set_log_priority(udev, LOG_INFO); + break; + case 'h': + printf("Usage: cdrom_id [options] \n" + " --lock-media lock the media (to enable eject request events)\n" + " --unlock-media unlock the media\n" + " --eject-media eject the media\n" + " --debug debug to stderr\n" + " --help print this help text\n\n"); + goto exit; + default: + rc = 1; + goto exit; + } + } + + node = argv[optind]; + if (!node) { + err(udev, "no device\n"); + fprintf(stderr, "no device\n"); + rc = 1; + goto exit; + } + + srand((unsigned int)getpid()); + for (cnt = 20; cnt > 0; cnt--) { + struct timespec duration; + + fd = open(node, O_RDONLY|O_NONBLOCK|(is_mounted(node) ? 0 : O_EXCL)); + if (fd >= 0 || errno != EBUSY) + break; + duration.tv_sec = 0; + duration.tv_nsec = (100 * 1000 * 1000) + (rand() % 100 * 1000 * 1000); + nanosleep(&duration, NULL); + } + if (fd < 0) { + info(udev, "unable to open '%s'\n", node); + fprintf(stderr, "unable to open '%s'\n", node); + rc = 1; + goto exit; + } + info(udev, "probing: '%s'\n", node); + + /* same data as original cdrom_id */ + if (cd_capability_compat(udev, fd) < 0) { + rc = 1; + goto exit; + } + + /* check for media - don't bail if there's no media as we still need to + * to read profiles */ + cd_media_compat(udev, fd); + + /* check if drive talks MMC */ + if (cd_inquiry(udev, fd) < 0) + goto work; + + /* read drive and possibly current profile */ + if (cd_profiles(udev, fd) != 0) + goto work; + + /* at this point we are guaranteed to have media in the drive - find out more about it */ + + /* get session/track info */ + cd_media_toc(udev, fd); + + /* get writable media state */ + cd_media_info(udev, fd); + +work: + /* lock the media, so we enable eject button events */ + if (lock && cd_media) { + info(udev, "PREVENT_ALLOW_MEDIUM_REMOVAL (lock)\n"); + media_lock(udev, fd, true); + } + + if (unlock && cd_media) { + info(udev, "PREVENT_ALLOW_MEDIUM_REMOVAL (unlock)\n"); + media_lock(udev, fd, false); + } + + if (eject) { + info(udev, "PREVENT_ALLOW_MEDIUM_REMOVAL (unlock)\n"); + media_lock(udev, fd, false); + info(udev, "START_STOP_UNIT (eject)\n"); + media_eject(udev, fd); + } + + printf("ID_CDROM=1\n"); + if (cd_cd_rom) + printf("ID_CDROM_CD=1\n"); + if (cd_cd_r) + printf("ID_CDROM_CD_R=1\n"); + if (cd_cd_rw) + printf("ID_CDROM_CD_RW=1\n"); + if (cd_dvd_rom) + printf("ID_CDROM_DVD=1\n"); + if (cd_dvd_r) + printf("ID_CDROM_DVD_R=1\n"); + if (cd_dvd_rw) + printf("ID_CDROM_DVD_RW=1\n"); + if (cd_dvd_ram) + printf("ID_CDROM_DVD_RAM=1\n"); + if (cd_dvd_plus_r) + printf("ID_CDROM_DVD_PLUS_R=1\n"); + if (cd_dvd_plus_rw) + printf("ID_CDROM_DVD_PLUS_RW=1\n"); + if (cd_dvd_plus_r_dl) + printf("ID_CDROM_DVD_PLUS_R_DL=1\n"); + if (cd_dvd_plus_rw_dl) + printf("ID_CDROM_DVD_PLUS_RW_DL=1\n"); + if (cd_bd) + printf("ID_CDROM_BD=1\n"); + if (cd_bd_r) + printf("ID_CDROM_BD_R=1\n"); + if (cd_bd_re) + printf("ID_CDROM_BD_RE=1\n"); + if (cd_hddvd) + printf("ID_CDROM_HDDVD=1\n"); + if (cd_hddvd_r) + printf("ID_CDROM_HDDVD_R=1\n"); + if (cd_hddvd_rw) + printf("ID_CDROM_HDDVD_RW=1\n"); + if (cd_mo) + printf("ID_CDROM_MO=1\n"); + if (cd_mrw) + printf("ID_CDROM_MRW=1\n"); + if (cd_mrw_w) + printf("ID_CDROM_MRW_W=1\n"); + + if (cd_media) + printf("ID_CDROM_MEDIA=1\n"); + if (cd_media_mo) + printf("ID_CDROM_MEDIA_MO=1\n"); + if (cd_media_mrw) + printf("ID_CDROM_MEDIA_MRW=1\n"); + if (cd_media_mrw_w) + printf("ID_CDROM_MEDIA_MRW_W=1\n"); + if (cd_media_cd_rom) + printf("ID_CDROM_MEDIA_CD=1\n"); + if (cd_media_cd_r) + printf("ID_CDROM_MEDIA_CD_R=1\n"); + if (cd_media_cd_rw) + printf("ID_CDROM_MEDIA_CD_RW=1\n"); + if (cd_media_dvd_rom) + printf("ID_CDROM_MEDIA_DVD=1\n"); + if (cd_media_dvd_r) + printf("ID_CDROM_MEDIA_DVD_R=1\n"); + if (cd_media_dvd_ram) + printf("ID_CDROM_MEDIA_DVD_RAM=1\n"); + if (cd_media_dvd_rw) + printf("ID_CDROM_MEDIA_DVD_RW=1\n"); + if (cd_media_dvd_plus_r) + printf("ID_CDROM_MEDIA_DVD_PLUS_R=1\n"); + if (cd_media_dvd_plus_rw) + printf("ID_CDROM_MEDIA_DVD_PLUS_RW=1\n"); + if (cd_media_dvd_plus_rw_dl) + printf("ID_CDROM_MEDIA_DVD_PLUS_RW_DL=1\n"); + if (cd_media_dvd_plus_r_dl) + printf("ID_CDROM_MEDIA_DVD_PLUS_R_DL=1\n"); + if (cd_media_bd) + printf("ID_CDROM_MEDIA_BD=1\n"); + if (cd_media_bd_r) + printf("ID_CDROM_MEDIA_BD_R=1\n"); + if (cd_media_bd_re) + printf("ID_CDROM_MEDIA_BD_RE=1\n"); + if (cd_media_hddvd) + printf("ID_CDROM_MEDIA_HDDVD=1\n"); + if (cd_media_hddvd_r) + printf("ID_CDROM_MEDIA_HDDVD_R=1\n"); + if (cd_media_hddvd_rw) + printf("ID_CDROM_MEDIA_HDDVD_RW=1\n"); + + if (cd_media_state != NULL) + printf("ID_CDROM_MEDIA_STATE=%s\n", cd_media_state); + if (cd_media_session_next > 0) + printf("ID_CDROM_MEDIA_SESSION_NEXT=%d\n", cd_media_session_next); + if (cd_media_session_count > 0) + printf("ID_CDROM_MEDIA_SESSION_COUNT=%d\n", cd_media_session_count); + if (cd_media_session_count > 1 && cd_media_session_last_offset > 0) + printf("ID_CDROM_MEDIA_SESSION_LAST_OFFSET=%llu\n", cd_media_session_last_offset); + if (cd_media_track_count > 0) + printf("ID_CDROM_MEDIA_TRACK_COUNT=%d\n", cd_media_track_count); + if (cd_media_track_count_audio > 0) + printf("ID_CDROM_MEDIA_TRACK_COUNT_AUDIO=%d\n", cd_media_track_count_audio); + if (cd_media_track_count_data > 0) + printf("ID_CDROM_MEDIA_TRACK_COUNT_DATA=%d\n", cd_media_track_count_data); +exit: + if (fd >= 0) + close(fd); + udev_unref(udev); + udev_log_close(); + return rc; +} diff --git a/src/udev/collect/collect.c b/src/udev/collect/collect.c new file mode 100644 index 0000000000..076fe479e2 --- /dev/null +++ b/src/udev/collect/collect.c @@ -0,0 +1,473 @@ +/* + * Collect variables across events. + * + * usage: collect [--add|--remove] + * + * Adds ID to the list governed by . + * must be part of the ID list . + * If all IDs given by are listed (ie collect has been + * invoked for each ID in ) collect returns 0, the + * number of missing IDs otherwise. + * A negative number is returned on error. + * + * Copyright(C) 2007, Hannes Reinecke + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libudev.h" +#include "libudev-private.h" + +#define BUFSIZE 16 +#define UDEV_ALARM_TIMEOUT 180 + +enum collect_state { + STATE_NONE, + STATE_OLD, + STATE_CONFIRMED, +}; + +struct _mate { + struct udev_list_node node; + char *name; + enum collect_state state; +}; + +static struct udev_list_node bunch; +static int debug; + +/* This can increase dynamically */ +static size_t bufsize = BUFSIZE; + +static struct _mate *node_to_mate(struct udev_list_node *node) +{ + char *mate; + + mate = (char *)node; + mate -= offsetof(struct _mate, node); + return (struct _mate *)mate; +} + +static void sig_alrm(int signo) +{ + exit(4); +} + +static void usage(void) +{ + printf("usage: collect [--add|--remove] [--debug] \n" + "\n" + " Adds ID to the list governed by .\n" + " must be part of the list .\n" + " If all IDs given by are listed (ie collect has been\n" + " invoked for each ID in ) collect returns 0, the\n" + " number of missing IDs otherwise.\n" + " On error a negative number is returned.\n" + "\n"); +} + +/* + * prepare + * + * Prepares the database file + */ +static int prepare(char *dir, char *filename) +{ + struct stat statbuf; + char buf[512]; + int fd; + + if (stat(dir, &statbuf) < 0) + mkdir(dir, 0700); + + sprintf(buf, "%s/%s", dir, filename); + + fd = open(buf,O_RDWR|O_CREAT, S_IRUSR|S_IWUSR); + if (fd < 0) + fprintf(stderr, "Cannot open %s: %s\n", buf, strerror(errno)); + + if (lockf(fd,F_TLOCK,0) < 0) { + if (debug) + fprintf(stderr, "Lock taken, wait for %d seconds\n", UDEV_ALARM_TIMEOUT); + if (errno == EAGAIN || errno == EACCES) { + alarm(UDEV_ALARM_TIMEOUT); + lockf(fd, F_LOCK, 0); + if (debug) + fprintf(stderr, "Acquired lock on %s\n", buf); + } else { + if (debug) + fprintf(stderr, "Could not get lock on %s: %s\n", buf, strerror(errno)); + } + } + + return fd; +} + +/* + * Read checkpoint file + * + * Tricky reading this. We allocate a buffer twice as large + * as we're going to read. Then we read into the upper half + * of that buffer and start parsing. + * Once we do _not_ find end-of-work terminator (whitespace + * character) we move the upper half to the lower half, + * adjust the read pointer and read the next bit. + * Quite clever methinks :-) + * I should become a programmer ... + * + * Yes, one could have used fgets() for this. But then we'd + * have to use freopen etc which I found quite tedious. + */ +static int checkout(int fd) +{ + int len; + char *buf, *ptr, *word = NULL; + struct _mate *him; + + restart: + len = bufsize >> 1; + buf = calloc(1,bufsize + 1); + if (!buf) { + fprintf(stderr, "Out of memory\n"); + return -1; + } + memset(buf, ' ', bufsize); + ptr = buf + len; + while ((read(fd, buf + len, len)) > 0) { + while (ptr && *ptr) { + word = ptr; + ptr = strpbrk(word," \n\t\r"); + if (!ptr && word < (buf + len)) { + bufsize = bufsize << 1; + if (debug) + fprintf(stderr, "ID overflow, restarting with size %zi\n", bufsize); + free(buf); + lseek(fd, 0, SEEK_SET); + goto restart; + } + if (ptr) { + *ptr = '\0'; + ptr++; + if (!strlen(word)) + continue; + + if (debug) + fprintf(stderr, "Found word %s\n", word); + him = malloc(sizeof (struct _mate)); + him->name = strdup(word); + him->state = STATE_OLD; + udev_list_node_append(&him->node, &bunch); + word = NULL; + } + } + memcpy(buf, buf + len, len); + memset(buf + len, ' ', len); + + if (!ptr) + ptr = word; + if (!ptr) + break; + ptr -= len; + } + + free(buf); + return 0; +} + +/* + * invite + * + * Adds a new ID 'us' to the internal list, + * marks it as confirmed. + */ +static void invite(char *us) +{ + struct udev_list_node *him_node; + struct _mate *who = NULL; + + if (debug) + fprintf(stderr, "Adding ID '%s'\n", us); + + udev_list_node_foreach(him_node, &bunch) { + struct _mate *him = node_to_mate(him_node); + + if (!strcmp(him->name, us)) { + him->state = STATE_CONFIRMED; + who = him; + } + } + if (debug && !who) + fprintf(stderr, "ID '%s' not in database\n", us); + +} + +/* + * reject + * + * Marks the ID 'us' as invalid, + * causing it to be removed when the + * list is written out. + */ +static void reject(char *us) +{ + struct udev_list_node *him_node; + struct _mate *who = NULL; + + if (debug) + fprintf(stderr, "Removing ID '%s'\n", us); + + udev_list_node_foreach(him_node, &bunch) { + struct _mate *him = node_to_mate(him_node); + + if (!strcmp(him->name, us)) { + him->state = STATE_NONE; + who = him; + } + } + if (debug && !who) + fprintf(stderr, "ID '%s' not in database\n", us); +} + +/* + * kickout + * + * Remove all IDs in the internal list which are not part + * of the list passed via the commandline. + */ +static void kickout(void) +{ + struct udev_list_node *him_node; + struct udev_list_node *tmp; + + udev_list_node_foreach_safe(him_node, tmp, &bunch) { + struct _mate *him = node_to_mate(him_node); + + if (him->state == STATE_OLD) { + udev_list_node_remove(&him->node); + free(him->name); + free(him); + } + } +} + +/* + * missing + * + * Counts all missing IDs in the internal list. + */ +static int missing(int fd) +{ + char *buf; + int ret = 0; + struct udev_list_node *him_node; + + buf = malloc(bufsize); + if (!buf) + return -1; + + udev_list_node_foreach(him_node, &bunch) { + struct _mate *him = node_to_mate(him_node); + + if (him->state == STATE_NONE) { + ret++; + } else { + while (strlen(him->name)+1 >= bufsize) { + char *tmpbuf; + + bufsize = bufsize << 1; + tmpbuf = realloc(buf, bufsize); + if (!tmpbuf) { + free(buf); + return -1; + } + buf = tmpbuf; + } + snprintf(buf, strlen(him->name)+2, "%s ", him->name); + write(fd, buf, strlen(buf)); + } + } + + free(buf); + return ret; +} + +/* + * everybody + * + * Prints out the status of the internal list. + */ +static void everybody(void) +{ + struct udev_list_node *him_node; + const char *state = ""; + + udev_list_node_foreach(him_node, &bunch) { + struct _mate *him = node_to_mate(him_node); + + switch (him->state) { + case STATE_NONE: + state = "none"; + break; + case STATE_OLD: + state = "old"; + break; + case STATE_CONFIRMED: + state = "confirmed"; + break; + } + fprintf(stderr, "ID: %s=%s\n", him->name, state); + } +} + +int main(int argc, char **argv) +{ + struct udev *udev; + static const struct option options[] = { + { "add", no_argument, NULL, 'a' }, + { "remove", no_argument, NULL, 'r' }, + { "debug", no_argument, NULL, 'd' }, + { "help", no_argument, NULL, 'h' }, + {} + }; + int argi; + char *checkpoint, *us; + int fd; + int i; + int ret = EXIT_SUCCESS; + int prune = 0; + char tmpdir[UTIL_PATH_SIZE]; + + udev = udev_new(); + if (udev == NULL) { + ret = EXIT_FAILURE; + goto exit; + } + + while (1) { + int option; + + option = getopt_long(argc, argv, "ardh", options, NULL); + if (option == -1) + break; + + switch (option) { + case 'a': + prune = 0; + break; + case 'r': + prune = 1; + break; + case 'd': + debug = 1; + break; + case 'h': + usage(); + goto exit; + default: + ret = 1; + goto exit; + } + } + + argi = optind; + if (argi + 2 > argc) { + printf("Missing parameter(s)\n"); + ret = 1; + goto exit; + } + checkpoint = argv[argi++]; + us = argv[argi++]; + + if (signal(SIGALRM, sig_alrm) == SIG_ERR) { + fprintf(stderr, "Cannot set SIGALRM: %s\n", strerror(errno)); + ret = 2; + goto exit; + } + + udev_list_node_init(&bunch); + + if (debug) + fprintf(stderr, "Using checkpoint '%s'\n", checkpoint); + + util_strscpyl(tmpdir, sizeof(tmpdir), udev_get_run_path(udev), "/collect", NULL); + fd = prepare(tmpdir, checkpoint); + if (fd < 0) { + ret = 3; + goto out; + } + + if (checkout(fd) < 0) { + ret = 2; + goto out; + } + + for (i = argi; i < argc; i++) { + struct udev_list_node *him_node; + struct _mate *who; + + who = NULL; + udev_list_node_foreach(him_node, &bunch) { + struct _mate *him = node_to_mate(him_node); + + if (!strcmp(him->name, argv[i])) + who = him; + } + if (!who) { + struct _mate *him; + + if (debug) + fprintf(stderr, "ID %s: not in database\n", argv[i]); + him = malloc(sizeof (struct _mate)); + him->name = malloc(strlen(argv[i]) + 1); + strcpy(him->name, argv[i]); + him->state = STATE_NONE; + udev_list_node_append(&him->node, &bunch); + } else { + if (debug) + fprintf(stderr, "ID %s: found in database\n", argv[i]); + who->state = STATE_CONFIRMED; + } + } + + if (prune) + reject(us); + else + invite(us); + + if (debug) { + everybody(); + fprintf(stderr, "Prune lists\n"); + } + kickout(); + + lseek(fd, 0, SEEK_SET); + ftruncate(fd, 0); + ret = missing(fd); + + lockf(fd, F_ULOCK, 0); + close(fd); +out: + if (debug) + everybody(); + if (ret >= 0) + printf("COLLECT_%s=%d\n", checkpoint, ret); +exit: + udev_unref(udev); + return ret; +} diff --git a/src/udev/configure.ac b/src/udev/configure.ac deleted file mode 100644 index b31b62f289..0000000000 --- a/src/udev/configure.ac +++ /dev/null @@ -1,242 +0,0 @@ -AC_PREREQ(2.60) -AC_INIT([udev], - [182], - [linux-hotplug@vger.kernel.org], - [udev], - [http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev.html]) -AC_CONFIG_SRCDIR([src/udevd.c]) -AC_CONFIG_AUX_DIR([build-aux]) -AM_INIT_AUTOMAKE([check-news foreign 1.11 -Wall -Wno-portability silent-rules tar-pax no-dist-gzip dist-xz subdir-objects]) -AC_USE_SYSTEM_EXTENSIONS -AC_SYS_LARGEFILE -AC_CONFIG_MACRO_DIR([m4]) -AM_SILENT_RULES([yes]) -LT_INIT([disable-static]) -AC_PROG_AWK -AC_PROG_SED -AC_PROG_MKDIR_P -GTK_DOC_CHECK(1.10) -AC_PREFIX_DEFAULT([/usr]) - -AC_PATH_PROG([XSLTPROC], [xsltproc]) -AM_CONDITIONAL(HAVE_XSLTPROC, test x"$XSLTPROC" != x) - -AC_SEARCH_LIBS([clock_gettime], [rt], [], [AC_MSG_ERROR([POSIX RT library not found])]) - -PKG_CHECK_MODULES(BLKID, blkid >= 2.20) - -PKG_CHECK_MODULES(KMOD, libkmod >= 5) - -AC_ARG_WITH([rootprefix], - AS_HELP_STRING([--with-rootprefix=DIR], [rootfs directory prefix for config files and kernel modules]), - [], [with_rootprefix=${ac_default_prefix}]) -AC_SUBST([rootprefix], [$with_rootprefix]) - -AC_ARG_WITH([rootlibdir], - AS_HELP_STRING([--with-rootlibdir=DIR], [rootfs directory to install shared libraries]), - [], [with_rootlibdir=$libdir]) -AC_SUBST([rootlib_execdir], [$with_rootlibdir]) - -AC_ARG_WITH([selinux], - AS_HELP_STRING([--with-selinux], [enable SELinux support]), - [], [with_selinux=no]) -AS_IF([test "x$with_selinux" = "xyes"], [ - LIBS_save=$LIBS - AC_CHECK_LIB(selinux, getprevcon, - [], - AC_MSG_ERROR([SELinux selected but libselinux not found])) - LIBS=$LIBS_save - SELINUX_LIBS="-lselinux -lsepol" - AC_DEFINE(WITH_SELINUX, [1] ,[SELinux support.]) -]) -AC_SUBST([SELINUX_LIBS]) -AM_CONDITIONAL(WITH_SELINUX, [test "x$with_selinux" = "xyes"]) - -AC_ARG_ENABLE([debug], - AS_HELP_STRING([--enable-debug], [enable debug messages @<:@default=disabled@:>@]), - [], [enable_debug=no]) -AS_IF([test "x$enable_debug" = "xyes"], [ AC_DEFINE(ENABLE_DEBUG, [1], [Debug messages.]) ]) - -AC_ARG_ENABLE([logging], - AS_HELP_STRING([--disable-logging], [disable system logging @<:@default=enabled@:>@]), - [], enable_logging=yes) -AS_IF([test "x$enable_logging" = "xyes"], [ AC_DEFINE(ENABLE_LOGGING, [1], [System logging.]) ]) - -AC_ARG_ENABLE([manpages], - AS_HELP_STRING([--disable-manpages], [disable man pages @<:@default=enabled@:>@]), - [], enable_manpages=yes) -AM_CONDITIONAL([ENABLE_MANPAGES], [test "x$enable_manpages" = "xyes"]) - -if test "x$cross_compiling" = "xno" ; then - AC_CHECK_FILES([/usr/share/pci.ids], [pciids=/usr/share/pci.ids]) - AC_CHECK_FILES([/usr/share/hwdata/pci.ids], [pciids=/usr/share/hwdata/pci.ids]) - AC_CHECK_FILES([/usr/share/misc/pci.ids], [pciids=/usr/share/misc/pci.ids]) -fi - -AC_ARG_WITH(usb-ids-path, - [AS_HELP_STRING([--with-usb-ids-path=DIR], [Path to usb.ids file])], - [USB_DATABASE=${withval}], - [if test -n "$usbids" ; then - USB_DATABASE="$usbids" - else - PKG_CHECK_MODULES(USBUTILS, usbutils >= 0.82) - AC_SUBST([USB_DATABASE], [$($PKG_CONFIG --variable=usbids usbutils)]) - fi]) -AC_MSG_CHECKING([for USB database location]) -AC_MSG_RESULT([$USB_DATABASE]) -AC_SUBST(USB_DATABASE) - -AC_ARG_WITH(pci-ids-path, - [AS_HELP_STRING([--with-pci-ids-path=DIR], [Path to pci.ids file])], - [PCI_DATABASE=${withval}], - [if test -n "$pciids" ; then - PCI_DATABASE="$pciids" - else - AC_MSG_ERROR([pci.ids not found, try --with-pci-ids-path=]) - fi]) -AC_MSG_CHECKING([for PCI database location]) -AC_MSG_RESULT([$PCI_DATABASE]) -AC_SUBST(PCI_DATABASE) - -AC_ARG_WITH(firmware-path, - AS_HELP_STRING([--with-firmware-path=DIR[[[:DIR[...]]]]], - [Firmware search path (default=ROOTPREFIX/lib/firmware/updates:ROOTPREFIX/lib/firmware)]), - [], [with_firmware_path="$rootprefix/lib/firmware/updates:$rootprefix/lib/firmware"]) -OLD_IFS=$IFS -IFS=: -for i in $with_firmware_path; do - if test "x${FIRMWARE_PATH}" = "x"; then - FIRMWARE_PATH="\\\"${i}/\\\"" - else - FIRMWARE_PATH="${FIRMWARE_PATH}, \\\"${i}/\\\"" - fi -done -IFS=$OLD_IFS -AC_SUBST([FIRMWARE_PATH], [$FIRMWARE_PATH]) - -AC_ARG_WITH([systemdsystemunitdir], - AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]), - [], [with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)]) -AS_IF([test "x$with_systemdsystemunitdir" != "xno"], [ AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir]) ]) -AM_CONDITIONAL(WITH_SYSTEMD, [test -n "$with_systemdsystemunitdir" -a "x$with_systemdsystemunitdir" != "xno" ]) - -# ------------------------------------------------------------------------------ -# GUdev - libudev gobject interface -# ------------------------------------------------------------------------------ -AC_ARG_ENABLE([gudev], - AS_HELP_STRING([--disable-gudev], [disable Gobject libudev support @<:@default=enabled@:>@]), - [], [enable_gudev=yes]) -AS_IF([test "x$enable_gudev" = "xyes"], [ PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.22.0 gobject-2.0 >= 2.22.0]) ]) - -AC_ARG_ENABLE([introspection], - AS_HELP_STRING([--disable-introspection], [disable GObject introspection @<:@default=enabled@:>@]), - [], [enable_introspection=yes]) -AS_IF([test "x$enable_introspection" = "xyes"], [ - PKG_CHECK_MODULES([INTROSPECTION], [gobject-introspection-1.0 >= 0.6.2]) - AC_DEFINE([ENABLE_INTROSPECTION], [1], [enable GObject introspection support]) - AC_SUBST([G_IR_SCANNER], [$($PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0)]) - AC_SUBST([G_IR_COMPILER], [$($PKG_CONFIG --variable=g_ir_compiler gobject-introspection-1.0)]) - AC_SUBST([G_IR_GENERATE], [$($PKG_CONFIG --variable=g_ir_generate gobject-introspection-1.0)]) - AC_SUBST([GIRDIR], [$($PKG_CONFIG --define-variable=datadir=${datadir} --variable=girdir gobject-introspection-1.0)]) - AC_SUBST([GIRTYPELIBDIR], [$($PKG_CONFIG --define-variable=libdir=${libdir} --variable=typelibdir gobject-introspection-1.0)]) -]) -AM_CONDITIONAL([ENABLE_INTROSPECTION], [test "x$enable_introspection" = "xyes"]) -AM_CONDITIONAL([ENABLE_GUDEV], [test "x$enable_gudev" = "xyes"]) - -# ------------------------------------------------------------------------------ -# keymap - map custom hardware's multimedia keys -# ------------------------------------------------------------------------------ -AC_ARG_ENABLE([keymap], - AS_HELP_STRING([--disable-keymap], [disable keymap fixup support @<:@default=enabled@:>@]), - [], [enable_keymap=yes]) -AS_IF([test "x$enable_keymap" = "xyes"], [ - AC_PATH_PROG([GPERF], [gperf]) - if test -z "$GPERF"; then - AC_MSG_ERROR([gperf is needed]) - fi - - AC_CHECK_HEADER([linux/input.h], [:], AC_MSG_ERROR([kernel headers not found])) - AC_SUBST([INCLUDE_PREFIX], [$(echo '#include ' | eval $ac_cpp -E - | sed -n '/linux\/input.h/ {s:.*"\(.*\)/linux/input.h".*:\1:; p; q}')]) -]) -AM_CONDITIONAL([ENABLE_KEYMAP], [test "x$enable_keymap" = "xyes"]) - -# ------------------------------------------------------------------------------ -# mtd_probe - autoloads FTL module for mtd devices -# ------------------------------------------------------------------------------ -AC_ARG_ENABLE([mtd_probe], - AS_HELP_STRING([--disable-mtd_probe], [disable MTD support @<:@default=enabled@:>@]), - [], [enable_mtd_probe=yes]) -AM_CONDITIONAL([ENABLE_MTD_PROBE], [test "x$enable_mtd_probe" = "xyes"]) - -# ------------------------------------------------------------------------------ -# rule_generator - persistent network and optical device rule generator -# ------------------------------------------------------------------------------ -AC_ARG_ENABLE([rule_generator], - AS_HELP_STRING([--enable-rule_generator], [enable persistent network + cdrom links support @<:@default=disabled@:>@]), - [], [enable_rule_generator=no]) -AM_CONDITIONAL([ENABLE_RULE_GENERATOR], [test "x$enable_rule_generator" = "xyes"]) - -# ------------------------------------------------------------------------------ -# create_floppy_devices - historical floppy kernel device nodes (/dev/fd0h1440, ...) -# ------------------------------------------------------------------------------ -AC_ARG_ENABLE([floppy], - AS_HELP_STRING([--enable-floppy], [enable legacy floppy support @<:@default=disabled@:>@]), - [], [enable_floppy=no]) -AM_CONDITIONAL([ENABLE_FLOPPY], [test "x$enable_floppy" = "xyes"]) - -my_CFLAGS="-Wall \ --Wmissing-declarations -Wmissing-prototypes \ --Wnested-externs -Wpointer-arith \ --Wpointer-arith -Wsign-compare -Wchar-subscripts \ --Wstrict-prototypes -Wshadow \ --Wformat-security -Wtype-limits" -AC_SUBST([my_CFLAGS]) - -AC_CONFIG_HEADERS(config.h) -AC_CONFIG_FILES([ - Makefile - src/docs/Makefile - src/docs/version.xml - src/gudev/docs/Makefile - src/gudev/docs/version.xml -]) - -AC_OUTPUT -AC_MSG_RESULT([ - $PACKAGE $VERSION - ======== - - prefix: ${prefix} - rootprefix: ${rootprefix} - sysconfdir: ${sysconfdir} - bindir: ${bindir} - libdir: ${libdir} - rootlibdir: ${rootlib_execdir} - libexecdir: ${libexecdir} - datarootdir: ${datarootdir} - mandir: ${mandir} - includedir: ${includedir} - include_prefix: ${INCLUDE_PREFIX} - systemdsystemunitdir: ${systemdsystemunitdir} - firmware path: ${FIRMWARE_PATH} - usb.ids: ${USB_DATABASE} - pci.ids: ${PCI_DATABASE} - - compiler: ${CC} - cflags: ${CFLAGS} - ldflags: ${LDFLAGS} - xsltproc: ${XSLTPROC} - gperf: ${GPERF} - - logging: ${enable_logging} - debug: ${enable_debug} - selinux: ${with_selinux} - - man pages ${enable_manpages} - gudev: ${enable_gudev} - gintrospection: ${enable_introspection} - keymap: ${enable_keymap} - mtd_probe: ${enable_mtd_probe} - rule_generator: ${enable_rule_generator} - floppy: ${enable_floppy} -]) diff --git a/src/udev/docs/.gitignore b/src/udev/docs/.gitignore new file mode 100644 index 0000000000..1fb9c51424 --- /dev/null +++ b/src/udev/docs/.gitignore @@ -0,0 +1,18 @@ +Makefile +libudev-overrides.txt +html/ +tmpl/ +xml/ +*.stamp +*.bak +version.xml +libudev-decl-list.txt +libudev-decl.txt +libudev-undeclared.txt +libudev-undocumented.txt +libudev-unused.txt +libudev.args +libudev.hierarchy +libudev.interfaces +libudev.prerequisites +libudev.signals diff --git a/src/udev/docs/Makefile.am b/src/udev/docs/Makefile.am new file mode 100644 index 0000000000..7cd4f9742c --- /dev/null +++ b/src/udev/docs/Makefile.am @@ -0,0 +1,99 @@ +## Process this file with automake to produce Makefile.in + +# We require automake 1.10 at least. +AUTOMAKE_OPTIONS = 1.10 + +# This is a blank Makefile.am for using gtk-doc. +# Copy this to your project's API docs directory and modify the variables to +# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples +# of using the various options. + +# The name of the module, e.g. 'glib'. +DOC_MODULE=libudev + +# Uncomment for versioned docs and specify the version of the module, e.g. '2'. +#DOC_MODULE_VERSION=2 + +# The top-level SGML file. You can change this if you want to. +DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.xml + +# The directory containing the source code. Relative to $(srcdir). +# gtk-doc will search all .c & .h files beneath here for inline comments +# documenting the functions and macros. +# e.g. DOC_SOURCE_DIR=../../../gtk +DOC_SOURCE_DIR=$(top_srcdir)/src/udev + +# Extra options to pass to gtkdoc-scangobj. Not normally needed. +SCANGOBJ_OPTIONS= + +# Extra options to supply to gtkdoc-scan. +# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" +SCAN_OPTIONS= + +# Extra options to supply to gtkdoc-mkdb. +# e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml +MKDB_OPTIONS=--sgml-mode --output-format=xml --name-space udev + +# Extra options to supply to gtkdoc-mktmpl +# e.g. MKTMPL_OPTIONS=--only-section-tmpl +MKTMPL_OPTIONS= + +# Extra options to supply to gtkdoc-mkhtml +MKHTML_OPTIONS=--path=$(abs_srcdir) --path=$(abs_builddir) + +# Extra options to supply to gtkdoc-fixref. Not normally needed. +# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html +FIXXREF_OPTIONS= + +# Used for dependencies. The docs will be rebuilt if any of these change. +# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h +# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c +HFILE_GLOB=$(top_srcdir)/src/udev/libudev*.h +CFILE_GLOB=$(top_srcdir)/src/udev/libudev*.c + +# Extra header to include when scanning, which are not under DOC_SOURCE_DIR +# e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h +EXTRA_HFILES= + +# Header files to ignore when scanning. Use base file name, no paths +# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h +IGNORE_HFILES= libudev-private.h + +# Images to copy into HTML directory. +# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png +HTML_IMAGES= + +# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). +# e.g. content_files=running.sgml building.sgml changes-2.0.sgml +content_files = version.xml + +# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded +# These files must be listed here *and* in content_files +# e.g. expand_content_files=running.sgml +expand_content_files= + +# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. +# Only needed if you are using gtkdoc-scangobj to dynamically query widget +# signals and properties. +# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) +# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) +GTKDOC_CFLAGS= +GTKDOC_LIBS= + +# This includes the standard gtk-doc make rules, copied by gtkdocize. +include $(top_srcdir)/gtk-doc.make + +# Other files to distribute +# e.g. EXTRA_DIST += version.xml.in +EXTRA_DIST += version.xml.in + +# Files not to distribute +# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types +# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt +#DISTCLEANFILES += + +# Comment this out if you want your docs-status tested during 'make check' +if ENABLE_GTK_DOC +#TESTS_ENVIRONMENT = cd $(srcsrc) +#TESTS = $(GTKDOC_CHECK) +endif diff --git a/src/udev/docs/libudev-docs.xml b/src/udev/docs/libudev-docs.xml new file mode 100644 index 0000000000..b7feb45529 --- /dev/null +++ b/src/udev/docs/libudev-docs.xml @@ -0,0 +1,32 @@ + + +]> + + + libudev Reference Manual + for libudev version &version; + + 2009-2011 + Kay Sievers <kay.sievers@vrfy.org> + + + + + libudev + + + + + + + + + + + API Index + + + diff --git a/src/udev/docs/libudev-sections.txt b/src/udev/docs/libudev-sections.txt new file mode 100644 index 0000000000..15c3e934b5 --- /dev/null +++ b/src/udev/docs/libudev-sections.txt @@ -0,0 +1,127 @@ +
+libudev +udev +udev +udev_ref +udev_unref +udev_new +udev_set_log_fn +udev_get_log_priority +udev_set_log_priority +udev_get_sys_path +udev_get_dev_path +udev_get_run_path +udev_get_userdata +udev_set_userdata +
+ +
+libudev-list +udev_list +udev_list_entry +udev_list_entry_get_next +udev_list_entry_get_by_name +udev_list_entry_get_name +udev_list_entry_get_value +udev_list_entry_foreach +
+ +
+libudev-device +udev_device +udev_device +udev_device_ref +udev_device_unref +udev_device_get_udev +udev_device_new_from_syspath +udev_device_new_from_devnum +udev_device_new_from_subsystem_sysname +udev_device_new_from_environment +udev_device_get_parent +udev_device_get_parent_with_subsystem_devtype +udev_device_get_devpath +udev_device_get_subsystem +udev_device_get_devtype +udev_device_get_syspath +udev_device_get_sysname +udev_device_get_sysnum +udev_device_get_devnode +udev_device_get_is_initialized +udev_device_get_devlinks_list_entry +udev_device_get_properties_list_entry +udev_device_get_tags_list_entry +udev_device_get_property_value +udev_device_get_driver +udev_device_get_devnum +udev_device_get_action +udev_device_get_sysattr_value +udev_device_get_sysattr_list_entry +udev_device_get_seqnum +udev_device_get_usec_since_initialized +udev_device_has_tag +
+ +
+libudev-monitor +udev_monitor +udev_monitor +udev_monitor_ref +udev_monitor_unref +udev_monitor_get_udev +udev_monitor_new_from_netlink +udev_monitor_new_from_socket +udev_monitor_enable_receiving +udev_monitor_set_receive_buffer_size +udev_monitor_get_fd +udev_monitor_receive_device +udev_monitor_filter_add_match_subsystem_devtype +udev_monitor_filter_add_match_tag +udev_monitor_filter_update +udev_monitor_filter_remove +
+ +
+libudev-enumerate +udev_enumerate +udev_enumerate +udev_enumerate_ref +udev_enumerate_unref +udev_enumerate_get_udev +udev_enumerate_new +udev_enumerate_add_match_subsystem +udev_enumerate_add_nomatch_subsystem +udev_enumerate_add_match_sysattr +udev_enumerate_add_nomatch_sysattr +udev_enumerate_add_match_property +udev_enumerate_add_match_tag +udev_enumerate_add_match_parent +udev_enumerate_add_match_is_initialized +udev_enumerate_add_match_sysname +udev_enumerate_add_syspath +udev_enumerate_scan_devices +udev_enumerate_scan_subsystems +udev_enumerate_get_list_entry +
+ +
+libudev-queue +udev_queue +udev_queue +udev_queue_ref +udev_queue_unref +udev_queue_get_udev +udev_queue_new +udev_queue_get_udev_is_active +udev_queue_get_queue_is_empty +udev_queue_get_seqnum_is_finished +udev_queue_get_seqnum_sequence_is_finished +udev_queue_get_queued_list_entry +udev_queue_get_kernel_seqnum +udev_queue_get_udev_seqnum +
+ +
+libudev-util +udev_util +udev_util_encode_string +
diff --git a/src/udev/docs/libudev.types b/src/udev/docs/libudev.types new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/udev/docs/version.xml.in b/src/udev/docs/version.xml.in new file mode 100644 index 0000000000..d78bda9342 --- /dev/null +++ b/src/udev/docs/version.xml.in @@ -0,0 +1 @@ +@VERSION@ diff --git a/src/udev/gudev/.gitignore b/src/udev/gudev/.gitignore new file mode 100644 index 0000000000..d20fa523e4 --- /dev/null +++ b/src/udev/gudev/.gitignore @@ -0,0 +1,9 @@ +gtk-doc.make +docs/version.xml +gudev-1.0.pc +gudevenumtypes.c +gudevenumtypes.h +gudevmarshal.c +gudevmarshal.h +GUdev-1.0.gir +GUdev-1.0.typelib diff --git a/src/udev/gudev/docs/.gitignore b/src/udev/gudev/docs/.gitignore new file mode 100644 index 0000000000..5d7d73d9a6 --- /dev/null +++ b/src/udev/gudev/docs/.gitignore @@ -0,0 +1,17 @@ +Makefile +gudev-overrides.txt +gudev-decl-list.txt +gudev-decl.txt +gudev-undeclared.txt +gudev-undocumented.txt +gudev-unused.txt +gudev.args +gudev.hierarchy +gudev.interfaces +gudev.prerequisites +gudev.signals +html.stamp +html/* +xml/* +tmpl/* +*.stamp diff --git a/src/udev/gudev/docs/Makefile.am b/src/udev/gudev/docs/Makefile.am new file mode 100644 index 0000000000..036ef43026 --- /dev/null +++ b/src/udev/gudev/docs/Makefile.am @@ -0,0 +1,106 @@ +## Process this file with automake to produce Makefile.in + +# We require automake 1.10 at least. +AUTOMAKE_OPTIONS = 1.10 + +# This is a blank Makefile.am for using gtk-doc. +# Copy this to your project's API docs directory and modify the variables to +# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples +# of using the various options. + +# The name of the module, e.g. 'glib'. +DOC_MODULE=gudev + +# Uncomment for versioned docs and specify the version of the module, e.g. '2'. +#DOC_MODULE_VERSION=2 + +# The top-level SGML file. You can change this if you want to. +DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.xml + +# The directory containing the source code. Relative to $(srcdir). +# gtk-doc will search all .c & .h files beneath here for inline comments +# documenting the functions and macros. +# e.g. DOC_SOURCE_DIR=../../../gtk +DOC_SOURCE_DIR=$(top_srcdir)/src/udev/gudev + +# Extra options to pass to gtkdoc-scangobj. Not normally needed. +SCANGOBJ_OPTIONS= + +# Extra options to supply to gtkdoc-scan. +# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" +SCAN_OPTIONS= + +# Extra options to supply to gtkdoc-mkdb. +# e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml +MKDB_OPTIONS=--sgml-mode --output-format=xml --name-space=g_udev + +# Extra options to supply to gtkdoc-mktmpl +# e.g. MKTMPL_OPTIONS=--only-section-tmpl +MKTMPL_OPTIONS= + +# Extra options to supply to gtkdoc-mkhtml +MKHTML_OPTIONS=--path=$(abs_srcdir) --path=$(abs_builddir) + +# Extra options to supply to gtkdoc-fixref. Not normally needed. +# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html +FIXXREF_OPTIONS= + +# Used for dependencies. The docs will be rebuilt if any of these change. +# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h +# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c +HFILE_GLOB=$(top_srcdir)/src/udev/gudev/*.h +CFILE_GLOB=$(top_srcdir)/src/udev/gudev/*.c + +# Extra header to include when scanning, which are not under DOC_SOURCE_DIR +# e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h +EXTRA_HFILES= + +# Header files to ignore when scanning. Use base file name, no paths +# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h +IGNORE_HFILES= + +# Images to copy into HTML directory. +# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png +HTML_IMAGES= + +# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). +# e.g. content_files=running.sgml building.sgml changes-2.0.sgml +content_files = version.xml + +# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded +# These files must be listed here *and* in content_files +# e.g. expand_content_files=running.sgml +expand_content_files= + +# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. +# Only needed if you are using gtkdoc-scangobj to dynamically query widget +# signals and properties. +# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) +# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) +GTKDOC_CFLAGS = \ + $(DBUS_GLIB_CFLAGS) \ + $(GLIB_CFLAGS) \ + -I$(top_srcdir)/src/udev/gudev \ + -I$(top_builddir)/src/udev/gudev + +GTKDOC_LIBS = \ + $(GLIB_LIBS) \ + $(top_builddir)/libgudev-1.0.la + +# This includes the standard gtk-doc make rules, copied by gtkdocize. +include $(top_srcdir)/gtk-doc.make + +# Other files to distribute +# e.g. EXTRA_DIST += version.xml.in +EXTRA_DIST += version.xml.in + +# Files not to distribute +# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types +# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt +#DISTCLEANFILES += + +# Comment this out if you want your docs-status tested during 'make check' +if ENABLE_GTK_DOC +#TESTS_ENVIRONMENT = cd $(srcsrc) +#TESTS = $(GTKDOC_CHECK) +endif diff --git a/src/udev/gudev/docs/gudev-docs.xml b/src/udev/gudev/docs/gudev-docs.xml new file mode 100644 index 0000000000..f876c3bc05 --- /dev/null +++ b/src/udev/gudev/docs/gudev-docs.xml @@ -0,0 +1,93 @@ + + +]> + + + GUDev Reference Manual + For GUdev version &version; + + + David + Zeuthen + +
+ davidz@redhat.com +
+
+
+ + Bastien + Nocera + +
+ hadess@hadess.net +
+
+
+
+ + + 2011 + The GUDev Authors + + + + + Permission is granted to copy, distribute and/or modify this + document under the terms of the GNU Free + Documentation License, Version 1.1 or any later + version published by the Free Software Foundation with no + Invariant Sections, no Front-Cover Texts, and no Back-Cover + Texts. You may obtain a copy of the GNU Free + Documentation License from the Free Software + Foundation by visiting their Web site or by writing + to: + +
+ The Free Software Foundation, Inc., + 59 Temple Place - Suite 330, + Boston, MA 02111-1307, + USA +
+
+ + + Many of the names used by companies to distinguish their + products and services are claimed as trademarks. Where those + names appear in any freedesktop.org documentation, and those + trademarks are made aware to the members of the + freedesktop.org Project, the names have been printed in caps + or initial caps. + +
+
+ + + API Reference + + + This part presents the class and function reference for the + libgudev library. + + + + + + + + + Object Hierarchy + + + + Index + + + Index of new symbols in 165 + + + +
diff --git a/src/udev/gudev/docs/gudev-sections.txt b/src/udev/gudev/docs/gudev-sections.txt new file mode 100644 index 0000000000..213e1a7465 --- /dev/null +++ b/src/udev/gudev/docs/gudev-sections.txt @@ -0,0 +1,113 @@ +
+gudevclient +GUdevClient +GUdevClient +GUdevClientClass +GUdevDeviceType +GUdevDeviceNumber +g_udev_client_new +g_udev_client_query_by_subsystem +g_udev_client_query_by_device_number +g_udev_client_query_by_device_file +g_udev_client_query_by_sysfs_path +g_udev_client_query_by_subsystem_and_name + +G_UDEV_CLIENT +G_UDEV_IS_CLIENT +G_UDEV_TYPE_CLIENT +g_udev_client_get_type +G_UDEV_CLIENT_CLASS +G_UDEV_IS_CLIENT_CLASS +G_UDEV_CLIENT_GET_CLASS + +GUdevClientPrivate +
+ +
+gudevdevice +GUdevDevice +GUdevDevice +GUdevDeviceClass +g_udev_device_get_subsystem +g_udev_device_get_devtype +g_udev_device_get_name +g_udev_device_get_number +g_udev_device_get_sysfs_path +g_udev_device_get_driver +g_udev_device_get_action +g_udev_device_get_seqnum +g_udev_device_get_device_type +g_udev_device_get_device_number +g_udev_device_get_device_file +g_udev_device_get_device_file_symlinks +g_udev_device_get_parent +g_udev_device_get_parent_with_subsystem +g_udev_device_get_tags +g_udev_device_get_is_initialized +g_udev_device_get_usec_since_initialized +g_udev_device_get_property_keys +g_udev_device_has_property +g_udev_device_get_property +g_udev_device_get_property_as_int +g_udev_device_get_property_as_uint64 +g_udev_device_get_property_as_double +g_udev_device_get_property_as_boolean +g_udev_device_get_property_as_strv +g_udev_device_get_sysfs_attr +g_udev_device_get_sysfs_attr_as_int +g_udev_device_get_sysfs_attr_as_uint64 +g_udev_device_get_sysfs_attr_as_double +g_udev_device_get_sysfs_attr_as_boolean +g_udev_device_get_sysfs_attr_as_strv + +G_UDEV_DEVICE +G_UDEV_IS_DEVICE +G_UDEV_TYPE_DEVICE +g_udev_device_get_type +G_UDEV_DEVICE_CLASS +G_UDEV_IS_DEVICE_CLASS +G_UDEV_DEVICE_GET_CLASS + +GUdevDevicePrivate +
+ +
+gudevenumerator +GUdevEnumerator +GUdevEnumerator +GUdevEnumeratorClass +g_udev_enumerator_new +g_udev_enumerator_add_match_subsystem +g_udev_enumerator_add_nomatch_subsystem +g_udev_enumerator_add_match_sysfs_attr +g_udev_enumerator_add_nomatch_sysfs_attr +g_udev_enumerator_add_match_property +g_udev_enumerator_add_match_name +g_udev_enumerator_add_match_tag +g_udev_enumerator_add_match_is_initialized +g_udev_enumerator_add_sysfs_path +g_udev_enumerator_execute + +G_UDEV_ENUMERATOR +G_UDEV_IS_ENUMERATOR +G_UDEV_TYPE_ENUMERATOR +g_udev_enumerator_get_type +G_UDEV_ENUMERATOR_CLASS +G_UDEV_IS_ENUMERATOR_CLASS +G_UDEV_ENUMERATOR_GET_CLASS + +GUdevEnumeratorPrivate +
+ +
+gudevmarshal + +g_udev_marshal_VOID__STRING_OBJECT +
+ +
+gudevenumtypes + +G_TYPE_UDEV_DEVICE_TYPE +g_udev_device_type_get_type +
diff --git a/src/udev/gudev/docs/gudev.types b/src/udev/gudev/docs/gudev.types new file mode 100644 index 0000000000..a89857a04d --- /dev/null +++ b/src/udev/gudev/docs/gudev.types @@ -0,0 +1,4 @@ +g_udev_device_type_get_type +g_udev_device_get_type +g_udev_client_get_type +g_udev_enumerator_get_type diff --git a/src/udev/gudev/docs/version.xml.in b/src/udev/gudev/docs/version.xml.in new file mode 100644 index 0000000000..d78bda9342 --- /dev/null +++ b/src/udev/gudev/docs/version.xml.in @@ -0,0 +1 @@ +@VERSION@ diff --git a/src/udev/gudev/gjs-example.js b/src/udev/gudev/gjs-example.js new file mode 100755 index 0000000000..5586fd6a61 --- /dev/null +++ b/src/udev/gudev/gjs-example.js @@ -0,0 +1,75 @@ +#!/usr/bin/env gjs-console + +// This currently depends on the following patches to gjs +// +// http://bugzilla.gnome.org/show_bug.cgi?id=584558 +// http://bugzilla.gnome.org/show_bug.cgi?id=584560 +// http://bugzilla.gnome.org/show_bug.cgi?id=584568 + +const GUdev = imports.gi.GUdev; +const Mainloop = imports.mainloop; + +function print_device (device) { + print (" subsystem: " + device.get_subsystem ()); + print (" devtype: " + device.get_devtype ()); + print (" name: " + device.get_name ()); + print (" number: " + device.get_number ()); + print (" sysfs_path: " + device.get_sysfs_path ()); + print (" driver: " + device.get_driver ()); + print (" action: " + device.get_action ()); + print (" seqnum: " + device.get_seqnum ()); + print (" device type: " + device.get_device_type ()); + print (" device number: " + device.get_device_number ()); + print (" device file: " + device.get_device_file ()); + print (" device file symlinks: " + device.get_device_file_symlinks ()); + print (" foo: " + device.get_sysfs_attr_as_strv ("stat")); + var keys = device.get_property_keys (); + for (var n = 0; n < keys.length; n++) { + print (" " + keys[n] + "=" + device.get_property (keys[n])); + } +} + +function on_uevent (client, action, device) { + print ("action " + action + " on device " + device.get_sysfs_path()); + print_device (device); + print (""); +} + +var client = new GUdev.Client ({subsystems: ["block", "usb/usb_interface"]}); +client.connect ("uevent", on_uevent); + +var block_devices = client.query_by_subsystem ("block"); +for (var n = 0; n < block_devices.length; n++) { + print ("block device: " + block_devices[n].get_device_file ()); +} + +var d; + +d = client.query_by_device_number (GUdev.DeviceType.BLOCK, 0x0810); +if (d == null) { + print ("query_by_device_number 0x810 -> null"); +} else { + print ("query_by_device_number 0x810 -> " + d.get_device_file ()); + var dd = d.get_parent_with_subsystem ("usb", null); + print_device (dd); + print ("--------------------------------------------------------------------------"); + while (d != null) { + print_device (d); + print (""); + d = d.get_parent (); + } +} + +d = client.query_by_sysfs_path ("/sys/block/sda/sda1"); +print ("query_by_sysfs_path (\"/sys/block/sda1\") -> " + d.get_device_file ()); + +d = client.query_by_subsystem_and_name ("block", "sda2"); +print ("query_by_subsystem_and_name (\"block\", \"sda2\") -> " + d.get_device_file ()); + +d = client.query_by_device_file ("/dev/sda"); +print ("query_by_device_file (\"/dev/sda\") -> " + d.get_device_file ()); + +d = client.query_by_device_file ("/dev/block/8:0"); +print ("query_by_device_file (\"/dev/block/8:0\") -> " + d.get_device_file ()); + +Mainloop.run('udev-example'); diff --git a/src/udev/gudev/gudev-1.0.pc.in b/src/udev/gudev/gudev-1.0.pc.in new file mode 100644 index 0000000000..058262d767 --- /dev/null +++ b/src/udev/gudev/gudev-1.0.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: gudev-1.0 +Description: GObject bindings for libudev +Version: @VERSION@ +Requires: glib-2.0, gobject-2.0 +Libs: -L${libdir} -lgudev-1.0 +Cflags: -I${includedir}/gudev-1.0 diff --git a/src/udev/gudev/gudev.h b/src/udev/gudev/gudev.h new file mode 100644 index 0000000000..a313460817 --- /dev/null +++ b/src/udev/gudev/gudev.h @@ -0,0 +1,33 @@ +/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2008 David Zeuthen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __G_UDEV_H__ +#define __G_UDEV_H__ + +#define _GUDEV_INSIDE_GUDEV_H 1 +#include +#include +#include +#include +#include +#include +#undef _GUDEV_INSIDE_GUDEV_H + +#endif /* __G_UDEV_H__ */ diff --git a/src/udev/gudev/gudevclient.c b/src/udev/gudev/gudevclient.c new file mode 100644 index 0000000000..2b94102ac5 --- /dev/null +++ b/src/udev/gudev/gudevclient.c @@ -0,0 +1,527 @@ +/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2008-2010 David Zeuthen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "gudevclient.h" +#include "gudevdevice.h" +#include "gudevmarshal.h" +#include "gudevprivate.h" + +/** + * SECTION:gudevclient + * @short_description: Query devices and listen to uevents + * + * #GUdevClient is used to query information about devices on a Linux + * system from the Linux kernel and the udev device + * manager. + * + * Device information is retrieved from the kernel (through the + * sysfs filesystem) and the udev daemon (through a + * tmpfs filesystem) and presented through + * #GUdevDevice objects. This means that no blocking IO ever happens + * (in both cases, we are essentially just reading data from kernel + * memory) and as such there are no asynchronous versions of the + * provided methods. + * + * To get #GUdevDevice objects, use + * g_udev_client_query_by_subsystem(), + * g_udev_client_query_by_device_number(), + * g_udev_client_query_by_device_file(), + * g_udev_client_query_by_sysfs_path(), + * g_udev_client_query_by_subsystem_and_name() + * or the #GUdevEnumerator type. + * + * To listen to uevents, connect to the #GUdevClient::uevent signal. + */ + +struct _GUdevClientPrivate +{ + GSource *watch_source; + struct udev *udev; + struct udev_monitor *monitor; + + gchar **subsystems; +}; + +enum +{ + PROP_0, + PROP_SUBSYSTEMS, +}; + +enum +{ + UEVENT_SIGNAL, + LAST_SIGNAL, +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE (GUdevClient, g_udev_client, G_TYPE_OBJECT) + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +monitor_event (GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + GUdevClient *client = (GUdevClient *) data; + GUdevDevice *device; + struct udev_device *udevice; + + if (client->priv->monitor == NULL) + goto out; + udevice = udev_monitor_receive_device (client->priv->monitor); + if (udevice == NULL) + goto out; + + device = _g_udev_device_new (udevice); + udev_device_unref (udevice); + g_signal_emit (client, + signals[UEVENT_SIGNAL], + 0, + g_udev_device_get_action (device), + device); + g_object_unref (device); + + out: + return TRUE; +} + +static void +g_udev_client_finalize (GObject *object) +{ + GUdevClient *client = G_UDEV_CLIENT (object); + + if (client->priv->watch_source != NULL) + { + g_source_destroy (client->priv->watch_source); + client->priv->watch_source = NULL; + } + + if (client->priv->monitor != NULL) + { + udev_monitor_unref (client->priv->monitor); + client->priv->monitor = NULL; + } + + if (client->priv->udev != NULL) + { + udev_unref (client->priv->udev); + client->priv->udev = NULL; + } + + g_strfreev (client->priv->subsystems); + + if (G_OBJECT_CLASS (g_udev_client_parent_class)->finalize != NULL) + G_OBJECT_CLASS (g_udev_client_parent_class)->finalize (object); +} + +static void +g_udev_client_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GUdevClient *client = G_UDEV_CLIENT (object); + + switch (prop_id) + { + case PROP_SUBSYSTEMS: + if (client->priv->subsystems != NULL) + g_strfreev (client->priv->subsystems); + client->priv->subsystems = g_strdupv (g_value_get_boxed (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_udev_client_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GUdevClient *client = G_UDEV_CLIENT (object); + + switch (prop_id) + { + case PROP_SUBSYSTEMS: + g_value_set_boxed (value, client->priv->subsystems); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_udev_client_constructed (GObject *object) +{ + GUdevClient *client = G_UDEV_CLIENT (object); + GIOChannel *channel; + guint n; + + client->priv->udev = udev_new (); + + /* connect to event source */ + client->priv->monitor = udev_monitor_new_from_netlink (client->priv->udev, "udev"); + + //g_debug ("ss = %p", client->priv->subsystems); + + if (client->priv->subsystems != NULL) + { + /* install subsystem filters to only wake up for certain events */ + for (n = 0; client->priv->subsystems[n] != NULL; n++) + { + gchar *subsystem; + gchar *devtype; + gchar *s; + + subsystem = g_strdup (client->priv->subsystems[n]); + devtype = NULL; + + //g_debug ("s = '%s'", subsystem); + + s = strstr (subsystem, "/"); + if (s != NULL) + { + devtype = s + 1; + *s = '\0'; + } + + if (client->priv->monitor != NULL) + udev_monitor_filter_add_match_subsystem_devtype (client->priv->monitor, subsystem, devtype); + + g_free (subsystem); + } + + /* listen to events, and buffer them */ + if (client->priv->monitor != NULL) + { + udev_monitor_enable_receiving (client->priv->monitor); + channel = g_io_channel_unix_new (udev_monitor_get_fd (client->priv->monitor)); + client->priv->watch_source = g_io_create_watch (channel, G_IO_IN); + g_io_channel_unref (channel); + g_source_set_callback (client->priv->watch_source, (GSourceFunc) monitor_event, client, NULL); + g_source_attach (client->priv->watch_source, g_main_context_get_thread_default ()); + g_source_unref (client->priv->watch_source); + } + else + { + client->priv->watch_source = NULL; + } + } + + if (G_OBJECT_CLASS (g_udev_client_parent_class)->constructed != NULL) + G_OBJECT_CLASS (g_udev_client_parent_class)->constructed (object); +} + + +static void +g_udev_client_class_init (GUdevClientClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + + gobject_class->constructed = g_udev_client_constructed; + gobject_class->set_property = g_udev_client_set_property; + gobject_class->get_property = g_udev_client_get_property; + gobject_class->finalize = g_udev_client_finalize; + + /** + * GUdevClient:subsystems: + * + * The subsystems to listen for uevents on. + * + * To listen for only a specific DEVTYPE for a given SUBSYSTEM, use + * "subsystem/devtype". For example, to only listen for uevents + * where SUBSYSTEM is usb and DEVTYPE is usb_interface, use + * "usb/usb_interface". + * + * If this property is %NULL, then no events will be reported. If + * it's the empty array, events from all subsystems will be + * reported. + */ + g_object_class_install_property (gobject_class, + PROP_SUBSYSTEMS, + g_param_spec_boxed ("subsystems", + "The subsystems to listen for changes on", + "The subsystems to listen for changes on", + G_TYPE_STRV, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE)); + + /** + * GUdevClient::uevent: + * @client: The #GUdevClient receiving the event. + * @action: The action for the uevent e.g. "add", "remove", "change", "move", etc. + * @device: Details about the #GUdevDevice the event is for. + * + * Emitted when @client receives an uevent. + * + * This signal is emitted in the + * thread-default main loop + * of the thread that @client was created in. + */ + signals[UEVENT_SIGNAL] = g_signal_new ("uevent", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GUdevClientClass, uevent), + NULL, + NULL, + g_udev_marshal_VOID__STRING_OBJECT, + G_TYPE_NONE, + 2, + G_TYPE_STRING, + G_UDEV_TYPE_DEVICE); + + g_type_class_add_private (klass, sizeof (GUdevClientPrivate)); +} + +static void +g_udev_client_init (GUdevClient *client) +{ + client->priv = G_TYPE_INSTANCE_GET_PRIVATE (client, + G_UDEV_TYPE_CLIENT, + GUdevClientPrivate); +} + +/** + * g_udev_client_new: + * @subsystems: (array zero-terminated=1) (element-type utf8) (transfer none) (allow-none): A %NULL terminated string array of subsystems to listen for uevents on, %NULL to not listen on uevents at all, or an empty array to listen to uevents on all subsystems. See the documentation for the #GUdevClient:subsystems property for details on this parameter. + * + * Constructs a #GUdevClient object that can be used to query + * information about devices. Connect to the #GUdevClient::uevent + * signal to listen for uevents. Note that signals are emitted in the + * thread-default main loop + * of the thread that you call this constructor from. + * + * Returns: A new #GUdevClient object. Free with g_object_unref(). + */ +GUdevClient * +g_udev_client_new (const gchar * const *subsystems) +{ + return G_UDEV_CLIENT (g_object_new (G_UDEV_TYPE_CLIENT, "subsystems", subsystems, NULL)); +} + +/** + * g_udev_client_query_by_subsystem: + * @client: A #GUdevClient. + * @subsystem: (allow-none): The subsystem to get devices for or %NULL to get all devices. + * + * Gets all devices belonging to @subsystem. + * + * Returns: (element-type GUdevDevice) (transfer full): A list of #GUdevDevice objects. The caller should free the result by using g_object_unref() on each element in the list and then g_list_free() on the list. + */ +GList * +g_udev_client_query_by_subsystem (GUdevClient *client, + const gchar *subsystem) +{ + struct udev_enumerate *enumerate; + struct udev_list_entry *l, *devices; + GList *ret; + + g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL); + + ret = NULL; + + /* prepare a device scan */ + enumerate = udev_enumerate_new (client->priv->udev); + + /* filter for subsystem */ + if (subsystem != NULL) + udev_enumerate_add_match_subsystem (enumerate, subsystem); + /* retrieve the list */ + udev_enumerate_scan_devices (enumerate); + + /* add devices to the list */ + devices = udev_enumerate_get_list_entry (enumerate); + for (l = devices; l != NULL; l = udev_list_entry_get_next (l)) + { + struct udev_device *udevice; + GUdevDevice *device; + + udevice = udev_device_new_from_syspath (udev_enumerate_get_udev (enumerate), + udev_list_entry_get_name (l)); + if (udevice == NULL) + continue; + device = _g_udev_device_new (udevice); + udev_device_unref (udevice); + ret = g_list_prepend (ret, device); + } + udev_enumerate_unref (enumerate); + + ret = g_list_reverse (ret); + + return ret; +} + +/** + * g_udev_client_query_by_device_number: + * @client: A #GUdevClient. + * @type: A value from the #GUdevDeviceType enumeration. + * @number: A device number. + * + * Looks up a device for a type and device number. + * + * Returns: (transfer full): A #GUdevDevice object or %NULL if the device was not found. Free with g_object_unref(). + */ +GUdevDevice * +g_udev_client_query_by_device_number (GUdevClient *client, + GUdevDeviceType type, + GUdevDeviceNumber number) +{ + struct udev_device *udevice; + GUdevDevice *device; + + g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL); + + device = NULL; + udevice = udev_device_new_from_devnum (client->priv->udev, type, number); + + if (udevice == NULL) + goto out; + + device = _g_udev_device_new (udevice); + udev_device_unref (udevice); + + out: + return device; +} + +/** + * g_udev_client_query_by_device_file: + * @client: A #GUdevClient. + * @device_file: A device file. + * + * Looks up a device for a device file. + * + * Returns: (transfer full): A #GUdevDevice object or %NULL if the device was not found. Free with g_object_unref(). + */ +GUdevDevice * +g_udev_client_query_by_device_file (GUdevClient *client, + const gchar *device_file) +{ + struct stat stat_buf; + GUdevDevice *device; + + g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL); + g_return_val_if_fail (device_file != NULL, NULL); + + device = NULL; + + if (stat (device_file, &stat_buf) != 0) + goto out; + + if (stat_buf.st_rdev == 0) + goto out; + + if (S_ISBLK (stat_buf.st_mode)) + device = g_udev_client_query_by_device_number (client, G_UDEV_DEVICE_TYPE_BLOCK, stat_buf.st_rdev); + else if (S_ISCHR (stat_buf.st_mode)) + device = g_udev_client_query_by_device_number (client, G_UDEV_DEVICE_TYPE_CHAR, stat_buf.st_rdev); + + out: + return device; +} + +/** + * g_udev_client_query_by_sysfs_path: + * @client: A #GUdevClient. + * @sysfs_path: A sysfs path. + * + * Looks up a device for a sysfs path. + * + * Returns: (transfer full): A #GUdevDevice object or %NULL if the device was not found. Free with g_object_unref(). + */ +GUdevDevice * +g_udev_client_query_by_sysfs_path (GUdevClient *client, + const gchar *sysfs_path) +{ + struct udev_device *udevice; + GUdevDevice *device; + + g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL); + g_return_val_if_fail (sysfs_path != NULL, NULL); + + device = NULL; + udevice = udev_device_new_from_syspath (client->priv->udev, sysfs_path); + if (udevice == NULL) + goto out; + + device = _g_udev_device_new (udevice); + udev_device_unref (udevice); + + out: + return device; +} + +/** + * g_udev_client_query_by_subsystem_and_name: + * @client: A #GUdevClient. + * @subsystem: A subsystem name. + * @name: The name of the device. + * + * Looks up a device for a subsystem and name. + * + * Returns: (transfer full): A #GUdevDevice object or %NULL if the device was not found. Free with g_object_unref(). + */ +GUdevDevice * +g_udev_client_query_by_subsystem_and_name (GUdevClient *client, + const gchar *subsystem, + const gchar *name) +{ + struct udev_device *udevice; + GUdevDevice *device; + + g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL); + g_return_val_if_fail (subsystem != NULL, NULL); + g_return_val_if_fail (name != NULL, NULL); + + device = NULL; + udevice = udev_device_new_from_subsystem_sysname (client->priv->udev, subsystem, name); + if (udevice == NULL) + goto out; + + device = _g_udev_device_new (udevice); + udev_device_unref (udevice); + + out: + return device; +} + +struct udev * +_g_udev_client_get_udev (GUdevClient *client) +{ + g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL); + return client->priv->udev; +} diff --git a/src/udev/gudev/gudevclient.h b/src/udev/gudev/gudevclient.h new file mode 100644 index 0000000000..b425d03d48 --- /dev/null +++ b/src/udev/gudev/gudevclient.h @@ -0,0 +1,100 @@ +/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2008 David Zeuthen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#if !defined (_GUDEV_COMPILATION) && !defined(_GUDEV_INSIDE_GUDEV_H) +#error "Only can be included directly, this file may disappear or change contents." +#endif + +#ifndef __G_UDEV_CLIENT_H__ +#define __G_UDEV_CLIENT_H__ + +#include + +G_BEGIN_DECLS + +#define G_UDEV_TYPE_CLIENT (g_udev_client_get_type ()) +#define G_UDEV_CLIENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_UDEV_TYPE_CLIENT, GUdevClient)) +#define G_UDEV_CLIENT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_UDEV_TYPE_CLIENT, GUdevClientClass)) +#define G_UDEV_IS_CLIENT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_UDEV_TYPE_CLIENT)) +#define G_UDEV_IS_CLIENT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_UDEV_TYPE_CLIENT)) +#define G_UDEV_CLIENT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_UDEV_TYPE_CLIENT, GUdevClientClass)) + +typedef struct _GUdevClientClass GUdevClientClass; +typedef struct _GUdevClientPrivate GUdevClientPrivate; + +/** + * GUdevClient: + * + * The #GUdevClient struct is opaque and should not be accessed directly. + */ +struct _GUdevClient +{ + GObject parent; + + /*< private >*/ + GUdevClientPrivate *priv; +}; + +/** + * GUdevClientClass: + * @parent_class: Parent class. + * @uevent: Signal class handler for the #GUdevClient::uevent signal. + * + * Class structure for #GUdevClient. + */ +struct _GUdevClientClass +{ + GObjectClass parent_class; + + /* signals */ + void (*uevent) (GUdevClient *client, + const gchar *action, + GUdevDevice *device); + + /*< private >*/ + /* Padding for future expansion */ + void (*reserved1) (void); + void (*reserved2) (void); + void (*reserved3) (void); + void (*reserved4) (void); + void (*reserved5) (void); + void (*reserved6) (void); + void (*reserved7) (void); + void (*reserved8) (void); +}; + +GType g_udev_client_get_type (void) G_GNUC_CONST; +GUdevClient *g_udev_client_new (const gchar* const *subsystems); +GList *g_udev_client_query_by_subsystem (GUdevClient *client, + const gchar *subsystem); +GUdevDevice *g_udev_client_query_by_device_number (GUdevClient *client, + GUdevDeviceType type, + GUdevDeviceNumber number); +GUdevDevice *g_udev_client_query_by_device_file (GUdevClient *client, + const gchar *device_file); +GUdevDevice *g_udev_client_query_by_sysfs_path (GUdevClient *client, + const gchar *sysfs_path); +GUdevDevice *g_udev_client_query_by_subsystem_and_name (GUdevClient *client, + const gchar *subsystem, + const gchar *name); + +G_END_DECLS + +#endif /* __G_UDEV_CLIENT_H__ */ diff --git a/src/udev/gudev/gudevdevice.c b/src/udev/gudev/gudevdevice.c new file mode 100644 index 0000000000..62a26f99b7 --- /dev/null +++ b/src/udev/gudev/gudevdevice.c @@ -0,0 +1,963 @@ +/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2008 David Zeuthen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "gudevdevice.h" +#include "gudevprivate.h" + +/** + * SECTION:gudevdevice + * @short_description: Get information about a device + * + * The #GUdevDevice class is used to get information about a specific + * device. Note that you cannot instantiate a #GUdevDevice object + * yourself. Instead you must use #GUdevClient to obtain #GUdevDevice + * objects. + * + * To get basic information about a device, use + * g_udev_device_get_subsystem(), g_udev_device_get_devtype(), + * g_udev_device_get_name(), g_udev_device_get_number(), + * g_udev_device_get_sysfs_path(), g_udev_device_get_driver(), + * g_udev_device_get_action(), g_udev_device_get_seqnum(), + * g_udev_device_get_device_type(), g_udev_device_get_device_number(), + * g_udev_device_get_device_file(), + * g_udev_device_get_device_file_symlinks(). + * + * To navigate the device tree, use g_udev_device_get_parent() and + * g_udev_device_get_parent_with_subsystem(). + * + * To access udev properties for the device, use + * g_udev_device_get_property_keys(), + * g_udev_device_has_property(), + * g_udev_device_get_property(), + * g_udev_device_get_property_as_int(), + * g_udev_device_get_property_as_uint64(), + * g_udev_device_get_property_as_double(), + * g_udev_device_get_property_as_boolean() and + * g_udev_device_get_property_as_strv(). + * + * To access sysfs attributes for the device, use + * g_udev_device_get_sysfs_attr(), + * g_udev_device_get_sysfs_attr_as_int(), + * g_udev_device_get_sysfs_attr_as_uint64(), + * g_udev_device_get_sysfs_attr_as_double(), + * g_udev_device_get_sysfs_attr_as_boolean() and + * g_udev_device_get_sysfs_attr_as_strv(). + * + * Note that all getters on #GUdevDevice are non-reffing – returned + * values are owned by the object, should not be freed and are only + * valid as long as the object is alive. + * + * By design, #GUdevDevice will not react to changes for a device – it + * only contains a snapshot of information when the #GUdevDevice + * object was created. To work with changes, you typically connect to + * the #GUdevClient::uevent signal on a #GUdevClient and get a new + * #GUdevDevice whenever an event happens. + */ + +struct _GUdevDevicePrivate +{ + struct udev_device *udevice; + + /* computed ondemand and cached */ + gchar **device_file_symlinks; + gchar **property_keys; + gchar **tags; + GHashTable *prop_strvs; + GHashTable *sysfs_attr_strvs; +}; + +G_DEFINE_TYPE (GUdevDevice, g_udev_device, G_TYPE_OBJECT) + +static void +g_udev_device_finalize (GObject *object) +{ + GUdevDevice *device = G_UDEV_DEVICE (object); + + g_strfreev (device->priv->device_file_symlinks); + g_strfreev (device->priv->property_keys); + g_strfreev (device->priv->tags); + + if (device->priv->udevice != NULL) + udev_device_unref (device->priv->udevice); + + if (device->priv->prop_strvs != NULL) + g_hash_table_unref (device->priv->prop_strvs); + + if (device->priv->sysfs_attr_strvs != NULL) + g_hash_table_unref (device->priv->sysfs_attr_strvs); + + if (G_OBJECT_CLASS (g_udev_device_parent_class)->finalize != NULL) + (* G_OBJECT_CLASS (g_udev_device_parent_class)->finalize) (object); +} + +static void +g_udev_device_class_init (GUdevDeviceClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + + gobject_class->finalize = g_udev_device_finalize; + + g_type_class_add_private (klass, sizeof (GUdevDevicePrivate)); +} + +static void +g_udev_device_init (GUdevDevice *device) +{ + device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device, + G_UDEV_TYPE_DEVICE, + GUdevDevicePrivate); +} + + +GUdevDevice * +_g_udev_device_new (struct udev_device *udevice) +{ + GUdevDevice *device; + + device = G_UDEV_DEVICE (g_object_new (G_UDEV_TYPE_DEVICE, NULL)); + device->priv->udevice = udev_device_ref (udevice); + + return device; +} + +/** + * g_udev_device_get_subsystem: + * @device: A #GUdevDevice. + * + * Gets the subsystem for @device. + * + * Returns: The subsystem for @device. + */ +const gchar * +g_udev_device_get_subsystem (GUdevDevice *device) +{ + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); + return udev_device_get_subsystem (device->priv->udevice); +} + +/** + * g_udev_device_get_devtype: + * @device: A #GUdevDevice. + * + * Gets the device type for @device. + * + * Returns: The devtype for @device. + */ +const gchar * +g_udev_device_get_devtype (GUdevDevice *device) +{ + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); + return udev_device_get_devtype (device->priv->udevice); +} + +/** + * g_udev_device_get_name: + * @device: A #GUdevDevice. + * + * Gets the name of @device, e.g. "sda3". + * + * Returns: The name of @device. + */ +const gchar * +g_udev_device_get_name (GUdevDevice *device) +{ + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); + return udev_device_get_sysname (device->priv->udevice); +} + +/** + * g_udev_device_get_number: + * @device: A #GUdevDevice. + * + * Gets the number of @device, e.g. "3" if g_udev_device_get_name() returns "sda3". + * + * Returns: The number of @device. + */ +const gchar * +g_udev_device_get_number (GUdevDevice *device) +{ + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); + return udev_device_get_sysnum (device->priv->udevice); +} + +/** + * g_udev_device_get_sysfs_path: + * @device: A #GUdevDevice. + * + * Gets the sysfs path for @device. + * + * Returns: The sysfs path for @device. + */ +const gchar * +g_udev_device_get_sysfs_path (GUdevDevice *device) +{ + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); + return udev_device_get_syspath (device->priv->udevice); +} + +/** + * g_udev_device_get_driver: + * @device: A #GUdevDevice. + * + * Gets the name of the driver used for @device. + * + * Returns: The name of the driver for @device or %NULL if unknown. + */ +const gchar * +g_udev_device_get_driver (GUdevDevice *device) +{ + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); + return udev_device_get_driver (device->priv->udevice); +} + +/** + * g_udev_device_get_action: + * @device: A #GUdevDevice. + * + * Gets the most recent action (e.g. "add", "remove", "change", etc.) for @device. + * + * Returns: An action string. + */ +const gchar * +g_udev_device_get_action (GUdevDevice *device) +{ + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); + return udev_device_get_action (device->priv->udevice); +} + +/** + * g_udev_device_get_seqnum: + * @device: A #GUdevDevice. + * + * Gets the most recent sequence number for @device. + * + * Returns: A sequence number. + */ +guint64 +g_udev_device_get_seqnum (GUdevDevice *device) +{ + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0); + return udev_device_get_seqnum (device->priv->udevice); +} + +/** + * g_udev_device_get_device_type: + * @device: A #GUdevDevice. + * + * Gets the type of the device file, if any, for @device. + * + * Returns: The device number for @device or #G_UDEV_DEVICE_TYPE_NONE if the device does not have a device file. + */ +GUdevDeviceType +g_udev_device_get_device_type (GUdevDevice *device) +{ + struct stat stat_buf; + const gchar *device_file; + GUdevDeviceType type; + + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), G_UDEV_DEVICE_TYPE_NONE); + + type = G_UDEV_DEVICE_TYPE_NONE; + + /* TODO: would be better to have support for this in libudev... */ + + device_file = g_udev_device_get_device_file (device); + if (device_file == NULL) + goto out; + + if (stat (device_file, &stat_buf) != 0) + goto out; + + if (S_ISBLK (stat_buf.st_mode)) + type = G_UDEV_DEVICE_TYPE_BLOCK; + else if (S_ISCHR (stat_buf.st_mode)) + type = G_UDEV_DEVICE_TYPE_CHAR; + + out: + return type; +} + +/** + * g_udev_device_get_device_number: + * @device: A #GUdevDevice. + * + * Gets the device number, if any, for @device. + * + * Returns: The device number for @device or 0 if unknown. + */ +GUdevDeviceNumber +g_udev_device_get_device_number (GUdevDevice *device) +{ + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0); + return udev_device_get_devnum (device->priv->udevice); +} + +/** + * g_udev_device_get_device_file: + * @device: A #GUdevDevice. + * + * Gets the device file for @device. + * + * Returns: The device file for @device or %NULL if no device file + * exists. + */ +const gchar * +g_udev_device_get_device_file (GUdevDevice *device) +{ + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); + return udev_device_get_devnode (device->priv->udevice); +} + +/** + * g_udev_device_get_device_file_symlinks: + * @device: A #GUdevDevice. + * + * Gets a list of symlinks (in /dev) that points to + * the device file for @device. + * + * Returns: (transfer none) (array zero-terminated=1) (element-type utf8): A %NULL terminated string array of symlinks. This array is owned by @device and should not be freed by the caller. + */ +const gchar * const * +g_udev_device_get_device_file_symlinks (GUdevDevice *device) +{ + struct udev_list_entry *l; + GPtrArray *p; + + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); + + if (device->priv->device_file_symlinks != NULL) + goto out; + + p = g_ptr_array_new (); + for (l = udev_device_get_devlinks_list_entry (device->priv->udevice); l != NULL; l = udev_list_entry_get_next (l)) + { + g_ptr_array_add (p, g_strdup (udev_list_entry_get_name (l))); + } + g_ptr_array_add (p, NULL); + device->priv->device_file_symlinks = (gchar **) g_ptr_array_free (p, FALSE); + + out: + return (const gchar * const *) device->priv->device_file_symlinks; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_udev_device_get_parent: + * @device: A #GUdevDevice. + * + * Gets the immediate parent of @device, if any. + * + * Returns: (transfer full): A #GUdevDevice or %NULL if @device has no parent. Free with g_object_unref(). + */ +GUdevDevice * +g_udev_device_get_parent (GUdevDevice *device) +{ + GUdevDevice *ret; + struct udev_device *udevice; + + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); + + ret = NULL; + + udevice = udev_device_get_parent (device->priv->udevice); + if (udevice == NULL) + goto out; + + ret = _g_udev_device_new (udevice); + + out: + return ret; +} + +/** + * g_udev_device_get_parent_with_subsystem: + * @device: A #GUdevDevice. + * @subsystem: The subsystem of the parent to get. + * @devtype: (allow-none): The devtype of the parent to get or %NULL. + * + * Walks up the chain of parents of @device and returns the first + * device encountered where @subsystem and @devtype matches, if any. + * + * Returns: (transfer full): A #GUdevDevice or %NULL if @device has no parent with @subsystem and @devtype. Free with g_object_unref(). + */ +GUdevDevice * +g_udev_device_get_parent_with_subsystem (GUdevDevice *device, + const gchar *subsystem, + const gchar *devtype) +{ + GUdevDevice *ret; + struct udev_device *udevice; + + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); + g_return_val_if_fail (subsystem != NULL, NULL); + + ret = NULL; + + udevice = udev_device_get_parent_with_subsystem_devtype (device->priv->udevice, + subsystem, + devtype); + if (udevice == NULL) + goto out; + + ret = _g_udev_device_new (udevice); + + out: + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_udev_device_get_property_keys: + * @device: A #GUdevDevice. + * + * Gets all keys for properties on @device. + * + * Returns: (transfer none) (array zero-terminated=1) (element-type utf8): A %NULL terminated string array of property keys. This array is owned by @device and should not be freed by the caller. + */ +const gchar* const * +g_udev_device_get_property_keys (GUdevDevice *device) +{ + struct udev_list_entry *l; + GPtrArray *p; + + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); + + if (device->priv->property_keys != NULL) + goto out; + + p = g_ptr_array_new (); + for (l = udev_device_get_properties_list_entry (device->priv->udevice); l != NULL; l = udev_list_entry_get_next (l)) + { + g_ptr_array_add (p, g_strdup (udev_list_entry_get_name (l))); + } + g_ptr_array_add (p, NULL); + device->priv->property_keys = (gchar **) g_ptr_array_free (p, FALSE); + + out: + return (const gchar * const *) device->priv->property_keys; +} + + +/** + * g_udev_device_has_property: + * @device: A #GUdevDevice. + * @key: Name of property. + * + * Check if a the property with the given key exists. + * + * Returns: %TRUE only if the value for @key exist. + */ +gboolean +g_udev_device_has_property (GUdevDevice *device, + const gchar *key) +{ + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), FALSE); + g_return_val_if_fail (key != NULL, FALSE); + return udev_device_get_property_value (device->priv->udevice, key) != NULL; +} + +/** + * g_udev_device_get_property: + * @device: A #GUdevDevice. + * @key: Name of property. + * + * Look up the value for @key on @device. + * + * Returns: The value for @key or %NULL if @key doesn't exist on @device. Do not free this string, it is owned by @device. + */ +const gchar * +g_udev_device_get_property (GUdevDevice *device, + const gchar *key) +{ + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); + g_return_val_if_fail (key != NULL, NULL); + return udev_device_get_property_value (device->priv->udevice, key); +} + +/** + * g_udev_device_get_property_as_int: + * @device: A #GUdevDevice. + * @key: Name of property. + * + * Look up the value for @key on @device and convert it to an integer + * using strtol(). + * + * Returns: The value for @key or 0 if @key doesn't exist or + * isn't an integer. + */ +gint +g_udev_device_get_property_as_int (GUdevDevice *device, + const gchar *key) +{ + gint result; + const gchar *s; + + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0); + g_return_val_if_fail (key != NULL, 0); + + result = 0; + s = g_udev_device_get_property (device, key); + if (s == NULL) + goto out; + + result = strtol (s, NULL, 0); +out: + return result; +} + +/** + * g_udev_device_get_property_as_uint64: + * @device: A #GUdevDevice. + * @key: Name of property. + * + * Look up the value for @key on @device and convert it to an unsigned + * 64-bit integer using g_ascii_strtoull(). + * + * Returns: The value for @key or 0 if @key doesn't exist or isn't a + * #guint64. + */ +guint64 +g_udev_device_get_property_as_uint64 (GUdevDevice *device, + const gchar *key) +{ + guint64 result; + const gchar *s; + + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0); + g_return_val_if_fail (key != NULL, 0); + + result = 0; + s = g_udev_device_get_property (device, key); + if (s == NULL) + goto out; + + result = g_ascii_strtoull (s, NULL, 0); +out: + return result; +} + +/** + * g_udev_device_get_property_as_double: + * @device: A #GUdevDevice. + * @key: Name of property. + * + * Look up the value for @key on @device and convert it to a double + * precision floating point number using strtod(). + * + * Returns: The value for @key or 0.0 if @key doesn't exist or isn't a + * #gdouble. + */ +gdouble +g_udev_device_get_property_as_double (GUdevDevice *device, + const gchar *key) +{ + gdouble result; + const gchar *s; + + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0.0); + g_return_val_if_fail (key != NULL, 0.0); + + result = 0.0; + s = g_udev_device_get_property (device, key); + if (s == NULL) + goto out; + + result = strtod (s, NULL); +out: + return result; +} + +/** + * g_udev_device_get_property_as_boolean: + * @device: A #GUdevDevice. + * @key: Name of property. + * + * Look up the value for @key on @device and convert it to an + * boolean. This is done by doing a case-insensitive string comparison + * on the string value against "1" and "true". + * + * Returns: The value for @key or %FALSE if @key doesn't exist or + * isn't a #gboolean. + */ +gboolean +g_udev_device_get_property_as_boolean (GUdevDevice *device, + const gchar *key) +{ + gboolean result; + const gchar *s; + + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), FALSE); + g_return_val_if_fail (key != NULL, FALSE); + + result = FALSE; + s = g_udev_device_get_property (device, key); + if (s == NULL) + goto out; + + if (strcmp (s, "1") == 0 || g_ascii_strcasecmp (s, "true") == 0) + result = TRUE; + out: + return result; +} + +static gchar ** +split_at_whitespace (const gchar *s) +{ + gchar **result; + guint n; + guint m; + + result = g_strsplit_set (s, " \v\t\r\n", 0); + + /* remove empty strings, thanks GLib */ + for (n = 0; result[n] != NULL; n++) + { + if (strlen (result[n]) == 0) + { + g_free (result[n]); + for (m = n; result[m] != NULL; m++) + result[m] = result[m + 1]; + n--; + } + } + + return result; +} + +/** + * g_udev_device_get_property_as_strv: + * @device: A #GUdevDevice. + * @key: Name of property. + * + * Look up the value for @key on @device and return the result of + * splitting it into non-empty tokens split at white space (only space + * (' '), form-feed ('\f'), newline ('\n'), carriage return ('\r'), + * horizontal tab ('\t'), and vertical tab ('\v') are considered; the + * locale is not taken into account). + * + * Returns: (transfer none) (array zero-terminated=1) (element-type utf8): The value of @key on @device split into tokens or %NULL if @key doesn't exist. This array is owned by @device and should not be freed by the caller. + */ +const gchar* const * +g_udev_device_get_property_as_strv (GUdevDevice *device, + const gchar *key) +{ + gchar **result; + const gchar *s; + + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); + g_return_val_if_fail (key != NULL, NULL); + + if (device->priv->prop_strvs != NULL) + { + result = g_hash_table_lookup (device->priv->prop_strvs, key); + if (result != NULL) + goto out; + } + + result = NULL; + s = g_udev_device_get_property (device, key); + if (s == NULL) + goto out; + + result = split_at_whitespace (s); + if (result == NULL) + goto out; + + if (device->priv->prop_strvs == NULL) + device->priv->prop_strvs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_strfreev); + g_hash_table_insert (device->priv->prop_strvs, g_strdup (key), result); + +out: + return (const gchar* const *) result; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_udev_device_get_sysfs_attr: + * @device: A #GUdevDevice. + * @name: Name of the sysfs attribute. + * + * Look up the sysfs attribute with @name on @device. + * + * Returns: The value of the sysfs attribute or %NULL if there is no + * such attribute. Do not free this string, it is owned by @device. + */ +const gchar * +g_udev_device_get_sysfs_attr (GUdevDevice *device, + const gchar *name) +{ + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); + g_return_val_if_fail (name != NULL, NULL); + return udev_device_get_sysattr_value (device->priv->udevice, name); +} + +/** + * g_udev_device_get_sysfs_attr_as_int: + * @device: A #GUdevDevice. + * @name: Name of the sysfs attribute. + * + * Look up the sysfs attribute with @name on @device and convert it to an integer + * using strtol(). + * + * Returns: The value of the sysfs attribute or 0 if there is no such + * attribute. + */ +gint +g_udev_device_get_sysfs_attr_as_int (GUdevDevice *device, + const gchar *name) +{ + gint result; + const gchar *s; + + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0); + g_return_val_if_fail (name != NULL, 0); + + result = 0; + s = g_udev_device_get_sysfs_attr (device, name); + if (s == NULL) + goto out; + + result = strtol (s, NULL, 0); +out: + return result; +} + +/** + * g_udev_device_get_sysfs_attr_as_uint64: + * @device: A #GUdevDevice. + * @name: Name of the sysfs attribute. + * + * Look up the sysfs attribute with @name on @device and convert it to an unsigned + * 64-bit integer using g_ascii_strtoull(). + * + * Returns: The value of the sysfs attribute or 0 if there is no such + * attribute. + */ +guint64 +g_udev_device_get_sysfs_attr_as_uint64 (GUdevDevice *device, + const gchar *name) +{ + guint64 result; + const gchar *s; + + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0); + g_return_val_if_fail (name != NULL, 0); + + result = 0; + s = g_udev_device_get_sysfs_attr (device, name); + if (s == NULL) + goto out; + + result = g_ascii_strtoull (s, NULL, 0); +out: + return result; +} + +/** + * g_udev_device_get_sysfs_attr_as_double: + * @device: A #GUdevDevice. + * @name: Name of the sysfs attribute. + * + * Look up the sysfs attribute with @name on @device and convert it to a double + * precision floating point number using strtod(). + * + * Returns: The value of the sysfs attribute or 0.0 if there is no such + * attribute. + */ +gdouble +g_udev_device_get_sysfs_attr_as_double (GUdevDevice *device, + const gchar *name) +{ + gdouble result; + const gchar *s; + + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0.0); + g_return_val_if_fail (name != NULL, 0.0); + + result = 0.0; + s = g_udev_device_get_sysfs_attr (device, name); + if (s == NULL) + goto out; + + result = strtod (s, NULL); +out: + return result; +} + +/** + * g_udev_device_get_sysfs_attr_as_boolean: + * @device: A #GUdevDevice. + * @name: Name of the sysfs attribute. + * + * Look up the sysfs attribute with @name on @device and convert it to an + * boolean. This is done by doing a case-insensitive string comparison + * on the string value against "1" and "true". + * + * Returns: The value of the sysfs attribute or %FALSE if there is no such + * attribute. + */ +gboolean +g_udev_device_get_sysfs_attr_as_boolean (GUdevDevice *device, + const gchar *name) +{ + gboolean result; + const gchar *s; + + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), FALSE); + g_return_val_if_fail (name != NULL, FALSE); + + result = FALSE; + s = g_udev_device_get_sysfs_attr (device, name); + if (s == NULL) + goto out; + + if (strcmp (s, "1") == 0 || g_ascii_strcasecmp (s, "true") == 0) + result = TRUE; + out: + return result; +} + +/** + * g_udev_device_get_sysfs_attr_as_strv: + * @device: A #GUdevDevice. + * @name: Name of the sysfs attribute. + * + * Look up the sysfs attribute with @name on @device and return the result of + * splitting it into non-empty tokens split at white space (only space (' '), + * form-feed ('\f'), newline ('\n'), carriage return ('\r'), horizontal + * tab ('\t'), and vertical tab ('\v') are considered; the locale is + * not taken into account). + * + * Returns: (transfer none) (array zero-terminated=1) (element-type utf8): The value of the sysfs attribute split into tokens or %NULL if there is no such attribute. This array is owned by @device and should not be freed by the caller. + */ +const gchar * const * +g_udev_device_get_sysfs_attr_as_strv (GUdevDevice *device, + const gchar *name) +{ + gchar **result; + const gchar *s; + + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); + g_return_val_if_fail (name != NULL, NULL); + + if (device->priv->sysfs_attr_strvs != NULL) + { + result = g_hash_table_lookup (device->priv->sysfs_attr_strvs, name); + if (result != NULL) + goto out; + } + + result = NULL; + s = g_udev_device_get_sysfs_attr (device, name); + if (s == NULL) + goto out; + + result = split_at_whitespace (s); + if (result == NULL) + goto out; + + if (device->priv->sysfs_attr_strvs == NULL) + device->priv->sysfs_attr_strvs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_strfreev); + g_hash_table_insert (device->priv->sysfs_attr_strvs, g_strdup (name), result); + +out: + return (const gchar* const *) result; +} + +/** + * g_udev_device_get_tags: + * @device: A #GUdevDevice. + * + * Gets all tags for @device. + * + * Returns: (transfer none) (array zero-terminated=1) (element-type utf8): A %NULL terminated string array of tags. This array is owned by @device and should not be freed by the caller. + * + * Since: 165 + */ +const gchar* const * +g_udev_device_get_tags (GUdevDevice *device) +{ + struct udev_list_entry *l; + GPtrArray *p; + + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); + + if (device->priv->tags != NULL) + goto out; + + p = g_ptr_array_new (); + for (l = udev_device_get_tags_list_entry (device->priv->udevice); l != NULL; l = udev_list_entry_get_next (l)) + { + g_ptr_array_add (p, g_strdup (udev_list_entry_get_name (l))); + } + g_ptr_array_add (p, NULL); + device->priv->tags = (gchar **) g_ptr_array_free (p, FALSE); + + out: + return (const gchar * const *) device->priv->tags; +} + +/** + * g_udev_device_get_is_initialized: + * @device: A #GUdevDevice. + * + * Gets whether @device has been initalized. + * + * Returns: Whether @device has been initialized. + * + * Since: 165 + */ +gboolean +g_udev_device_get_is_initialized (GUdevDevice *device) +{ + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), FALSE); + return udev_device_get_is_initialized (device->priv->udevice); +} + +/** + * g_udev_device_get_usec_since_initialized: + * @device: A #GUdevDevice. + * + * Gets number of micro-seconds since @device was initialized. + * + * This only works for devices with properties in the udev + * database. All other devices return 0. + * + * Returns: Number of micro-seconds since @device was initialized or 0 if unknown. + * + * Since: 165 + */ +guint64 +g_udev_device_get_usec_since_initialized (GUdevDevice *device) +{ + g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0); + return udev_device_get_usec_since_initialized (device->priv->udevice); +} diff --git a/src/udev/gudev/gudevdevice.h b/src/udev/gudev/gudevdevice.h new file mode 100644 index 0000000000..d4873bad0f --- /dev/null +++ b/src/udev/gudev/gudevdevice.h @@ -0,0 +1,128 @@ +/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2008 David Zeuthen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#if !defined (_GUDEV_COMPILATION) && !defined(_GUDEV_INSIDE_GUDEV_H) +#error "Only can be included directly, this file may disappear or change contents." +#endif + +#ifndef __G_UDEV_DEVICE_H__ +#define __G_UDEV_DEVICE_H__ + +#include + +G_BEGIN_DECLS + +#define G_UDEV_TYPE_DEVICE (g_udev_device_get_type ()) +#define G_UDEV_DEVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_UDEV_TYPE_DEVICE, GUdevDevice)) +#define G_UDEV_DEVICE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_UDEV_TYPE_DEVICE, GUdevDeviceClass)) +#define G_UDEV_IS_DEVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_UDEV_TYPE_DEVICE)) +#define G_UDEV_IS_DEVICE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_UDEV_TYPE_DEVICE)) +#define G_UDEV_DEVICE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_UDEV_TYPE_DEVICE, GUdevDeviceClass)) + +typedef struct _GUdevDeviceClass GUdevDeviceClass; +typedef struct _GUdevDevicePrivate GUdevDevicePrivate; + +/** + * GUdevDevice: + * + * The #GUdevDevice struct is opaque and should not be accessed directly. + */ +struct _GUdevDevice +{ + GObject parent; + + /*< private >*/ + GUdevDevicePrivate *priv; +}; + +/** + * GUdevDeviceClass: + * @parent_class: Parent class. + * + * Class structure for #GUdevDevice. + */ +struct _GUdevDeviceClass +{ + GObjectClass parent_class; + + /*< private >*/ + /* Padding for future expansion */ + void (*reserved1) (void); + void (*reserved2) (void); + void (*reserved3) (void); + void (*reserved4) (void); + void (*reserved5) (void); + void (*reserved6) (void); + void (*reserved7) (void); + void (*reserved8) (void); +}; + +GType g_udev_device_get_type (void) G_GNUC_CONST; +gboolean g_udev_device_get_is_initialized (GUdevDevice *device); +guint64 g_udev_device_get_usec_since_initialized (GUdevDevice *device); +const gchar *g_udev_device_get_subsystem (GUdevDevice *device); +const gchar *g_udev_device_get_devtype (GUdevDevice *device); +const gchar *g_udev_device_get_name (GUdevDevice *device); +const gchar *g_udev_device_get_number (GUdevDevice *device); +const gchar *g_udev_device_get_sysfs_path (GUdevDevice *device); +const gchar *g_udev_device_get_driver (GUdevDevice *device); +const gchar *g_udev_device_get_action (GUdevDevice *device); +guint64 g_udev_device_get_seqnum (GUdevDevice *device); +GUdevDeviceType g_udev_device_get_device_type (GUdevDevice *device); +GUdevDeviceNumber g_udev_device_get_device_number (GUdevDevice *device); +const gchar *g_udev_device_get_device_file (GUdevDevice *device); +const gchar* const *g_udev_device_get_device_file_symlinks (GUdevDevice *device); +GUdevDevice *g_udev_device_get_parent (GUdevDevice *device); +GUdevDevice *g_udev_device_get_parent_with_subsystem (GUdevDevice *device, + const gchar *subsystem, + const gchar *devtype); +const gchar* const *g_udev_device_get_property_keys (GUdevDevice *device); +gboolean g_udev_device_has_property (GUdevDevice *device, + const gchar *key); +const gchar *g_udev_device_get_property (GUdevDevice *device, + const gchar *key); +gint g_udev_device_get_property_as_int (GUdevDevice *device, + const gchar *key); +guint64 g_udev_device_get_property_as_uint64 (GUdevDevice *device, + const gchar *key); +gdouble g_udev_device_get_property_as_double (GUdevDevice *device, + const gchar *key); +gboolean g_udev_device_get_property_as_boolean (GUdevDevice *device, + const gchar *key); +const gchar* const *g_udev_device_get_property_as_strv (GUdevDevice *device, + const gchar *key); + +const gchar *g_udev_device_get_sysfs_attr (GUdevDevice *device, + const gchar *name); +gint g_udev_device_get_sysfs_attr_as_int (GUdevDevice *device, + const gchar *name); +guint64 g_udev_device_get_sysfs_attr_as_uint64 (GUdevDevice *device, + const gchar *name); +gdouble g_udev_device_get_sysfs_attr_as_double (GUdevDevice *device, + const gchar *name); +gboolean g_udev_device_get_sysfs_attr_as_boolean (GUdevDevice *device, + const gchar *name); +const gchar* const *g_udev_device_get_sysfs_attr_as_strv (GUdevDevice *device, + const gchar *name); +const gchar* const *g_udev_device_get_tags (GUdevDevice *device); + +G_END_DECLS + +#endif /* __G_UDEV_DEVICE_H__ */ diff --git a/src/udev/gudev/gudevenumerator.c b/src/udev/gudev/gudevenumerator.c new file mode 100644 index 0000000000..db09074625 --- /dev/null +++ b/src/udev/gudev/gudevenumerator.c @@ -0,0 +1,431 @@ +/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2008-2010 David Zeuthen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "gudevclient.h" +#include "gudevenumerator.h" +#include "gudevdevice.h" +#include "gudevmarshal.h" +#include "gudevprivate.h" + +/** + * SECTION:gudevenumerator + * @short_description: Lookup and sort devices + * + * #GUdevEnumerator is used to lookup and sort devices. + * + * Since: 165 + */ + +struct _GUdevEnumeratorPrivate +{ + GUdevClient *client; + struct udev_enumerate *e; +}; + +enum +{ + PROP_0, + PROP_CLIENT, +}; + +G_DEFINE_TYPE (GUdevEnumerator, g_udev_enumerator, G_TYPE_OBJECT) + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +g_udev_enumerator_finalize (GObject *object) +{ + GUdevEnumerator *enumerator = G_UDEV_ENUMERATOR (object); + + if (enumerator->priv->client != NULL) + { + g_object_unref (enumerator->priv->client); + enumerator->priv->client = NULL; + } + + if (enumerator->priv->e != NULL) + { + udev_enumerate_unref (enumerator->priv->e); + enumerator->priv->e = NULL; + } + + if (G_OBJECT_CLASS (g_udev_enumerator_parent_class)->finalize != NULL) + G_OBJECT_CLASS (g_udev_enumerator_parent_class)->finalize (object); +} + +static void +g_udev_enumerator_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GUdevEnumerator *enumerator = G_UDEV_ENUMERATOR (object); + + switch (prop_id) + { + case PROP_CLIENT: + if (enumerator->priv->client != NULL) + g_object_unref (enumerator->priv->client); + enumerator->priv->client = g_value_dup_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_udev_enumerator_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GUdevEnumerator *enumerator = G_UDEV_ENUMERATOR (object); + + switch (prop_id) + { + case PROP_CLIENT: + g_value_set_object (value, enumerator->priv->client); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_udev_enumerator_constructed (GObject *object) +{ + GUdevEnumerator *enumerator = G_UDEV_ENUMERATOR (object); + + g_assert (G_UDEV_IS_CLIENT (enumerator->priv->client)); + + enumerator->priv->e = udev_enumerate_new (_g_udev_client_get_udev (enumerator->priv->client)); + + if (G_OBJECT_CLASS (g_udev_enumerator_parent_class)->constructed != NULL) + G_OBJECT_CLASS (g_udev_enumerator_parent_class)->constructed (object); +} + +static void +g_udev_enumerator_class_init (GUdevEnumeratorClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + + gobject_class->finalize = g_udev_enumerator_finalize; + gobject_class->set_property = g_udev_enumerator_set_property; + gobject_class->get_property = g_udev_enumerator_get_property; + gobject_class->constructed = g_udev_enumerator_constructed; + + /** + * GUdevEnumerator:client: + * + * The #GUdevClient to enumerate devices from. + * + * Since: 165 + */ + g_object_class_install_property (gobject_class, + PROP_CLIENT, + g_param_spec_object ("client", + "The client to enumerate devices from", + "The client to enumerate devices from", + G_UDEV_TYPE_CLIENT, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE)); + + g_type_class_add_private (klass, sizeof (GUdevEnumeratorPrivate)); +} + +static void +g_udev_enumerator_init (GUdevEnumerator *enumerator) +{ + enumerator->priv = G_TYPE_INSTANCE_GET_PRIVATE (enumerator, + G_UDEV_TYPE_ENUMERATOR, + GUdevEnumeratorPrivate); +} + +/** + * g_udev_enumerator_new: + * @client: A #GUdevClient to enumerate devices from. + * + * Constructs a #GUdevEnumerator object that can be used to enumerate + * and sort devices. Use the add_match_*() and add_nomatch_*() methods + * and execute the query to get a list of devices with + * g_udev_enumerator_execute(). + * + * Returns: A new #GUdevEnumerator object. Free with g_object_unref(). + * + * Since: 165 + */ +GUdevEnumerator * +g_udev_enumerator_new (GUdevClient *client) +{ + g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL); + return G_UDEV_ENUMERATOR (g_object_new (G_UDEV_TYPE_ENUMERATOR, "client", client, NULL)); +} + + +/** + * g_udev_enumerator_add_match_subsystem: + * @enumerator: A #GUdevEnumerator. + * @subsystem: Wildcard for subsystem name e.g. 'scsi' or 'a*'. + * + * All returned devices will match the given @subsystem. + * + * Returns: (transfer none): The passed in @enumerator. + * + * Since: 165 + */ +GUdevEnumerator * +g_udev_enumerator_add_match_subsystem (GUdevEnumerator *enumerator, + const gchar *subsystem) +{ + g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL); + g_return_val_if_fail (subsystem != NULL, NULL); + udev_enumerate_add_match_subsystem (enumerator->priv->e, subsystem); + return enumerator; +} + +/** + * g_udev_enumerator_add_nomatch_subsystem: + * @enumerator: A #GUdevEnumerator. + * @subsystem: Wildcard for subsystem name e.g. 'scsi' or 'a*'. + * + * All returned devices will not match the given @subsystem. + * + * Returns: (transfer none): The passed in @enumerator. + * + * Since: 165 + */ +GUdevEnumerator * +g_udev_enumerator_add_nomatch_subsystem (GUdevEnumerator *enumerator, + const gchar *subsystem) +{ + g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL); + g_return_val_if_fail (subsystem != NULL, NULL); + udev_enumerate_add_nomatch_subsystem (enumerator->priv->e, subsystem); + return enumerator; +} + +/** + * g_udev_enumerator_add_match_sysfs_attr: + * @enumerator: A #GUdevEnumerator. + * @name: Wildcard filter for sysfs attribute key. + * @value: Wildcard filter for sysfs attribute value. + * + * All returned devices will have a sysfs attribute matching the given @name and @value. + * + * Returns: (transfer none): The passed in @enumerator. + * + * Since: 165 + */ +GUdevEnumerator * +g_udev_enumerator_add_match_sysfs_attr (GUdevEnumerator *enumerator, + const gchar *name, + const gchar *value) +{ + g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL); + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (value != NULL, NULL); + udev_enumerate_add_match_sysattr (enumerator->priv->e, name, value); + return enumerator; +} + +/** + * g_udev_enumerator_add_nomatch_sysfs_attr: + * @enumerator: A #GUdevEnumerator. + * @name: Wildcard filter for sysfs attribute key. + * @value: Wildcard filter for sysfs attribute value. + * + * All returned devices will not have a sysfs attribute matching the given @name and @value. + * + * Returns: (transfer none): The passed in @enumerator. + * + * Since: 165 + */ +GUdevEnumerator * +g_udev_enumerator_add_nomatch_sysfs_attr (GUdevEnumerator *enumerator, + const gchar *name, + const gchar *value) +{ + g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL); + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (value != NULL, NULL); + udev_enumerate_add_nomatch_sysattr (enumerator->priv->e, name, value); + return enumerator; +} + +/** + * g_udev_enumerator_add_match_property: + * @enumerator: A #GUdevEnumerator. + * @name: Wildcard filter for property name. + * @value: Wildcard filter for property value. + * + * All returned devices will have a property matching the given @name and @value. + * + * Returns: (transfer none): The passed in @enumerator. + * + * Since: 165 + */ +GUdevEnumerator * +g_udev_enumerator_add_match_property (GUdevEnumerator *enumerator, + const gchar *name, + const gchar *value) +{ + g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL); + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (value != NULL, NULL); + udev_enumerate_add_match_property (enumerator->priv->e, name, value); + return enumerator; +} + +/** + * g_udev_enumerator_add_match_name: + * @enumerator: A #GUdevEnumerator. + * @name: Wildcard filter for kernel name e.g. "sda*". + * + * All returned devices will match the given @name. + * + * Returns: (transfer none): The passed in @enumerator. + * + * Since: 165 + */ +GUdevEnumerator * +g_udev_enumerator_add_match_name (GUdevEnumerator *enumerator, + const gchar *name) +{ + g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL); + g_return_val_if_fail (name != NULL, NULL); + udev_enumerate_add_match_sysname (enumerator->priv->e, name); + return enumerator; +} + +/** + * g_udev_enumerator_add_sysfs_path: + * @enumerator: A #GUdevEnumerator. + * @sysfs_path: A sysfs path, e.g. "/sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda" + * + * Add a device to the list of devices, to retrieve it back sorted in dependency order. + * + * Returns: (transfer none): The passed in @enumerator. + * + * Since: 165 + */ +GUdevEnumerator * +g_udev_enumerator_add_sysfs_path (GUdevEnumerator *enumerator, + const gchar *sysfs_path) +{ + g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL); + g_return_val_if_fail (sysfs_path != NULL, NULL); + udev_enumerate_add_syspath (enumerator->priv->e, sysfs_path); + return enumerator; +} + +/** + * g_udev_enumerator_add_match_tag: + * @enumerator: A #GUdevEnumerator. + * @tag: A udev tag e.g. "udev-acl". + * + * All returned devices will match the given @tag. + * + * Returns: (transfer none): The passed in @enumerator. + * + * Since: 165 + */ +GUdevEnumerator * +g_udev_enumerator_add_match_tag (GUdevEnumerator *enumerator, + const gchar *tag) +{ + g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL); + g_return_val_if_fail (tag != NULL, NULL); + udev_enumerate_add_match_tag (enumerator->priv->e, tag); + return enumerator; +} + +/** + * g_udev_enumerator_add_match_is_initialized: + * @enumerator: A #GUdevEnumerator. + * + * All returned devices will be initialized. + * + * Returns: (transfer none): The passed in @enumerator. + * + * Since: 165 + */ +GUdevEnumerator * +g_udev_enumerator_add_match_is_initialized (GUdevEnumerator *enumerator) +{ + g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL); + udev_enumerate_add_match_is_initialized (enumerator->priv->e); + return enumerator; +} + +/** + * g_udev_enumerator_execute: + * @enumerator: A #GUdevEnumerator. + * + * Executes the query in @enumerator. + * + * Returns: (element-type GUdevDevice) (transfer full): A list of #GUdevDevice objects. The caller should free the result by using g_object_unref() on each element in the list and then g_list_free() on the list. + * + * Since: 165 + */ +GList * +g_udev_enumerator_execute (GUdevEnumerator *enumerator) +{ + GList *ret; + struct udev_list_entry *l, *devices; + + g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL); + + ret = NULL; + + /* retrieve the list */ + udev_enumerate_scan_devices (enumerator->priv->e); + + devices = udev_enumerate_get_list_entry (enumerator->priv->e); + for (l = devices; l != NULL; l = udev_list_entry_get_next (l)) + { + struct udev_device *udevice; + GUdevDevice *device; + + udevice = udev_device_new_from_syspath (udev_enumerate_get_udev (enumerator->priv->e), + udev_list_entry_get_name (l)); + if (udevice == NULL) + continue; + + device = _g_udev_device_new (udevice); + udev_device_unref (udevice); + ret = g_list_prepend (ret, device); + } + + ret = g_list_reverse (ret); + + return ret; +} diff --git a/src/udev/gudev/gudevenumerator.h b/src/udev/gudev/gudevenumerator.h new file mode 100644 index 0000000000..3fddccf573 --- /dev/null +++ b/src/udev/gudev/gudevenumerator.h @@ -0,0 +1,107 @@ +/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2008-2010 David Zeuthen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#if !defined (_GUDEV_COMPILATION) && !defined(_GUDEV_INSIDE_GUDEV_H) +#error "Only can be included directly, this file may disappear or change contents." +#endif + +#ifndef __G_UDEV_ENUMERATOR_H__ +#define __G_UDEV_ENUMERATOR_H__ + +#include + +G_BEGIN_DECLS + +#define G_UDEV_TYPE_ENUMERATOR (g_udev_enumerator_get_type ()) +#define G_UDEV_ENUMERATOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_UDEV_TYPE_ENUMERATOR, GUdevEnumerator)) +#define G_UDEV_ENUMERATOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_UDEV_TYPE_ENUMERATOR, GUdevEnumeratorClass)) +#define G_UDEV_IS_ENUMERATOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_UDEV_TYPE_ENUMERATOR)) +#define G_UDEV_IS_ENUMERATOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_UDEV_TYPE_ENUMERATOR)) +#define G_UDEV_ENUMERATOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_UDEV_TYPE_ENUMERATOR, GUdevEnumeratorClass)) + +typedef struct _GUdevEnumeratorClass GUdevEnumeratorClass; +typedef struct _GUdevEnumeratorPrivate GUdevEnumeratorPrivate; + +/** + * GUdevEnumerator: + * + * The #GUdevEnumerator struct is opaque and should not be accessed directly. + * + * Since: 165 + */ +struct _GUdevEnumerator +{ + GObject parent; + + /*< private >*/ + GUdevEnumeratorPrivate *priv; +}; + +/** + * GUdevEnumeratorClass: + * @parent_class: Parent class. + * + * Class structure for #GUdevEnumerator. + * + * Since: 165 + */ +struct _GUdevEnumeratorClass +{ + GObjectClass parent_class; + + /*< private >*/ + /* Padding for future expansion */ + void (*reserved1) (void); + void (*reserved2) (void); + void (*reserved3) (void); + void (*reserved4) (void); + void (*reserved5) (void); + void (*reserved6) (void); + void (*reserved7) (void); + void (*reserved8) (void); +}; + +GType g_udev_enumerator_get_type (void) G_GNUC_CONST; +GUdevEnumerator *g_udev_enumerator_new (GUdevClient *client); +GUdevEnumerator *g_udev_enumerator_add_match_subsystem (GUdevEnumerator *enumerator, + const gchar *subsystem); +GUdevEnumerator *g_udev_enumerator_add_nomatch_subsystem (GUdevEnumerator *enumerator, + const gchar *subsystem); +GUdevEnumerator *g_udev_enumerator_add_match_sysfs_attr (GUdevEnumerator *enumerator, + const gchar *name, + const gchar *value); +GUdevEnumerator *g_udev_enumerator_add_nomatch_sysfs_attr (GUdevEnumerator *enumerator, + const gchar *name, + const gchar *value); +GUdevEnumerator *g_udev_enumerator_add_match_property (GUdevEnumerator *enumerator, + const gchar *name, + const gchar *value); +GUdevEnumerator *g_udev_enumerator_add_match_name (GUdevEnumerator *enumerator, + const gchar *name); +GUdevEnumerator *g_udev_enumerator_add_match_tag (GUdevEnumerator *enumerator, + const gchar *tag); +GUdevEnumerator *g_udev_enumerator_add_match_is_initialized (GUdevEnumerator *enumerator); +GUdevEnumerator *g_udev_enumerator_add_sysfs_path (GUdevEnumerator *enumerator, + const gchar *sysfs_path); +GList *g_udev_enumerator_execute (GUdevEnumerator *enumerator); + +G_END_DECLS + +#endif /* __G_UDEV_ENUMERATOR_H__ */ diff --git a/src/udev/gudev/gudevenums.h b/src/udev/gudev/gudevenums.h new file mode 100644 index 0000000000..c3a0aa8747 --- /dev/null +++ b/src/udev/gudev/gudevenums.h @@ -0,0 +1,49 @@ +/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2008 David Zeuthen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#if !defined (_GUDEV_COMPILATION) && !defined(_GUDEV_INSIDE_GUDEV_H) +#error "Only can be included directly, this file may disappear or change contents." +#endif + +#ifndef __G_UDEV_ENUMS_H__ +#define __G_UDEV_ENUMS_H__ + +#include + +G_BEGIN_DECLS + +/** + * GUdevDeviceType: + * @G_UDEV_DEVICE_TYPE_NONE: Device does not have a device file. + * @G_UDEV_DEVICE_TYPE_BLOCK: Device is a block device. + * @G_UDEV_DEVICE_TYPE_CHAR: Device is a character device. + * + * Enumeration used to specify a the type of a device. + */ +typedef enum +{ + G_UDEV_DEVICE_TYPE_NONE = 0, + G_UDEV_DEVICE_TYPE_BLOCK = 'b', + G_UDEV_DEVICE_TYPE_CHAR = 'c', +} GUdevDeviceType; + +G_END_DECLS + +#endif /* __G_UDEV_ENUMS_H__ */ diff --git a/src/udev/gudev/gudevenumtypes.c.template b/src/udev/gudev/gudevenumtypes.c.template new file mode 100644 index 0000000000..fc30b39e2e --- /dev/null +++ b/src/udev/gudev/gudevenumtypes.c.template @@ -0,0 +1,39 @@ +/*** BEGIN file-header ***/ +#include + +/*** END file-header ***/ + +/*** BEGIN file-production ***/ +/* enumerations from "@filename@" */ +/*** END file-production ***/ + +/*** BEGIN value-header ***/ +GType +@enum_name@_get_type (void) +{ + static volatile gsize g_define_type_id__volatile = 0; + + if (g_once_init_enter (&g_define_type_id__volatile)) + { + static const G@Type@Value values[] = { +/*** END value-header ***/ + +/*** BEGIN value-production ***/ + { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, +/*** END value-production ***/ + +/*** BEGIN value-tail ***/ + { 0, NULL, NULL } + }; + GType g_define_type_id = + g_@type@_register_static (g_intern_static_string ("@EnumName@"), values); + g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); + } + + return g_define_type_id__volatile; +} + +/*** END value-tail ***/ + +/*** BEGIN file-tail ***/ +/*** END file-tail ***/ diff --git a/src/udev/gudev/gudevenumtypes.h.template b/src/udev/gudev/gudevenumtypes.h.template new file mode 100644 index 0000000000..d0ab3393e6 --- /dev/null +++ b/src/udev/gudev/gudevenumtypes.h.template @@ -0,0 +1,24 @@ +/*** BEGIN file-header ***/ +#ifndef __GUDEV_ENUM_TYPES_H__ +#define __GUDEV_ENUM_TYPES_H__ + +#include + +G_BEGIN_DECLS +/*** END file-header ***/ + +/*** BEGIN file-production ***/ + +/* enumerations from "@filename@" */ +/*** END file-production ***/ + +/*** BEGIN value-header ***/ +GType @enum_name@_get_type (void) G_GNUC_CONST; +#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ()) +/*** END value-header ***/ + +/*** BEGIN file-tail ***/ +G_END_DECLS + +#endif /* __GUDEV_ENUM_TYPES_H__ */ +/*** END file-tail ***/ diff --git a/src/udev/gudev/gudevmarshal.list b/src/udev/gudev/gudevmarshal.list new file mode 100644 index 0000000000..7e665999e8 --- /dev/null +++ b/src/udev/gudev/gudevmarshal.list @@ -0,0 +1 @@ +VOID:STRING,OBJECT diff --git a/src/udev/gudev/gudevprivate.h b/src/udev/gudev/gudevprivate.h new file mode 100644 index 0000000000..8866f52b88 --- /dev/null +++ b/src/udev/gudev/gudevprivate.h @@ -0,0 +1,41 @@ +/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2008 David Zeuthen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#if !defined (_GUDEV_COMPILATION) && !defined(_GUDEV_INSIDE_GUDEV_H) +#error "Only can be included directly, this file may disappear or change contents." +#endif + +#ifndef __G_UDEV_PRIVATE_H__ +#define __G_UDEV_PRIVATE_H__ + +#include + +#include + +G_BEGIN_DECLS + +GUdevDevice * +_g_udev_device_new (struct udev_device *udevice); + +struct udev *_g_udev_client_get_udev (GUdevClient *client); + +G_END_DECLS + +#endif /* __G_UDEV_PRIVATE_H__ */ diff --git a/src/udev/gudev/gudevtypes.h b/src/udev/gudev/gudevtypes.h new file mode 100644 index 0000000000..888482783d --- /dev/null +++ b/src/udev/gudev/gudevtypes.h @@ -0,0 +1,51 @@ +/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2008 David Zeuthen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#if !defined (_GUDEV_COMPILATION) && !defined(_GUDEV_INSIDE_GUDEV_H) +#error "Only can be included directly, this file may disappear or change contents." +#endif + +#ifndef __G_UDEV_TYPES_H__ +#define __G_UDEV_TYPES_H__ + +#include +#include + +G_BEGIN_DECLS + +typedef struct _GUdevClient GUdevClient; +typedef struct _GUdevDevice GUdevDevice; +typedef struct _GUdevEnumerator GUdevEnumerator; + +/** + * GUdevDeviceNumber: + * + * Corresponds to the standard #dev_t type as defined by POSIX (Until + * bug 584517 is resolved this work-around is needed). + */ +#ifdef _GUDEV_WORK_AROUND_DEV_T_BUG +typedef guint64 GUdevDeviceNumber; /* __UQUAD_TYPE */ +#else +typedef dev_t GUdevDeviceNumber; +#endif + +G_END_DECLS + +#endif /* __G_UDEV_TYPES_H__ */ diff --git a/src/udev/gudev/seed-example-enum.js b/src/udev/gudev/seed-example-enum.js new file mode 100755 index 0000000000..66206ad806 --- /dev/null +++ b/src/udev/gudev/seed-example-enum.js @@ -0,0 +1,38 @@ +#!/usr/bin/env seed + +const GLib = imports.gi.GLib; +const GUdev = imports.gi.GUdev; + +function print_device(device) { + print(" initialized: " + device.get_is_initialized()); + print(" usec since initialized: " + device.get_usec_since_initialized()); + print(" subsystem: " + device.get_subsystem()); + print(" devtype: " + device.get_devtype()); + print(" name: " + device.get_name()); + print(" number: " + device.get_number()); + print(" sysfs_path: " + device.get_sysfs_path()); + print(" driver: " + device.get_driver()); + print(" action: " + device.get_action()); + print(" seqnum: " + device.get_seqnum()); + print(" device type: " + device.get_device_type()); + print(" device number: " + device.get_device_number()); + print(" device file: " + device.get_device_file()); + print(" device file symlinks: " + device.get_device_file_symlinks()); + print(" tags: " + device.get_tags()); + var keys = device.get_property_keys(); + for (var n = 0; n < keys.length; n++) { + print(" " + keys[n] + "=" + device.get_property(keys[n])); + } +} + +var client = new GUdev.Client({subsystems: []}); +var enumerator = new GUdev.Enumerator({client: client}); +enumerator.add_match_subsystem('b*') + +var devices = enumerator.execute(); + +for (var n=0; n < devices.length; n++) { + var device = devices[n]; + print_device(device); + print(""); +} diff --git a/src/udev/gudev/seed-example.js b/src/udev/gudev/seed-example.js new file mode 100755 index 0000000000..e2ac324d23 --- /dev/null +++ b/src/udev/gudev/seed-example.js @@ -0,0 +1,72 @@ +#!/usr/bin/env seed + +// seed example + +const GLib = imports.gi.GLib; +const GUdev = imports.gi.GUdev; + +function print_device (device) { + print (" subsystem: " + device.get_subsystem ()); + print (" devtype: " + device.get_devtype ()); + print (" name: " + device.get_name ()); + print (" number: " + device.get_number ()); + print (" sysfs_path: " + device.get_sysfs_path ()); + print (" driver: " + device.get_driver ()); + print (" action: " + device.get_action ()); + print (" seqnum: " + device.get_seqnum ()); + print (" device type: " + device.get_device_type ()); + print (" device number: " + device.get_device_number ()); + print (" device file: " + device.get_device_file ()); + print (" device file symlinks: " + device.get_device_file_symlinks ()); + print (" foo: " + device.get_sysfs_attr_as_strv ("stat")); + var keys = device.get_property_keys (); + for (var n = 0; n < keys.length; n++) { + print (" " + keys[n] + "=" + device.get_property (keys[n])); + } +} + +function on_uevent (client, action, device) { + print ("action " + action + " on device " + device.get_sysfs_path()); + print_device (device); + print (""); +} + +var client = new GUdev.Client ({subsystems: ["block", "usb/usb_interface"]}); +client.signal.connect ("uevent", on_uevent); + +var block_devices = client.query_by_subsystem ("block"); +for (var n = 0; n < block_devices.length; n++) { + print ("block device: " + block_devices[n].get_device_file ()); +} + +var d; + +d = client.query_by_device_number (GUdev.DeviceType.BLOCK, 0x0810); +if (d == null) { + print ("query_by_device_number 0x810 -> null"); +} else { + print ("query_by_device_number 0x810 -> " + d.get_device_file ()); + dd = d.get_parent_with_subsystem ("usb", null); + print_device (dd); + print ("--------------------------------------------------------------------------"); + while (d != null) { + print_device (d); + print (""); + d = d.get_parent (); + } +} + +d = client.query_by_sysfs_path ("/sys/block/sda/sda1"); +print ("query_by_sysfs_path (\"/sys/block/sda1\") -> " + d.get_device_file ()); + +d = client.query_by_subsystem_and_name ("block", "sda2"); +print ("query_by_subsystem_and_name (\"block\", \"sda2\") -> " + d.get_device_file ()); + +d = client.query_by_device_file ("/dev/sda"); +print ("query_by_device_file (\"/dev/sda\") -> " + d.get_device_file ()); + +d = client.query_by_device_file ("/dev/block/8:0"); +print ("query_by_device_file (\"/dev/block/8:0\") -> " + d.get_device_file ()); + +var mainloop = GLib.main_loop_new (); +GLib.main_loop_run (mainloop); diff --git a/src/udev/keymap/.gitignore b/src/udev/keymap/.gitignore new file mode 100644 index 0000000000..4567584f4e --- /dev/null +++ b/src/udev/keymap/.gitignore @@ -0,0 +1,5 @@ +keyboard-force-release.sh +keys-from-name.gperf +keys-from-name.h +keys-to-name.h +keys.txt diff --git a/src/udev/keymap/95-keyboard-force-release.rules b/src/udev/keymap/95-keyboard-force-release.rules new file mode 100644 index 0000000000..03d56e8aa4 --- /dev/null +++ b/src/udev/keymap/95-keyboard-force-release.rules @@ -0,0 +1,54 @@ +# Set model specific atkbd force_release quirk +# +# Several laptops have hotkeys which don't generate release events, +# which can cause problems with software key repeat. +# The atkbd driver has a quirk handler for generating synthetic +# release events, which can be configured via sysfs since 2.6.32. +# Simply add a file with a list of scancodes for your laptop model +# in /usr/lib/udev/keymaps, and add a rule here. +# If the hotkeys also need a keymap assignment you can copy the +# scancodes from the keymap file, otherwise you can run +# /usr/lib/udev/keymap -i /dev/input/eventX +# on a Linux vt to find out. + +ACTION=="remove", GOTO="force_release_end" +SUBSYSTEM!="serio", GOTO="force_release_end" +KERNEL!="serio*", GOTO="force_release_end" +DRIVER!="atkbd", GOTO="force_release_end" + +ENV{DMI_VENDOR}="$attr{[dmi/id]sys_vendor}" + +ENV{DMI_VENDOR}=="[sS][aA][mM][sS][uU][nN][gG]*", RUN+="keyboard-force-release.sh $devpath samsung-other" +ENV{DMI_VENDOR}=="[sS][aA][mM][sS][uU][nN][gG]*", ATTR{[dmi/id]product_name}=="*90X3A*", RUN+="keyboard-force-release.sh $devpath samsung-90x3a" + +ENV{DMI_VENDOR}=="Dell Inc.", ATTR{[dmi/id]product_name}=="Studio 1557|Studio 1558", RUN+="keyboard-force-release.sh $devpath common-volume-keys" +ENV{DMI_VENDOR}=="Dell Inc.", ATTR{[dmi/id]product_name}=="Latitude E*|Precision M*", RUN+="keyboard-force-release.sh $devpath dell-touchpad" + +ENV{DMI_VENDOR}=="FUJITSU SIEMENS", ATTR{[dmi/id]product_name}=="AMILO Si 1848+u|AMILO Xi 2428", RUN+="keyboard-force-release.sh $devpath common-volume-keys" + +ENV{DMI_VENDOR}=="FOXCONN", ATTR{[dmi/id]product_name}=="QBOOK", RUN+="keyboard-force-release.sh $devpath common-volume-keys" + +ENV{DMI_VENDOR}=="MTC", ATTR{[dmi/id]product_version}=="A0", RUN+="keyboard-force-release.sh $devpath common-volume-keys" + +ENV{DMI_VENDOR}=="PEGATRON CORP.", ATTR{[dmi/id]product_name}=="Spring Peak", RUN+="keyboard-force-release.sh $devpath common-volume-keys" + +ENV{DMI_VENDOR}=="TOSHIBA", ATTR{[dmi/id]product_name}=="Satellite [uU]300*|Satellite Pro [uU]300*|Satellite [uU]305*|SATELLITE [uU]500*", RUN+="keyboard-force-release.sh $devpath common-volume-keys" + +ENV{DMI_VENDOR}=="Viooo Corporation", ATTR{[dmi/id]product_name}=="PT17", RUN+="keyboard-force-release.sh $devpath common-volume-keys" + +# These are all the HP laptops that setup a touchpad toggle key +ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*[pP][aA][vV][iI][lL][iI][oO][nN]*", RUN+="keyboard-force-release.sh $devpath hp-other" +ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*[tT][xX]2*", RUN+="keyboard-force-release.sh $devpath hp-other" +ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*2510p*|*2530p*|HP G60 Notebook PC", RUN+="keyboard-force-release.sh $devpath hp-other" + +ENV{DMI_VENDOR}=="Zepto", ATTR{[dmi/id]product_name}=="Znote 6615WD", RUN+="keyboard-force-release.sh $devpath common-volume-keys" + +ENV{DMI_VENDOR}=="Zepto", ATTR{[dmi/id]product_name}=="Znote", ATTR{[dmi/id]product_version}=="6625WD", RUN+="keyboard-force-release.sh $devpath common-volume-keys" + +ENV{DMI_VENDOR}=="HANNspree", ATTR{[dmi/id]product_name}=="SN10E100", RUN+="keyboard-force-release.sh $devpath common-volume-keys" + +ENV{DMI_VENDOR}=="GIGABYTE", ATTR{[dmi/id]product_name}=="i1520M", RUN+="keyboard-force-release.sh $devpath common-volume-keys" + +ENV{DMI_VENDOR}=="BenQ", ATTR{[dmi/id]product_name}=="*nScreen*", RUN+="keyboard-force-release.sh $devpath common-volume-keys" + +LABEL="force_release_end" diff --git a/src/udev/keymap/95-keymap.rules b/src/udev/keymap/95-keymap.rules new file mode 100644 index 0000000000..bbf311a17a --- /dev/null +++ b/src/udev/keymap/95-keymap.rules @@ -0,0 +1,170 @@ +# Set model specific hotkey keycodes. +# +# Key map overrides can be specified by either giving scancode/keyname pairs +# directly as keymap arguments (if there are just one or two to change), or as +# a file name (in /usr/lib/udev/keymaps), which has to contain scancode/keyname +# pairs. + +ACTION=="remove", GOTO="keyboard_end" +KERNEL!="event*", GOTO="keyboard_end" +ENV{ID_INPUT_KEY}=="", GOTO="keyboard_end" +SUBSYSTEMS=="bluetooth", GOTO="keyboard_end" + +SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" +SUBSYSTEMS=="usb", GOTO="keyboard_usbcheck" +GOTO="keyboard_modulecheck" + +# +# The following are external USB keyboards +# + +LABEL="keyboard_usbcheck" + +ENV{ID_VENDOR}=="Genius", ENV{ID_MODEL_ID}=="0708", ENV{ID_USB_INTERFACE_NUM}=="01", RUN+="keymap $name genius-slimstar-320" +ENV{ID_VENDOR}=="Logitech*", ATTRS{name}=="Logitech USB Multimedia Keyboard", RUN+="keymap $name logitech-wave" +ENV{ID_VENDOR}=="Logitech*", ATTRS{name}=="Logitech USB Receiver", RUN+="keymap $name logitech-wave-cordless" +# Logitech Cordless Wave Pro looks slightly weird; some hotkeys are coming through the mouse interface +ENV{ID_VENDOR_ID}=="046d", ENV{ID_MODEL_ID}=="c52[9b]", ATTRS{name}=="Logitech USB Receiver", RUN+="keymap $name logitech-wave-pro-cordless" + +ENV{ID_VENDOR}=="Lite-On_Technology_Corp*", ATTRS{name}=="Lite-On Technology Corp. ThinkPad USB Keyboard with TrackPoint", RUN+="keymap $name lenovo-thinkpad-usb-keyboard-trackpoint" +ENV{ID_VENDOR_ID}=="04b3", ENV{ID_MODEL_ID}=="301[89]", RUN+="keymap $name ibm-thinkpad-usb-keyboard-trackpoint" + +ENV{ID_VENDOR}=="Microsoft", ENV{ID_MODEL_ID}=="00db", RUN+="keymap $name 0xc022d zoomin 0xc022e zoomout" + +GOTO="keyboard_end" + +# +# The following are exposed as separate input devices with low key codes, thus +# we need to check their input device product name +# + +LABEL="keyboard_modulecheck" + +ENV{DMI_VENDOR}="$attr{[dmi/id]sys_vendor}" +ENV{DMI_VENDOR}=="", GOTO="keyboard_end" + +ENV{DMI_VENDOR}=="LENOVO*", KERNELS=="input*", ATTRS{name}=="ThinkPad Extra Buttons", RUN+="keymap $name module-lenovo" +ENV{DMI_VENDOR}=="LENOVO*", KERNELS=="input*", ATTRS{name}=="Lenovo ThinkPad SL Series extra buttons", RUN+="keymap $name 0x0E bluetooth" + +ENV{DMI_VENDOR}=="ASUS*", KERNELS=="input*", ATTRS{name}=="Asus Extra Buttons", ATTR{[dmi/id]product_name}=="W3J", RUN+="keymap $name module-asus-w3j" +ENV{DMI_VENDOR}=="ASUS*", KERNELS=="input*", ATTRS{name}=="Eee PC WMI hotkeys|Asus Laptop Support|Asus*WMI*", RUN+="keymap $name 0x6B f21" +ENV{DMI_VENDOR}=="ASUS*", KERNELS=="input*", ATTRS{name}=="Eee PC Hotkey Driver", RUN+="keymap $name 0x37 f21" + +ENV{DMI_VENDOR}=="IBM*", KERNELS=="input*", ATTRS{name}=="ThinkPad Extra Buttons", RUN+="keymap $name module-ibm" +ENV{DMI_VENDOR}=="Sony*", KERNELS=="input*", ATTRS{name}=="Sony Vaio Keys", RUN+="keymap $name module-sony" +ENV{DMI_VENDOR}=="Acer*", KERNELS=="input*", ATTRS{name}=="Acer WMI hotkeys", RUN+="keymap $name 0x82 f21" +ENV{DMI_VENDOR}=="MICRO-STAR*|Micro-Star*", KERNELS=="input*", ATTRS{name}=="MSI Laptop hotkeys", RUN+="keymap $name 0x213 f22 0x214 f23" + +# Older Vaios have some different keys +ENV{DMI_VENDOR}=="Sony*", ATTR{[dmi/id]product_name}=="*PCG-C1*|*PCG-K25*|*PCG-F1*|*PCG-F2*|*PCG-F3*|*PCG-F4*|*PCG-F5*|*PCG-F6*|*PCG-FX*|*PCG-FRV*|*PCG-GR*|*PCG-TR*|*PCG-NV*|*PCG-Z*|*VGN-S360*", ATTRS{name}=="Sony Vaio Keys", RUN+="keymap $name module-sony-old" + +# Some Sony VGN models have yet another one +ENV{DMI_VENDOR}=="Sony*", ATTR{[dmi/id]product_name}=="VGN-AR71*|VGN-FW*|VGN-Z21*", ATTRS{name}=="Sony Vaio Keys", RUN+="keymap $name module-sony-vgn" + + +# +# The following rules belong to standard i8042 AT keyboard with high key codes. +# + +DRIVERS=="atkbd", GOTO="keyboard_vendorcheck" +GOTO="keyboard_end" + +LABEL="keyboard_vendorcheck" + +ENV{DMI_VENDOR}=="Dell*", RUN+="keymap $name dell" +ENV{DMI_VENDOR}=="Dell*", ATTR{[dmi/id]product_name}=="Inspiron 910|Inspiron 1010|Inspiron 1011|Inspiron 1012|Inspiron 1110|Inspiron 1210", RUN+="keymap $name 0x84 wlan" +ENV{DMI_VENDOR}=="Dell*", ATTR{[dmi/id]product_name}=="Latitude XT2", RUN+="keymap $name dell-latitude-xt2" + +ENV{DMI_VENDOR}=="Compaq*", ATTR{[dmi/id]product_name}=="*E500*|*Evo N*", RUN+="keymap $name compaq-e_evo" + +ENV{DMI_VENDOR}=="LENOVO*", ATTR{[dmi/id]product_version}=="*3000*", RUN+="keymap $name lenovo-3000" +ENV{DMI_VENDOR}=="LENOVO*", ATTR{[dmi/id]product_version}=="ThinkPad X6*", ATTR{[dmi/id]product_version}=="* Tablet", RUN+="keymap $name lenovo-thinkpad_x6_tablet" +ENV{DMI_VENDOR}=="LENOVO*", ATTR{[dmi/id]product_version}=="ThinkPad X2[02]* Tablet*", ATTR{[dmi/id]product_version}=="* Tablet", RUN+="keymap $name lenovo-thinkpad_x200_tablet" +ENV{DMI_VENDOR}=="LENOVO*", ATTR{[dmi/id]product_version}=="*IdeaPad*", RUN+="keymap $name lenovo-ideapad" +ENV{DMI_VENDOR}=="LENOVO*", ATTR{[dmi/id]product_name}=="S10-*", RUN+="keymap $name lenovo-ideapad" +ENV{DMI_VENDOR}=="LENOVO", ATTR{[dmi/id]product_version}=="*IdeaPad Y550*", RUN+="keymap $name 0x95 media 0xA3 play" + +ENV{DMI_VENDOR}=="Hewlett-Packard*", RUN+="keymap $name hewlett-packard" +ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*[tT][aA][bB][lL][eE][tT]*", RUN+="keymap $name hewlett-packard-tablet" +ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*[pP][aA][vV][iI][lL][iI][oO][nN]*", RUN+="keymap $name hewlett-packard-pavilion" +ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*Compaq*|*EliteBook*|*2230s*", RUN+="keymap $name hewlett-packard-compaq_elitebook" +ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*2510p*|*2530p*|HP G60 Notebook PC", RUN+="keymap $name hewlett-packard-2510p_2530p" +ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*[tT][xX]2*", RUN+="keymap $name hewlett-packard-tx2" +ENV{DMI_VENDOR}=="Hewlett-Packard", ATTR{[dmi/id]product_name}=="Presario 2100*", RUN+="keymap $name hewlett-packard-presario-2100" +ENV{DMI_VENDOR}=="Hewlett-Packard", ATTR{[dmi/id]product_name}=="HP G62 Notebook PC", RUN+="keymap $name 0xB2 www" +ENV{DMI_VENDOR}=="Hewlett-Packard", ATTR{[dmi/id]product_name}=="HP ProBook*", RUN+="keymap $name 0xF8 rfkill" +# HP Pavillion dv6315ea has empty DMI_VENDOR +ATTR{[dmi/id]board_vendor}=="Quanta", ATTR{[dmi/id]board_name}=="30B7", ATTR{[dmi/id]board_version}=="65.2B", RUN+="keymap $name 0x88 media" # "quick play + +# Gateway clone of Acer Aspire One AOA110/AOA150 +ENV{DMI_VENDOR}=="Gateway*", ATTR{[dmi/id]product_name}=="*AOA1*", RUN+="keymap $name acer" + +ENV{DMI_VENDOR}=="Acer*", RUN+="keymap $name acer" +ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_name}=="Extensa*", ATTR{[dmi/id]product_name}=="*5210*|*5220*|*5610*|*5620*|*5720*", RUN+="keymap $name 0xEE screenlock" +ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_name}=="TravelMate*C3[01]0*", RUN+="keymap $name acer-travelmate_c300" +ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_name}=="TravelMate*6292*|TravelMate*8471*|TravelMate*4720*|TravelMate*7720*|Aspire 1810T*|AO751h|AO531h", RUN+="keymap $name 0xD9 bluetooth" +ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_name}=="TravelMate*4720*", RUN+="keymap $name 0xB2 www 0xEE screenlock" +ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_name}=="TravelMate 6593|Aspire 1640", RUN+="keymap $name 0xB2 www 0xEE screenlock" +ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_name}=="Aspire 6920", RUN+="keymap $name acer-aspire_6920" +ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_name}=="Aspire 5920G", RUN+="keymap $name acer-aspire_5920g" +ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_name}=="Aspire 5720*", RUN+="keymap $name acer-aspire_5720" +ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_name}=="Aspire 8930", RUN+="keymap $name acer-aspire_8930" +ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_serial}=="ZG8*", RUN+="keymap $name acer-aspire_5720" + +ENV{DMI_VENDOR}=="*BenQ*", ATTR{[dmi/id]product_name}=="*Joybook R22*", RUN+="keymap $name 0x6E wlan" + +ENV{DMI_VENDOR}=="FUJITSU*", ATTR{[dmi/id]product_name}=="*AMILO Pro V3205*", RUN+="keymap $name fujitsu-amilo_pro_v3205" +ENV{DMI_VENDOR}=="FUJITSU*", ATTR{[dmi/id]product_name}=="*AMILO Pa 2548*", RUN+="keymap $name fujitsu-amilo_pa_2548" +ENV{DMI_VENDOR}=="FUJITSU*", ATTR{[dmi/id]product_name}=="*ESPRIMO Mobile V5*", RUN+="keymap $name fujitsu-esprimo_mobile_v5" +ENV{DMI_VENDOR}=="FUJITSU*", ATTR{[dmi/id]product_name}=="*ESPRIMO Mobile V6*", RUN+="keymap $name fujitsu-esprimo_mobile_v6" +ENV{DMI_VENDOR}=="FUJITSU*", ATTR{[dmi/id]product_name}=="*AMILO Pro Edition V3505*", RUN+="keymap $name fujitsu-amilo_pro_edition_v3505" +ENV{DMI_VENDOR}=="FUJITSU*", ATTR{[dmi/id]product_name}=="*Amilo Si 1520*", RUN+="keymap $name fujitsu-amilo_si_1520" +ENV{DMI_VENDOR}=="FUJITSU*", ATTR{[dmi/id]product_name}=="AMILO*M*", RUN+="keymap $name 0x97 prog2 0x9F prog1" +ENV{DMI_VENDOR}=="FUJITSU*", ATTR{[dmi/id]product_name}=="Amilo Li 1718", RUN+="keymap $name 0xD6 wlan" +ENV{DMI_VENDOR}=="FUJITSU*", ATTR{[dmi/id]product_name}=="AMILO Li 2732", RUN+="keymap $name fujitsu-amilo_li_2732" + +ENV{DMI_VENDOR}=="LG*", ATTR{[dmi/id]product_name}=="*X110*", RUN+="keymap $name lg-x110" + +ENV{DMI_VENDOR}=="MEDION*", ATTR{[dmi/id]product_name}=="*FID2060*", RUN+="keymap $name medion-fid2060" +ENV{DMI_VENDOR}=="MEDIONNB", ATTR{[dmi/id]product_name}=="A555*", RUN+="keymap $name medionnb-a555" + +ENV{DMI_VENDOR}=="MICRO-STAR*|Micro-Star*", RUN+="keymap $name micro-star" + +# some MSI models generate ACPI/input events on the LNXVIDEO input devices, +# plus some extra synthesized ones on atkbd as an echo of actually changing the +# brightness; so ignore those atkbd ones, to avoid loops +ENV{DMI_VENDOR}=="MICRO-STAR*", ATTR{[dmi/id]product_name}=="*U-100*|*U100*|*N033", RUN+="keymap $name 0xF7 reserved 0xF8 reserved" + +ENV{DMI_VENDOR}=="INVENTEC", ATTR{[dmi/id]product_name}=="SYMPHONY 6.0/7.0", RUN+="keymap $name inventec-symphony_6.0_7.0" + +ENV{DMI_VENDOR}=="MAXDATA", ATTR{[dmi/id]product_name}=="Pro 7000*", RUN+="keymap $name maxdata-pro_7000" + +ENV{DMI_VENDOR}=="[sS][aA][mM][sS][uU][nN][gG]*", RUN+="keymap $name samsung-other" +ENV{DMI_VENDOR}=="[sS][aA][mM][sS][uU][nN][gG]*", ATTR{[dmi/id]product_name}=="*SX20S*", RUN+="keymap $name samsung-sx20s" +ENV{DMI_VENDOR}=="[sS][aA][mM][sS][uU][nN][gG]*", ATTR{[dmi/id]product_name}=="SQ1US", RUN+="keymap $name samsung-sq1us" +ENV{DMI_VENDOR}=="[sS][aA][mM][sS][uU][nN][gG]*", ATTR{[dmi/id]product_name}=="*700Z*", RUN+="keymap $name 0xBA ejectcd 0x96 keyboardbrightnessup 0x97 keyboardbrightnessdown" +ENV{DMI_VENDOR}=="[sS][aA][mM][sS][uU][nN][gG]*", ATTR{[dmi/id]product_name}=="*90X3A*", RUN+="keymap $name samsung-90x3a" + +ENV{DMI_VENDOR}=="TOSHIBA", ATTR{[dmi/id]product_name}=="SATELLITE A100", RUN+="keymap $name toshiba-satellite_a100" +ENV{DMI_VENDOR}=="TOSHIBA", ATTR{[dmi/id]product_name}=="Satellite A110", RUN+="keymap $name toshiba-satellite_a110" +ENV{DMI_VENDOR}=="TOSHIBA", ATTR{[dmi/id]product_name}=="Satellite M30X", RUN+="keymap $name toshiba-satellite_m30x" + +ENV{DMI_VENDOR}=="OQO Inc.*", ATTR{[dmi/id]product_name}=="OQO Model 2*", RUN+="keymap $name oqo-model2" + +ENV{DMI_VENDOR}=="ONKYO CORPORATION", ATTR{[dmi/id]product_name}=="ONKYOPC", RUN+="keymap $name onkyo" + +ENV{DMI_VENDOR}=="ASUS", RUN+="keymap $name asus" + +ENV{DMI_VENDOR}=="VIA", ATTR{[dmi/id]product_name}=="K8N800", ATTR{[dmi/id]product_version}=="VT8204B", RUN+="keymap $name 0x81 prog1" + +ENV{DMI_VENDOR}=="Zepto", ATTR{[dmi/id]product_name}=="Znote", ATTR{[dmi/id]product_version}=="62*|63*", RUN+="keymap $name zepto-znote" + +ENV{DMI_VENDOR}=="Everex", ATTR{[dmi/id]product_name}=="XT5000*", RUN+="keymap $name everex-xt5000" + +ENV{DMI_VENDOR}=="COMPAL", ATTR{[dmi/id]product_name}=="HEL80I", RUN+="keymap $name 0x84 wlan" + +ENV{DMI_VENDOR}=="OLPC", ATTR{[dmi/id]product_name}=="XO", RUN+="keymap $name olpc-xo" + +ENV{DMI_VENDOR}=="Alienware*", ATTR{[dmi/id]product_name}=="M14xR1", RUN+="keymap $name 0x8A ejectcd" + +LABEL="keyboard_end" diff --git a/src/udev/keymap/README.keymap.txt b/src/udev/keymap/README.keymap.txt new file mode 100644 index 0000000000..52d50ed2de --- /dev/null +++ b/src/udev/keymap/README.keymap.txt @@ -0,0 +1,101 @@ += The udev keymap tool = + +== Introduction == + +This udev extension configures computer model specific key mappings. This is +particularly necessary for the non-standard extra keys found on many laptops, +such as "brightness up", "next song", "www browser", or "suspend". Often these +are accessed with the Fn key. + +Every key produces a "scan code", which is highly vendor/model specific for the +nonstandard keys. This tool maintains mappings for these scan codes to standard +"key codes", which denote the "meaning" of the key. The key codes are defined +in /usr/include/linux/input.h. + +If some of your keys on your keyboard are not working at all, or produce the +wrong effect, then a very likely cause of this is that the scan code -> key +code mapping is incorrect on your computer. + +== Structure == + +udev-keymap consists of the following parts: + + keymaps/*:: mappings of scan codes to key code names + + 95-keymap.rules:: udev rules for mapping system vendor/product names and + input module names to one of the keymaps above + + keymap:: manipulate an evdev input device: + * write a key map file into a device (used by udev rules) + * dump current scan → key code mapping + * interactively display scan and key codes of pressed keys + + findkeyboards:: display evdev input devices which belong to actual keyboards, + i. e. those suitable for the keymap program + + fdi2rules.py:: convert hal keymap FDIs into udev rules and key map files + (Please note that this is far from perfect, since the mapping between fdi and + udev rules is not straightforward, and impossible in some cases.) + +== Fixing broken keys == + +In order to make a broken key work on your system and send it back to upstream +for inclusion you need to do the following steps: + + 1. Find the keyboard device. + + Run /usr/lib/udev/findkeyboards. This should always give you an "AT + keyboard" and possibly a "module". Some laptops (notably Thinkpads, Sonys, and + Acers) have multimedia/function keys on a separate input device instead of the + primary keyboard. The keyboard device should have a name like "input/event3". + In the following commands, the name will be written as "input/eventX" (replace + X with the appropriate number). + + 2. Find broken scan codes: + + sudo /usr/lib/udev/keymap -i input/eventX + + Press all multimedia/function keys and check if the key name that gets printed + out is plausible. If it is unknown or wrong, write down the scan code (looks + like "0x1E") and the intended functionality of this key. Look in + /usr/include/linux/input.h for an available KEY_XXXXX constant which most + closely approximates this functionality and write it down as the new key code. + + For example, you might press a key labeled "web browser" which currently + produces "unknown". Note down this: + + 0x1E www # Fn+F2 web browser + + Repeat that for all other keys. Write the resulting list into a file. Look at + /usr/lib/udev/keymaps/ for existing key map files and make sure that you use the + same structure. + + If the key only ever works once and then your keyboard (or the entire desktop) + gets stuck for a long time, then it is likely that the BIOS fails to send a + corresponding "key release" event after the key press event. Please note down + this case as well, as it can be worked around in + /usr/lib/udev/keymaps/95-keyboard-force-release.rules . + + 3. Find out your system vendor and product: + + cat /sys/class/dmi/id/sys_vendor + cat /sys/class/dmi/id/product_name + + 4. Generate a device dump with "udevadm info --export-db > /tmp/udev-db.txt". + + 6. Send the system vendor/product names, the key mapping from step 2, + and /tmp/udev-db.txt from step 4 to the linux-hotplug@vger.kernel.org mailing + list, so that they can be included in the next release. + +For local testing, copy your map file to /usr/lib/udev/keymaps/ with an appropriate +name, and add an appropriate udev rule to /usr/lib/udev/rules.d/95-keymap.rules: + + * If you selected an "AT keyboard", add the rule to the section after + 'LABEL="keyboard_vendorcheck"'. + + * If you selected a "module", add the rule to the top section where the + "ThinkPad Extra Buttons" are. + +== Author == + +keymap is written and maintained by Martin Pitt . diff --git a/src/udev/keymap/check-keymaps.sh b/src/udev/keymap/check-keymaps.sh new file mode 100755 index 0000000000..405168c667 --- /dev/null +++ b/src/udev/keymap/check-keymaps.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +# check that all key names in keymaps/* are known in +# and that all key maps listed in the rules are valid and present in +# Makefile.am +SRCDIR=${1:-.} +KEYLIST=${2:-src/keymap/keys.txt} +KEYMAPS_DIR=$SRCDIR/src/keymap/keymaps +RULES=$SRCDIR/src/keymap/95-keymap.rules + +[ -e "$KEYLIST" ] || { + echo "need $KEYLIST please build first" >&2 + exit 1 +} + +missing=$(join -v 2 <(awk '{print tolower(substr($1,5))}' $KEYLIST | sort -u) \ + <(grep -hv '^#' ${KEYMAPS_DIR}/*| awk '{print $2}' | sort -u)) +[ -z "$missing" ] || { + echo "ERROR: unknown key names in src/keymap/keymaps/*:" >&2 + echo "$missing" >&2 + exit 1 +} + +# check that all maps referred to in $RULES exist +maps=$(sed -rn '/keymap \$name/ { s/^.*\$name ([^"[:space:]]+).*$/\1/; p }' $RULES) +for m in $maps; do + # ignore inline mappings + [ "$m" = "${m#0x}" ] || continue + + [ -e ${KEYMAPS_DIR}/$m ] || { + echo "ERROR: unknown map name in $RULES: $m" >&2 + exit 1 + } + grep -q "src/keymap/keymaps/$m\>" $SRCDIR/Makefile.am || { + echo "ERROR: map file $m is not added to Makefile.am" >&2 + exit 1 + } +done diff --git a/src/udev/keymap/findkeyboards b/src/udev/keymap/findkeyboards new file mode 100755 index 0000000000..9ce27429b2 --- /dev/null +++ b/src/udev/keymap/findkeyboards @@ -0,0 +1,68 @@ +#!/bin/sh -e +# Find "real" keyboard devices and print their device path. +# Author: Martin Pitt +# +# Copyright (C) 2009, Canonical Ltd. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. + +# returns OK if $1 contains $2 +strstr() { + [ "${1#*$2*}" != "$1" ] +} + +# returns OK if $1 contains $2 at the beginning +str_starts() { + [ "${1#$2*}" != "$1" ] +} + +str_line_starts() { + while read a; do str_starts "$a" "$1" && return 0;done + return 1; +} + +# print a list of input devices which are keyboard-like +keyboard_devices() { + # standard AT keyboard + for dev in `udevadm trigger --dry-run --verbose --property-match=ID_INPUT_KEYBOARD=1`; do + walk=`udevadm info --attribute-walk --path=$dev` + env=`udevadm info --query=env --path=$dev` + # filter out non-event devices, such as the parent input devices which have no devnode + if ! echo "$env" | str_line_starts 'DEVNAME='; then + continue + fi + if strstr "$walk" 'DRIVERS=="atkbd"'; then + echo -n 'AT keyboard: ' + elif echo "$env" | str_line_starts 'ID_USB_DRIVER=usbhid'; then + echo -n 'USB keyboard: ' + else + echo -n 'Unknown type: ' + fi + udevadm info --query=name --path=$dev + done + + # modules + module=$(udevadm trigger --verbose --dry-run --subsystem-match=input --attr-match=name='*Extra Buttons') + module="$module + $(udevadm trigger --verbose --dry-run --subsystem-match=input --attr-match=name='*extra buttons')" + module="$module + $(udevadm trigger --verbose --dry-run --subsystem-match=input --attr-match=name='Sony Vaio Keys')" + for m in $module; do + for evdev in $m/event*/dev; do + if [ -e "$evdev" ]; then + echo -n 'module: ' + udevadm info --query=name --path=${evdev%%/dev} + fi + done + done +} + +keyboard_devices diff --git a/src/udev/keymap/force-release-maps/common-volume-keys b/src/udev/keymap/force-release-maps/common-volume-keys new file mode 100644 index 0000000000..3a7654d735 --- /dev/null +++ b/src/udev/keymap/force-release-maps/common-volume-keys @@ -0,0 +1,3 @@ +0xa0 #mute +0xae #volume down +0xb0 #volume up diff --git a/src/udev/keymap/force-release-maps/dell-touchpad b/src/udev/keymap/force-release-maps/dell-touchpad new file mode 100644 index 0000000000..18e9bdee66 --- /dev/null +++ b/src/udev/keymap/force-release-maps/dell-touchpad @@ -0,0 +1 @@ +0x9E diff --git a/src/udev/keymap/force-release-maps/hp-other b/src/udev/keymap/force-release-maps/hp-other new file mode 100644 index 0000000000..6621370095 --- /dev/null +++ b/src/udev/keymap/force-release-maps/hp-other @@ -0,0 +1,3 @@ +# list of scancodes (hex or decimal), optional comment +0xd8 # Touchpad off +0xd9 # Touchpad on diff --git a/src/udev/keymap/force-release-maps/samsung-90x3a b/src/udev/keymap/force-release-maps/samsung-90x3a new file mode 100644 index 0000000000..65707effb7 --- /dev/null +++ b/src/udev/keymap/force-release-maps/samsung-90x3a @@ -0,0 +1,6 @@ +# list of scancodes (hex or decimal), optional comment +0xCE # Fn+F8 keyboard backlit up +0x8D # Fn+F7 keyboard backlit down +0x97 # Fn+F12 wifi on/off +0x96 # Fn+F1 performance mode (?) +0xD5 # Fn+F6 battery life extender diff --git a/src/udev/keymap/force-release-maps/samsung-other b/src/udev/keymap/force-release-maps/samsung-other new file mode 100644 index 0000000000..c51123a0b6 --- /dev/null +++ b/src/udev/keymap/force-release-maps/samsung-other @@ -0,0 +1,10 @@ +# list of scancodes (hex or decimal), optional comment +0x82 # Fn+F4 CRT/LCD +0x83 # Fn+F2 battery +0x84 # Fn+F5 backlight on/off +0x86 # Fn+F9 WLAN +0x88 # Fn-Up brightness up +0x89 # Fn-Down brightness down +0xB3 # Fn+F8 switch power mode (battery/dynamic/performance) +0xF7 # Fn+F10 Touchpad on +0xF9 # Fn+F10 Touchpad off diff --git a/src/udev/keymap/keyboard-force-release.sh.in b/src/udev/keymap/keyboard-force-release.sh.in new file mode 100755 index 0000000000..dd040cebc3 --- /dev/null +++ b/src/udev/keymap/keyboard-force-release.sh.in @@ -0,0 +1,22 @@ +#!@rootprefix@/bin/sh -e +# read list of scancodes, convert hex to decimal and +# append to the atkbd force_release sysfs attribute +# $1 sysfs devpath for serioX +# $2 file with scancode list (hex or dec) + +case "$2" in + /*) scf="$2" ;; + *) scf="@pkglibexecdir@/keymaps/force-release/$2" ;; +esac + +read attr <"/sys/$1/force_release" +while read scancode dummy; do + case "$scancode" in + \#*) ;; + *) + scancode=$(($scancode)) + attr="$attr${attr:+,}$scancode" + ;; + esac +done <"$scf" +echo "$attr" >"/sys/$1/force_release" diff --git a/src/udev/keymap/keymap.c b/src/udev/keymap/keymap.c new file mode 100644 index 0000000000..4c30ccf314 --- /dev/null +++ b/src/udev/keymap/keymap.c @@ -0,0 +1,447 @@ +/* + * keymap - dump keymap of an evdev device or set a new keymap from a file + * + * Based on keyfuzz by Lennart Poettering + * Adapted for udev-extras by Martin Pitt + * + * Copyright (C) 2006, Lennart Poettering + * Copyright (C) 2009, Canonical Ltd. + * + * keymap is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * keymap is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with keymap; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const struct key* lookup_key (const char *str, unsigned int len); + +#include "keys-from-name.h" +#include "keys-to-name.h" + +#define MAX_SCANCODES 1024 + +static int evdev_open(const char *dev) +{ + int fd; + char fn[PATH_MAX]; + + if (strncmp(dev, "/dev", 4) != 0) { + snprintf(fn, sizeof(fn), "/dev/%s", dev); + dev = fn; + } + + if ((fd = open(dev, O_RDWR)) < 0) { + fprintf(stderr, "error open('%s'): %m\n", dev); + return -1; + } + return fd; +} + +static int evdev_get_keycode(int fd, int scancode, int e) +{ + int codes[2]; + + codes[0] = scancode; + if (ioctl(fd, EVIOCGKEYCODE, codes) < 0) { + if (e && errno == EINVAL) { + return -2; + } else { + fprintf(stderr, "EVIOCGKEYCODE: %m\n"); + return -1; + } + } + return codes[1]; +} + +static int evdev_set_keycode(int fd, int scancode, int keycode) +{ + int codes[2]; + + codes[0] = scancode; + codes[1] = keycode; + + if (ioctl(fd, EVIOCSKEYCODE, codes) < 0) { + fprintf(stderr, "EVIOCSKEYCODE: %m\n"); + return -1; + } + return 0; +} + +static int evdev_driver_version(int fd, char *v, size_t l) +{ + int version; + + if (ioctl(fd, EVIOCGVERSION, &version)) { + fprintf(stderr, "EVIOCGVERSION: %m\n"); + return -1; + } + + snprintf(v, l, "%i.%i.%i.", version >> 16, (version >> 8) & 0xff, version & 0xff); + return 0; +} + +static int evdev_device_name(int fd, char *n, size_t l) +{ + if (ioctl(fd, EVIOCGNAME(l), n) < 0) { + fprintf(stderr, "EVIOCGNAME: %m\n"); + return -1; + } + return 0; +} + +/* Return a lower-case string with KEY_ prefix removed */ +static const char* format_keyname(const char* key) { + static char result[101]; + const char* s; + int len; + + for (s = key+4, len = 0; *s && len < 100; ++len, ++s) + result[len] = tolower(*s); + result[len] = '\0'; + return result; +} + +static int dump_table(int fd) { + char version[256], name[256]; + int scancode, r = -1; + + if (evdev_driver_version(fd, version, sizeof(version)) < 0) + goto fail; + + if (evdev_device_name(fd, name, sizeof(name)) < 0) + goto fail; + + printf("### evdev %s, driver '%s'\n", version, name); + + r = 0; + for (scancode = 0; scancode < MAX_SCANCODES; scancode++) { + int keycode; + + if ((keycode = evdev_get_keycode(fd, scancode, 1)) < 0) { + if (keycode == -2) + continue; + r = -1; + break; + } + + if (keycode < KEY_MAX && key_names[keycode]) + printf("0x%03x %s\n", scancode, format_keyname(key_names[keycode])); + else + printf("0x%03x 0x%03x\n", scancode, keycode); + } +fail: + return r; +} + +static void set_key(int fd, const char* scancode_str, const char* keyname) +{ + unsigned scancode; + char *endptr; + char t[105] = "KEY_UNKNOWN"; + const struct key *k; + + scancode = (unsigned) strtol(scancode_str, &endptr, 0); + if (*endptr != '\0') { + fprintf(stderr, "ERROR: Invalid scancode\n"); + exit(1); + } + + snprintf(t, sizeof(t), "KEY_%s", keyname); + + if (!(k = lookup_key(t, strlen(t)))) { + fprintf(stderr, "ERROR: Unknown key name '%s'\n", keyname); + exit(1); + } + + if (evdev_set_keycode(fd, scancode, k->id) < 0) + fprintf(stderr, "setting scancode 0x%2X to key code %i failed\n", + scancode, k->id); + else + printf("setting scancode 0x%2X to key code %i\n", + scancode, k->id); +} + +static int merge_table(int fd, FILE *f) { + int r = 0; + int line = 0; + + while (!feof(f)) { + char s[256], *p; + int scancode, new_keycode, old_keycode; + + if (!fgets(s, sizeof(s), f)) + break; + + line++; + p = s+strspn(s, "\t "); + if (*p == '#' || *p == '\n') + continue; + + if (sscanf(p, "%i %i", &scancode, &new_keycode) != 2) { + char t[105] = "KEY_UNKNOWN"; + const struct key *k; + + if (sscanf(p, "%i %100s", &scancode, t+4) != 2) { + fprintf(stderr, "WARNING: Parse failure at line %i, ignoring.\n", line); + r = -1; + continue; + } + + if (!(k = lookup_key(t, strlen(t)))) { + fprintf(stderr, "WARNING: Unknown key '%s' at line %i, ignoring.\n", t, line); + r = -1; + continue; + } + + new_keycode = k->id; + } + + + if ((old_keycode = evdev_get_keycode(fd, scancode, 0)) < 0) { + r = -1; + goto fail; + } + + if (evdev_set_keycode(fd, scancode, new_keycode) < 0) { + r = -1; + goto fail; + } + + if (new_keycode != old_keycode) + fprintf(stderr, "Remapped scancode 0x%02x to 0x%02x (prior: 0x%02x)\n", + scancode, new_keycode, old_keycode); + } +fail: + fclose(f); + return r; +} + + +/* read one event; return 1 if valid */ +static int read_event(int fd, struct input_event* ev) +{ + int ret; + ret = read(fd, ev, sizeof(struct input_event)); + + if (ret < 0) { + perror("read"); + return 0; + } + if (ret != sizeof(struct input_event)) { + fprintf(stderr, "did not get enough data for event struct, aborting\n"); + return 0; + } + + return 1; +} + +static void print_key(uint32_t scancode, uint16_t keycode, int has_scan, int has_key) +{ + const char *keyname; + + /* ignore key release events */ + if (has_key == 1) + return; + + if (has_key == 0 && has_scan != 0) { + fprintf(stderr, "got scan code event 0x%02X without a key code event\n", + scancode); + return; + } + + if (has_scan != 0) + printf("scan code: 0x%02X ", scancode); + else + printf("(no scan code received) "); + + keyname = key_names[keycode]; + if (keyname != NULL) + printf("key code: %s\n", format_keyname(keyname)); + else + printf("key code: %03X\n", keycode); +} + +static void interactive(int fd) +{ + struct input_event ev; + uint32_t last_scan = 0; + uint16_t last_key = 0; + int has_scan; /* boolean */ + int has_key; /* 0: none, 1: release, 2: press */ + + /* grab input device */ + ioctl(fd, EVIOCGRAB, 1); + puts("Press ESC to finish, or Control-C if this device is not your primary keyboard"); + + has_scan = has_key = 0; + while (read_event(fd, &ev)) { + /* Drivers usually send the scan code first, then the key code, + * then a SYN. Some drivers (like thinkpad_acpi) send the key + * code first, and some drivers might not send SYN events, so + * keep a robust state machine which can deal with any of those + */ + + if (ev.type == EV_MSC && ev.code == MSC_SCAN) { + if (has_scan) { + fputs("driver did not send SYN event in between key events; previous event:\n", + stderr); + print_key(last_scan, last_key, has_scan, has_key); + has_key = 0; + } + + last_scan = ev.value; + has_scan = 1; + /*printf("--- got scan %u; has scan %i key %i\n", last_scan, has_scan, has_key); */ + } + else if (ev.type == EV_KEY) { + if (has_key) { + fputs("driver did not send SYN event in between key events; previous event:\n", + stderr); + print_key(last_scan, last_key, has_scan, has_key); + has_scan = 0; + } + + last_key = ev.code; + has_key = 1 + ev.value; + /*printf("--- got key %hu; has scan %i key %i\n", last_key, has_scan, has_key);*/ + + /* Stop on ESC */ + if (ev.code == KEY_ESC && ev.value == 0) + break; + } + else if (ev.type == EV_SYN) { + /*printf("--- got SYN; has scan %i key %i\n", has_scan, has_key);*/ + print_key(last_scan, last_key, has_scan, has_key); + + has_scan = has_key = 0; + } + + } + + /* release input device */ + ioctl(fd, EVIOCGRAB, 0); +} + +static void help(int error) +{ + const char* h = "Usage: keymap []\n" + " keymap scancode keyname [...]\n" + " keymap -i \n"; + if (error) { + fputs(h, stderr); + exit(2); + } else { + fputs(h, stdout); + exit(0); + } +} + +int main(int argc, char **argv) +{ + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "interactive", no_argument, NULL, 'i' }, + {} + }; + int fd = -1; + int opt_interactive = 0; + int i; + + while (1) { + int option; + + option = getopt_long(argc, argv, "hi", options, NULL); + if (option == -1) + break; + + switch (option) { + case 'h': + help(0); + + case 'i': + opt_interactive = 1; + break; + default: + return 1; + } + } + + if (argc < optind+1) + help (1); + + if ((fd = evdev_open(argv[optind])) < 0) + return 3; + + /* one argument (device): dump or interactive */ + if (argc == optind+1) { + if (opt_interactive) + interactive(fd); + else + dump_table(fd); + return 0; + } + + /* two arguments (device, mapfile): set map file */ + if (argc == optind+2) { + const char *filearg = argv[optind+1]; + if (strchr(filearg, '/')) { + /* Keymap file argument is a path */ + FILE *f = fopen(filearg, "r"); + if (f) + merge_table(fd, f); + else + perror(filearg); + } else { + /* Keymap file argument is a filename */ + /* Open override file if present, otherwise default file */ + char keymap_path[PATH_MAX]; + snprintf(keymap_path, sizeof(keymap_path), "%s%s", SYSCONFDIR "/udev/keymaps/", filearg); + FILE *f = fopen(keymap_path, "r"); + if (f) { + merge_table(fd, f); + } else { + snprintf(keymap_path, sizeof(keymap_path), "%s%s", UDEVLIBEXECDIR "/keymaps/", filearg); + f = fopen(keymap_path, "r"); + if (f) + merge_table(fd, f); + else + perror(keymap_path); + } + } + return 0; + } + + /* more arguments (device, scancode/keyname pairs): set keys directly */ + if ((argc - optind - 1) % 2 == 0) { + for (i = optind+1; i < argc; i += 2) + set_key(fd, argv[i], argv[i+1]); + return 0; + } + + /* invalid number of arguments */ + help(1); + return 1; /* not reached */ +} diff --git a/src/udev/keymap/keymaps/acer b/src/udev/keymap/keymaps/acer new file mode 100644 index 0000000000..4e7c297dea --- /dev/null +++ b/src/udev/keymap/keymaps/acer @@ -0,0 +1,22 @@ +0xA5 help # Fn+F1 +0xA6 setup # Fn+F2 Acer eSettings +0xA7 battery # Fn+F3 Power Management +0xA9 switchvideomode # Fn+F5 +0xB3 euro +0xB4 dollar +0xCE brightnessup # Fn+Right +0xD4 bluetooth # (toggle) off-to-on +0xD5 wlan # (toggle) on-to-off +0xD6 wlan # (toggle) off-to-on +0xD7 bluetooth # (toggle) on-to-off +0xD8 bluetooth # (toggle) off-to-on +0xD9 brightnessup # Fn+Right +0xEE brightnessup # Fn+Right +0xEF brightnessdown # Fn+Left +0xF1 f22 # Fn+F7 Touchpad toggle (off-to-on) +0xF2 f23 # Fn+F7 Touchpad toggle (on-to-off) +0xF3 prog2 # "P2" programmable button +0xF4 prog1 # "P1" programmable button +0xF5 presentation +0xF8 fn +0xF9 f23 # Launch NTI shadow diff --git a/src/udev/keymap/keymaps/acer-aspire_5720 b/src/udev/keymap/keymaps/acer-aspire_5720 new file mode 100644 index 0000000000..1496d63a52 --- /dev/null +++ b/src/udev/keymap/keymaps/acer-aspire_5720 @@ -0,0 +1,4 @@ +0x84 bluetooth # sent when bluetooth module missing, and key pressed +0x92 media # acer arcade +0xD4 bluetooth # bluetooth on +0xD9 bluetooth # bluetooth off diff --git a/src/udev/keymap/keymaps/acer-aspire_5920g b/src/udev/keymap/keymaps/acer-aspire_5920g new file mode 100644 index 0000000000..633c4e854c --- /dev/null +++ b/src/udev/keymap/keymaps/acer-aspire_5920g @@ -0,0 +1,5 @@ +0x8A media +0x92 media +0xA6 setup +0xB2 www +0xD9 bluetooth # (toggle) on-to-off diff --git a/src/udev/keymap/keymaps/acer-aspire_6920 b/src/udev/keymap/keymaps/acer-aspire_6920 new file mode 100644 index 0000000000..699c954b4e --- /dev/null +++ b/src/udev/keymap/keymaps/acer-aspire_6920 @@ -0,0 +1,5 @@ +0xD9 bluetooth # (toggle) on-to-off +0x92 media +0x9E back +0x83 rewind +0x89 fastforward diff --git a/src/udev/keymap/keymaps/acer-aspire_8930 b/src/udev/keymap/keymaps/acer-aspire_8930 new file mode 100644 index 0000000000..fb27bfb4f5 --- /dev/null +++ b/src/udev/keymap/keymaps/acer-aspire_8930 @@ -0,0 +1,5 @@ +0xCA prog3 # key 'HOLD' on cine dash media console +0x83 rewind +0x89 fastforward +0x92 media # key 'ARCADE' on cine dash media console +0x9E back diff --git a/src/udev/keymap/keymaps/acer-travelmate_c300 b/src/udev/keymap/keymaps/acer-travelmate_c300 new file mode 100644 index 0000000000..bfef4cf868 --- /dev/null +++ b/src/udev/keymap/keymaps/acer-travelmate_c300 @@ -0,0 +1,5 @@ +0x67 f24 # FIXME: rotate screen +0x68 up +0x69 down +0x6B fn +0x6C screenlock # FIXME: lock tablet device/buttons diff --git a/src/udev/keymap/keymaps/asus b/src/udev/keymap/keymaps/asus new file mode 100644 index 0000000000..2a5995f982 --- /dev/null +++ b/src/udev/keymap/keymaps/asus @@ -0,0 +1,3 @@ +0xED volumeup +0xEE volumedown +0xEF mute diff --git a/src/udev/keymap/keymaps/compaq-e_evo b/src/udev/keymap/keymaps/compaq-e_evo new file mode 100644 index 0000000000..5fbc573aa4 --- /dev/null +++ b/src/udev/keymap/keymaps/compaq-e_evo @@ -0,0 +1,4 @@ +0xA3 www # I key +0x9A search +0x9E email +0x9F homepage diff --git a/src/udev/keymap/keymaps/dell b/src/udev/keymap/keymaps/dell new file mode 100644 index 0000000000..4f907b3eef --- /dev/null +++ b/src/udev/keymap/keymaps/dell @@ -0,0 +1,29 @@ +0x81 playpause # Play/Pause +0x82 stopcd # Stop +0x83 previoussong # Previous song +0x84 nextsong # Next song +0x85 brightnessdown # Fn+Down arrow Brightness Down +0x86 brightnessup # Fn+Up arrow Brightness Up +0x87 battery # Fn+F3 battery icon +0x88 unknown # Fn+F2 Turn On/Off Wireless - handled in hardware +0x89 ejectclosecd # Fn+F10 Eject CD +0x8A suspend # Fn+F1 hibernate +0x8B switchvideomode # Fn+F8 CRT/LCD (high keycode: "displaytoggle") +0x8C f23 # Fn+Right arrow Auto Brightness +0x8F switchvideomode # Fn+F7 aspect ratio +0x90 previoussong # Front panel previous song +0x91 prog1 # Wifi Catcher (DELL Specific) +0x92 media # MediaDirect button (house icon) +0x93 f23 # FIXME Fn+Left arrow Auto Brightness +0x95 camera # Shutter button Takes a picture if optional camera available +0x97 email # Tablet email button +0x98 f21 # FIXME: Tablet screen rotatation +0x99 nextsong # Front panel next song +0x9A setup # Tablet tools button +0x9B switchvideomode # Display Toggle button +0x9E f21 #touchpad toggle +0xA2 playpause # Front panel play/pause +0xA4 stopcd # Front panel stop +0xED media # MediaDirect button +0xD8 screenlock # FIXME: Tablet lock button +0xD9 f21 # touchpad toggle diff --git a/src/udev/keymap/keymaps/dell-latitude-xt2 b/src/udev/keymap/keymaps/dell-latitude-xt2 new file mode 100644 index 0000000000..39872f559d --- /dev/null +++ b/src/udev/keymap/keymaps/dell-latitude-xt2 @@ -0,0 +1,4 @@ +0x9B up # tablet rocker up +0x9E enter # tablet rocker press +0x9F back # tablet back +0xA3 down # tablet rocker down diff --git a/src/udev/keymap/keymaps/everex-xt5000 b/src/udev/keymap/keymaps/everex-xt5000 new file mode 100644 index 0000000000..4823a832f5 --- /dev/null +++ b/src/udev/keymap/keymaps/everex-xt5000 @@ -0,0 +1,7 @@ +0x5C media +0x65 f21 # Fn+F5 Touchpad toggle +0x67 prog3 # Fan Speed Control button +0x6F brightnessup +0x7F brightnessdown +0xB2 www +0xEC mail diff --git a/src/udev/keymap/keymaps/fujitsu-amilo_li_2732 b/src/udev/keymap/keymaps/fujitsu-amilo_li_2732 new file mode 100644 index 0000000000..9b8b36a170 --- /dev/null +++ b/src/udev/keymap/keymaps/fujitsu-amilo_li_2732 @@ -0,0 +1,3 @@ +0xD9 brightnessdown # Fn+F8 brightness down +0xEF brightnessup # Fn+F9 brightness up +0xA9 switchvideomode # Fn+F10 Cycle between available video outputs diff --git a/src/udev/keymap/keymaps/fujitsu-amilo_pa_2548 b/src/udev/keymap/keymaps/fujitsu-amilo_pa_2548 new file mode 100644 index 0000000000..f7b0c52444 --- /dev/null +++ b/src/udev/keymap/keymaps/fujitsu-amilo_pa_2548 @@ -0,0 +1,3 @@ +0xE0 volumedown +0xE1 volumeup +0xE5 prog1 diff --git a/src/udev/keymap/keymaps/fujitsu-amilo_pro_edition_v3505 b/src/udev/keymap/keymaps/fujitsu-amilo_pro_edition_v3505 new file mode 100644 index 0000000000..d2e38cbb23 --- /dev/null +++ b/src/udev/keymap/keymaps/fujitsu-amilo_pro_edition_v3505 @@ -0,0 +1,4 @@ +0xA5 help # Fn-F1 +0xA9 switchvideomode # Fn-F3 +0xD9 brightnessdown # Fn-F8 +0xE0 brightnessup # Fn-F9 diff --git a/src/udev/keymap/keymaps/fujitsu-amilo_pro_v3205 b/src/udev/keymap/keymaps/fujitsu-amilo_pro_v3205 new file mode 100644 index 0000000000..43e3199d59 --- /dev/null +++ b/src/udev/keymap/keymaps/fujitsu-amilo_pro_v3205 @@ -0,0 +1,2 @@ +0xF4 f21 # FIXME: silent-mode decrease CPU/GPU clock +0xF7 switchvideomode # Fn+F3 diff --git a/src/udev/keymap/keymaps/fujitsu-amilo_si_1520 b/src/udev/keymap/keymaps/fujitsu-amilo_si_1520 new file mode 100644 index 0000000000..1419bd9b5e --- /dev/null +++ b/src/udev/keymap/keymaps/fujitsu-amilo_si_1520 @@ -0,0 +1,6 @@ +0xE1 wlan +0xF3 wlan +0xEE brightnessdown +0xE0 brightnessup +0xE2 bluetooth +0xF7 video diff --git a/src/udev/keymap/keymaps/fujitsu-esprimo_mobile_v5 b/src/udev/keymap/keymaps/fujitsu-esprimo_mobile_v5 new file mode 100644 index 0000000000..d3d056b366 --- /dev/null +++ b/src/udev/keymap/keymaps/fujitsu-esprimo_mobile_v5 @@ -0,0 +1,4 @@ +0xA9 switchvideomode +0xD9 brightnessdown +0xDF sleep +0xEF brightnessup diff --git a/src/udev/keymap/keymaps/fujitsu-esprimo_mobile_v6 b/src/udev/keymap/keymaps/fujitsu-esprimo_mobile_v6 new file mode 100644 index 0000000000..52c70c50cb --- /dev/null +++ b/src/udev/keymap/keymaps/fujitsu-esprimo_mobile_v6 @@ -0,0 +1,2 @@ +0xCE brightnessup +0xEF brightnessdown diff --git a/src/udev/keymap/keymaps/genius-slimstar-320 b/src/udev/keymap/keymaps/genius-slimstar-320 new file mode 100644 index 0000000000..d0a3656dd8 --- /dev/null +++ b/src/udev/keymap/keymaps/genius-slimstar-320 @@ -0,0 +1,35 @@ +# Genius SlimStar 320 +# +# Only buttons which are not properly mapped yet are configured below + +# "Scroll wheel", a circular up/down/left/right button. Aimed for scolling, +# but since there are no scrollleft/scrollright, let's map to back/forward. +0x900f0 scrollup +0x900f1 scrolldown +0x900f3 back +0x900f2 forward + +# Multimedia buttons, left side (from left to right) +# [W] +0x900f5 wordprocessor +# [Ex] +0x900f6 spreadsheet +# [P] +0x900f4 presentation +# Other five (calculator, playpause, stop, mute and eject) are OK + +# Right side, from left to right +# [e] +0xc0223 www +# "man" +0x900f7 chat +# "Y" +0x900fb prog1 +# [X] +0x900f8 close +# "picture" +0x900f9 graphicseditor +# "two windows" +0x900fd scale +# "lock" +0x900fc screenlock diff --git a/src/udev/keymap/keymaps/hewlett-packard b/src/udev/keymap/keymaps/hewlett-packard new file mode 100644 index 0000000000..4461fa2ce5 --- /dev/null +++ b/src/udev/keymap/keymaps/hewlett-packard @@ -0,0 +1,12 @@ +0x81 fn_esc +0x89 battery # FnF8 +0x8A screenlock # FnF6 +0x8B camera +0x8C media # music +0x8E dvd +0xB1 help +0xB3 f23 # FIXME: Auto brightness +0xD7 wlan +0x92 brightnessdown # FnF7 (FnF9 on 6730b) +0x97 brightnessup # FnF8 (FnF10 on 6730b) +0xEE switchvideomode # FnF4 diff --git a/src/udev/keymap/keymaps/hewlett-packard-2510p_2530p b/src/udev/keymap/keymaps/hewlett-packard-2510p_2530p new file mode 100644 index 0000000000..41ad2e9b5a --- /dev/null +++ b/src/udev/keymap/keymaps/hewlett-packard-2510p_2530p @@ -0,0 +1,2 @@ +0xD8 f23 # touchpad off +0xD9 f22 # touchpad on diff --git a/src/udev/keymap/keymaps/hewlett-packard-compaq_elitebook b/src/udev/keymap/keymaps/hewlett-packard-compaq_elitebook new file mode 100644 index 0000000000..42007c5483 --- /dev/null +++ b/src/udev/keymap/keymaps/hewlett-packard-compaq_elitebook @@ -0,0 +1,2 @@ +0x88 presentation +0xD9 help # I key (high keycode: "info") diff --git a/src/udev/keymap/keymaps/hewlett-packard-pavilion b/src/udev/keymap/keymaps/hewlett-packard-pavilion new file mode 100644 index 0000000000..3d3cefc8e6 --- /dev/null +++ b/src/udev/keymap/keymaps/hewlett-packard-pavilion @@ -0,0 +1,3 @@ +0x88 media # FIXME: quick play +0xD8 f23 # touchpad off +0xD9 f22 # touchpad on diff --git a/src/udev/keymap/keymaps/hewlett-packard-presario-2100 b/src/udev/keymap/keymaps/hewlett-packard-presario-2100 new file mode 100644 index 0000000000..1df39dcbd2 --- /dev/null +++ b/src/udev/keymap/keymaps/hewlett-packard-presario-2100 @@ -0,0 +1,3 @@ +0xF0 help +0xF1 screenlock +0xF3 search diff --git a/src/udev/keymap/keymaps/hewlett-packard-tablet b/src/udev/keymap/keymaps/hewlett-packard-tablet new file mode 100644 index 0000000000..d19005ab90 --- /dev/null +++ b/src/udev/keymap/keymaps/hewlett-packard-tablet @@ -0,0 +1,6 @@ +0x82 prog2 # Funny Key +0x83 prog1 # Q +0x84 tab +0x85 esc +0x86 pageup +0x87 pagedown diff --git a/src/udev/keymap/keymaps/hewlett-packard-tx2 b/src/udev/keymap/keymaps/hewlett-packard-tx2 new file mode 100644 index 0000000000..36a690fcf6 --- /dev/null +++ b/src/udev/keymap/keymaps/hewlett-packard-tx2 @@ -0,0 +1,3 @@ +0xC2 media +0xD8 f23 # Toggle touchpad button on tx2 (OFF) +0xD9 f22 # Toggle touchpad button on tx2 (ON) diff --git a/src/udev/keymap/keymaps/ibm-thinkpad-usb-keyboard-trackpoint b/src/udev/keymap/keymaps/ibm-thinkpad-usb-keyboard-trackpoint new file mode 100644 index 0000000000..027e50bf88 --- /dev/null +++ b/src/udev/keymap/keymaps/ibm-thinkpad-usb-keyboard-trackpoint @@ -0,0 +1,7 @@ +0x900f0 screenlock +0x900f1 wlan +0x900f2 switchvideomode +0x900f3 suspend +0x900f4 brightnessup +0x900f5 brightnessdown +0x900f8 zoom diff --git a/src/udev/keymap/keymaps/inventec-symphony_6.0_7.0 b/src/udev/keymap/keymaps/inventec-symphony_6.0_7.0 new file mode 100644 index 0000000000..4a8b4ba5a7 --- /dev/null +++ b/src/udev/keymap/keymaps/inventec-symphony_6.0_7.0 @@ -0,0 +1,2 @@ +0xF3 prog2 +0xF4 prog1 diff --git a/src/udev/keymap/keymaps/lenovo-3000 b/src/udev/keymap/keymaps/lenovo-3000 new file mode 100644 index 0000000000..5bd165654a --- /dev/null +++ b/src/udev/keymap/keymaps/lenovo-3000 @@ -0,0 +1,5 @@ +0x8B switchvideomode # Fn+F7 video +0x96 wlan # Fn+F5 wireless +0x97 sleep # Fn+F4 suspend +0x98 suspend # Fn+F12 hibernate +0xB4 prog1 # Lenovo Care diff --git a/src/udev/keymap/keymaps/lenovo-ideapad b/src/udev/keymap/keymaps/lenovo-ideapad new file mode 100644 index 0000000000..fc339839f2 --- /dev/null +++ b/src/udev/keymap/keymaps/lenovo-ideapad @@ -0,0 +1,8 @@ +# Key codes observed on S10-3, assumed valid on other IdeaPad models +0x81 rfkill # does nothing in BIOS +0x83 display_off # BIOS toggles screen state +0xB9 brightnessup # does nothing in BIOS +0xBA brightnessdown # does nothing in BIOS +0xF1 camera # BIOS toggles camera power +0xf2 f21 # touchpad toggle (key alternately emits f2 and f3) +0xf3 f21 diff --git a/src/udev/keymap/keymaps/lenovo-thinkpad-usb-keyboard-trackpoint b/src/udev/keymap/keymaps/lenovo-thinkpad-usb-keyboard-trackpoint new file mode 100644 index 0000000000..47e8846a68 --- /dev/null +++ b/src/udev/keymap/keymaps/lenovo-thinkpad-usb-keyboard-trackpoint @@ -0,0 +1,13 @@ +0x90012 screenlock # Fn+F2 +0x90013 battery # Fn+F3 +0x90014 wlan # Fn+F5 +0x90016 switchvideomode # Fn+F7 +0x90017 f21 # Fn+F8 touchpadtoggle +0x90019 suspend # Fn+F12 +0x9001A brightnessup # Fn+Home +0x9001B brightnessdown # Fn+End +0x9001D zoom # Fn+Space +0x90011 prog1 # Thinkvantage button + +0x90015 camera # Fn+F6 headset/camera VoIP key ?? +0x90010 micmute # Microphone mute button diff --git a/src/udev/keymap/keymaps/lenovo-thinkpad_x200_tablet b/src/udev/keymap/keymaps/lenovo-thinkpad_x200_tablet new file mode 100644 index 0000000000..31ea3b2c70 --- /dev/null +++ b/src/udev/keymap/keymaps/lenovo-thinkpad_x200_tablet @@ -0,0 +1,6 @@ +0x5D menu +0x63 fn +0x66 screenlock +0x67 cyclewindows # bezel circular arrow +0x68 setup # bezel setup / menu +0x6c direction # rotate screen diff --git a/src/udev/keymap/keymaps/lenovo-thinkpad_x6_tablet b/src/udev/keymap/keymaps/lenovo-thinkpad_x6_tablet new file mode 100644 index 0000000000..6fd16b5662 --- /dev/null +++ b/src/udev/keymap/keymaps/lenovo-thinkpad_x6_tablet @@ -0,0 +1,8 @@ +0x6C f21 # rotate +0x68 screenlock # screenlock +0x6B esc # escape +0x6D right # right on d-pad +0x6E left # left on d-pad +0x71 up # up on d-pad +0x6F down # down on d-pad +0x69 enter # enter on d-pad diff --git a/src/udev/keymap/keymaps/lg-x110 b/src/udev/keymap/keymaps/lg-x110 new file mode 100644 index 0000000000..ba08cba3fe --- /dev/null +++ b/src/udev/keymap/keymaps/lg-x110 @@ -0,0 +1,12 @@ +0xA0 mute # Fn-F9 +0xAE volumedown # Fn-Left +0xAF search # Fn-F3 +0xB0 volumeup # Fn-Right +0xB1 battery # Fn-F10 Info +0xB3 suspend # Fn-F12 +0xDF sleep # Fn-F4 +# 0xE2 bluetooth # satellite dish2 +0xE4 f21 # Fn-F5 Touchpad disable +0xF6 wlan # Fn-F6 +0xF7 reserved # brightnessdown # Fn-Down +0xF8 reserved # brightnessup # Fn-Up diff --git a/src/udev/keymap/keymaps/logitech-wave b/src/udev/keymap/keymaps/logitech-wave new file mode 100644 index 0000000000..caa5d5d310 --- /dev/null +++ b/src/udev/keymap/keymaps/logitech-wave @@ -0,0 +1,16 @@ +0x9001C scale #expo +0x9001F zoomout #zoom out +0x90020 zoomin #zoom in +0x9003D prog1 #gadget +0x90005 camera #camera +0x90018 media #media center +0x90041 wordprocessor #fn+f1 (word) +0x90042 spreadsheet #fn+f2 (excel) +0x90043 calendar #fn+f3 (calendar) +0x90044 prog2 #fn+f4 (program a) +0x90045 prog3 #fn+f5 (program b) +0x90046 prog4 #fn+f6 (program c) +0x90048 messenger #fn+f8 (msn messenger) +0x9002D find #fn+f10 (search www) +0x9004B search #fn+f11 (search pc) +0x9004C ejectclosecd #fn+f12 (eject) diff --git a/src/udev/keymap/keymaps/logitech-wave-cordless b/src/udev/keymap/keymaps/logitech-wave-cordless new file mode 100644 index 0000000000..a10dad5e4d --- /dev/null +++ b/src/udev/keymap/keymaps/logitech-wave-cordless @@ -0,0 +1,15 @@ +0xD4 zoomin +0xCC zoomout +0xC0183 media +0xC1005 camera +0xC101F zoomout +0xC1020 zoomin +0xC1041 wordprocessor +0xC1042 spreadsheet +0xC1043 calendar +0xC1044 prog2 #fn+f4 (program a) +0xC1045 prog3 #fn+f5 (program b) +0xC1046 prog4 #fn+f6 (program c) +0xC1048 messenger +0xC104A find #fn+f10 (search www) +0xC104C ejectclosecd diff --git a/src/udev/keymap/keymaps/logitech-wave-pro-cordless b/src/udev/keymap/keymaps/logitech-wave-pro-cordless new file mode 100644 index 0000000000..e7aa02206c --- /dev/null +++ b/src/udev/keymap/keymaps/logitech-wave-pro-cordless @@ -0,0 +1,12 @@ +0xC01B6 camera +0xC0183 media +0xC0184 wordprocessor +0xC0186 spreadsheet +0xC018E calendar +0xC0223 homepage +0xC01BC messenger +0xC018A mail +0xC0221 search +0xC00B8 ejectcd +0xC022D zoomin +0xC022E zoomout diff --git a/src/udev/keymap/keymaps/maxdata-pro_7000 b/src/udev/keymap/keymaps/maxdata-pro_7000 new file mode 100644 index 0000000000..c0e4f77af4 --- /dev/null +++ b/src/udev/keymap/keymaps/maxdata-pro_7000 @@ -0,0 +1,9 @@ +0x97 prog2 +0x9F prog1 +0xA0 mute # Fn-F5 +0x82 www +0xEC email +0xAE volumedown # Fn-Down +0xB0 volumeup # Fn-Up +0xDF suspend # Fn+F2 +0xF5 help diff --git a/src/udev/keymap/keymaps/medion-fid2060 b/src/udev/keymap/keymaps/medion-fid2060 new file mode 100644 index 0000000000..5a76c76799 --- /dev/null +++ b/src/udev/keymap/keymaps/medion-fid2060 @@ -0,0 +1,2 @@ +0x6B channeldown # Thottle Down +0x6D channelup # Thottle Up diff --git a/src/udev/keymap/keymaps/medionnb-a555 b/src/udev/keymap/keymaps/medionnb-a555 new file mode 100644 index 0000000000..c3b5dfa60b --- /dev/null +++ b/src/udev/keymap/keymaps/medionnb-a555 @@ -0,0 +1,4 @@ +0x63 www # N button +0x66 prog1 # link 1 button +0x67 email # envelope button +0x69 prog2 # link 2 button diff --git a/src/udev/keymap/keymaps/micro-star b/src/udev/keymap/keymaps/micro-star new file mode 100644 index 0000000000..4a438698ed --- /dev/null +++ b/src/udev/keymap/keymaps/micro-star @@ -0,0 +1,13 @@ +0xA0 mute # Fn-F9 +0xAE volumedown # Fn-F7 +0xB0 volumeup # Fn-F8 +0xB2 www # e button +0xDF sleep # Fn-F12 +0xE2 bluetooth # satellite dish2 +0xE4 f21 # Fn-F3 Touchpad disable +0xEC email # envelope button +0xEE camera # Fn-F6 camera disable +0xF6 wlan # satellite dish1 +0xF7 brightnessdown # Fn-F4 +0xF8 brightnessup # Fn-F5 +0xF9 search diff --git a/src/udev/keymap/keymaps/module-asus-w3j b/src/udev/keymap/keymaps/module-asus-w3j new file mode 100644 index 0000000000..773e0b3e82 --- /dev/null +++ b/src/udev/keymap/keymaps/module-asus-w3j @@ -0,0 +1,11 @@ +0x41 nextsong +0x45 playpause +0x43 stopcd +0x40 previoussong +0x4C ejectclosecd +0x32 mute +0x31 volumedown +0x30 volumeup +0x5D wlan +0x7E bluetooth +0x8A media # high keycode: "tv" diff --git a/src/udev/keymap/keymaps/module-ibm b/src/udev/keymap/keymaps/module-ibm new file mode 100644 index 0000000000..a92dfa2506 --- /dev/null +++ b/src/udev/keymap/keymaps/module-ibm @@ -0,0 +1,16 @@ +0x01 battery # Fn+F2 +0x02 screenlock # Fn+F3 +0x03 sleep # Fn+F4 +0x04 wlan # Fn+F5 +0x06 switchvideomode # Fn+F7 +0x07 zoom # Fn+F8 screen expand +0x08 f24 # Fn+F9 undock +0x0B suspend # Fn+F12 +0x0F brightnessup # Fn+Home +0x10 brightnessdown # Fn+End +0x11 kbdillumtoggle # Fn+PgUp - ThinkLight +0x13 zoom # Fn+Space +0x14 volumeup +0x15 volumedown +0x16 mute +0x17 prog1 # ThinkPad/ThinkVantage button (high keycode: "vendor") diff --git a/src/udev/keymap/keymaps/module-lenovo b/src/udev/keymap/keymaps/module-lenovo new file mode 100644 index 0000000000..8e38883091 --- /dev/null +++ b/src/udev/keymap/keymaps/module-lenovo @@ -0,0 +1,17 @@ +0x1 screenlock # Fn+F2 +0x2 battery # Fn+F3 +0x3 sleep # Fn+F4 +0x4 wlan # Fn+F5 +0x6 switchvideomode # Fn+F7 +0x7 f21 # Fn+F8 touchpadtoggle +0x8 f24 # Fn+F9 undock +0xB suspend # Fn+F12 +0xF brightnessup # Fn+Home +0x10 brightnessdown # Fn+End +0x11 kbdillumtoggle # Fn+PgUp - ThinkLight +0x13 zoom # Fn+Space +0x14 volumeup +0x15 volumedown +0x16 mute +0x17 prog1 # ThinkPad/ThinkVantage button (high keycode: "vendor") +0x1A micmute # Microphone mute diff --git a/src/udev/keymap/keymaps/module-sony b/src/udev/keymap/keymaps/module-sony new file mode 100644 index 0000000000..7c000131d1 --- /dev/null +++ b/src/udev/keymap/keymaps/module-sony @@ -0,0 +1,8 @@ +0x06 mute # Fn+F2 +0x07 volumedown # Fn+F3 +0x08 volumeup # Fn+F4 +0x09 brightnessdown # Fn+F5 +0x0A brightnessup # Fn+F6 +0x0B switchvideomode # Fn+F7 +0x0E zoom # Fn+F10 +0x10 suspend # Fn+F12 diff --git a/src/udev/keymap/keymaps/module-sony-old b/src/udev/keymap/keymaps/module-sony-old new file mode 100644 index 0000000000..596a34258a --- /dev/null +++ b/src/udev/keymap/keymaps/module-sony-old @@ -0,0 +1,2 @@ +0x06 battery +0x07 mute diff --git a/src/udev/keymap/keymaps/module-sony-vgn b/src/udev/keymap/keymaps/module-sony-vgn new file mode 100644 index 0000000000..c8ba001516 --- /dev/null +++ b/src/udev/keymap/keymaps/module-sony-vgn @@ -0,0 +1,8 @@ +0x00 brightnessdown # Fn+F5 +0x10 brightnessup # Fn+F6 +0x11 switchvideomode # Fn+F7 +0x12 zoomout +0x14 zoomin +0x15 suspend # Fn+F12 +0x17 prog1 +0x20 media diff --git a/src/udev/keymap/keymaps/olpc-xo b/src/udev/keymap/keymaps/olpc-xo new file mode 100644 index 0000000000..34434a121d --- /dev/null +++ b/src/udev/keymap/keymaps/olpc-xo @@ -0,0 +1,74 @@ +0x59 fn +0x81 fn_esc +0xF9 camera +0xF8 sound # Fn-CAMERA = Mic + + +# Function key mappings, as per +# http://dev.laptop.org/ticket/10213#comment:20 +# +# Unmodified F1-F8 produce F1-F8, so no remap necessary. +# Unmodified F9-F12 control brightness and volume. +0x43 brightnessdown +0x44 brightnessup +0x57 volumedown +0x58 volumeup + +# fn-modified fkeys all produce the unmodified version of the key. +0xBB f1 +0xBC f2 +0xBD f3 +0xBE f4 +0xBF f5 +0xC0 f6 +0xC1 f7 +0xC2 f8 +0xC3 f9 +0xC4 f10 +0xD7 f11 +0xD8 f12 + + +# Using F13-F21 for the .5 F keys right now. +0xF7 f13 +0xF6 f14 +0xF5 f15 +0xF4 f16 +0xF3 f17 +0xF2 f18 +0xF1 f19 +0xF0 f20 +0xEF f21 + +0xEE chat +0xE4 chat # Just mapping Fn-Chat to Chat for now +0xDD menu # Frame +0xDA prog1 # Fn-Frame + +# The FN of some keys is other keys +0xD3 delete +0xD2 insert +0xC9 pageup +0xD1 pagedown +0xC7 home +0xCF end + +# Language key - don't ask what they are doing as KEY_HP +0x73 hp +0x7E hp + +0xDB leftmeta # left grab +0xDC rightmeta # right grab +0x85 rightmeta # Right grab releases on a different scancode +0xD6 kbdillumtoggle # Fn-space +0x69 switchvideomode # Brightness key + +# Game keys +0x65 kp8 # up +0x66 kp2 # down +0x67 kp4 # left +0x68 kp6 # right +0xE5 kp9 # pgup +0xE6 kp3 # pgdn +0xE7 kp7 # home +0xE8 kp1 # end diff --git a/src/udev/keymap/keymaps/onkyo b/src/udev/keymap/keymaps/onkyo new file mode 100644 index 0000000000..ee864ade4d --- /dev/null +++ b/src/udev/keymap/keymaps/onkyo @@ -0,0 +1,14 @@ +0xA0 mute # Fn+D +0xAE volumedown # Fn+F +0xB0 volumeup # Fn+G +0xDF sleep # Fn+W +0xE0 bluetooth # Fn+H +0xE2 cyclewindows # Fn+Esc +0xEE battery # Fn+Q +0xF0 media # Fn+R +0xF5 switchvideomode # Fn+E +0xF6 camera # Fn+T +0xF7 f21 # Fn+Y (touchpad toggle) +0xF8 brightnessup # Fn+S +0xF9 brightnessdown # Fn+A +0xFB wlan # Fn+J diff --git a/src/udev/keymap/keymaps/oqo-model2 b/src/udev/keymap/keymaps/oqo-model2 new file mode 100644 index 0000000000..b7f4851abe --- /dev/null +++ b/src/udev/keymap/keymaps/oqo-model2 @@ -0,0 +1,5 @@ +0x8E wlan +0xF0 switchvideomode +0xF1 mute +0xF2 volumedown +0xF3 volumeup diff --git a/src/udev/keymap/keymaps/samsung-90x3a b/src/udev/keymap/keymaps/samsung-90x3a new file mode 100644 index 0000000000..8b65eb6d03 --- /dev/null +++ b/src/udev/keymap/keymaps/samsung-90x3a @@ -0,0 +1,5 @@ +0x96 kbdillumup         # Fn+F8 keyboard backlit up +0x97 kbdillumdown       # Fn+F7 keyboard backlit down +0xD5 wlan               # Fn+F12 wifi on/off +0xCE prog1              # Fn+F1 performance mode +0x8D prog2              # Fn+F6 battery life extender diff --git a/src/udev/keymap/keymaps/samsung-other b/src/udev/keymap/keymaps/samsung-other new file mode 100644 index 0000000000..3ac0c2f10c --- /dev/null +++ b/src/udev/keymap/keymaps/samsung-other @@ -0,0 +1,14 @@ +0x74 prog1 # User key +0x75 www +0x78 mail +0x82 switchvideomode # Fn+F4 CRT/LCD (high keycode: "displaytoggle") +0x83 battery # Fn+F2 +0x84 prog1 # Fn+F5 backlight on/off +0x86 wlan # Fn+F9 +0x88 brightnessup # Fn-Up +0x89 brightnessdown # Fn-Down +0xB1 prog2 # Fn+F7 run Samsung Magic Doctor (keypressed event is generated twice) +0xB3 prog3 # Fn+F8 switch power mode (battery/dynamic/performance) +0xB4 wlan # Fn+F9 (X60P) +0xF7 f22 # Fn+F10 Touchpad on +0xF9 f23 # Fn+F10 Touchpad off diff --git a/src/udev/keymap/keymaps/samsung-sq1us b/src/udev/keymap/keymaps/samsung-sq1us new file mode 100644 index 0000000000..ea2141ef84 --- /dev/null +++ b/src/udev/keymap/keymaps/samsung-sq1us @@ -0,0 +1,7 @@ +0xD4 menu +0xD8 f1 +0xD9 f10 +0xD6 f3 +0xD7 f9 +0xE4 f5 +0xEE f11 diff --git a/src/udev/keymap/keymaps/samsung-sx20s b/src/udev/keymap/keymaps/samsung-sx20s new file mode 100644 index 0000000000..9d954ee415 --- /dev/null +++ b/src/udev/keymap/keymaps/samsung-sx20s @@ -0,0 +1,4 @@ +0x74 mute +0x75 mute +0x77 f22 # Touchpad on +0x79 f23 # Touchpad off diff --git a/src/udev/keymap/keymaps/toshiba-satellite_a100 b/src/udev/keymap/keymaps/toshiba-satellite_a100 new file mode 100644 index 0000000000..22007be71b --- /dev/null +++ b/src/udev/keymap/keymaps/toshiba-satellite_a100 @@ -0,0 +1,2 @@ +0xA4 stopcd +0xB2 www diff --git a/src/udev/keymap/keymaps/toshiba-satellite_a110 b/src/udev/keymap/keymaps/toshiba-satellite_a110 new file mode 100644 index 0000000000..1429409351 --- /dev/null +++ b/src/udev/keymap/keymaps/toshiba-satellite_a110 @@ -0,0 +1,10 @@ +0x92 stop +0x93 www +0x94 media +0x9E f22 # Touchpad on +0x9F f23 # Touchpad off +0xB9 nextsong +0xD9 brightnessup +0xEE screenlock +0xF4 previoussong +0xF7 playpause diff --git a/src/udev/keymap/keymaps/toshiba-satellite_m30x b/src/udev/keymap/keymaps/toshiba-satellite_m30x new file mode 100644 index 0000000000..ae8e34941b --- /dev/null +++ b/src/udev/keymap/keymaps/toshiba-satellite_m30x @@ -0,0 +1,6 @@ +0xef brightnessdown +0xd9 brightnessup +0xee screenlock +0x93 media +0x9e f22 #touchpad_enable +0x9f f23 #touchpad_disable diff --git a/src/udev/keymap/keymaps/zepto-znote b/src/udev/keymap/keymaps/zepto-znote new file mode 100644 index 0000000000..cf72fda47b --- /dev/null +++ b/src/udev/keymap/keymaps/zepto-znote @@ -0,0 +1,11 @@ +0x93 switchvideomode # Fn+F3 Toggle Video Output +0x95 brightnessdown # Fn+F4 Brightness Down +0x91 brightnessup # Fn+F5 Brightness Up +0xA5 f23 # Fn+F6 Disable Touchpad +0xA6 f22 # Fn+F6 Enable Touchpad +0xA7 bluetooth # Fn+F10 Enable Bluetooth +0XA9 bluetooth # Fn+F10 Disable Bluetooth +0xF1 wlan # RF Switch Off +0xF2 wlan # RF Switch On +0xF4 prog1 # P1 Button +0xF3 prog2 # P2 Button diff --git a/src/udev/libudev-device-private.c b/src/udev/libudev-device-private.c new file mode 100644 index 0000000000..13fdb8eb57 --- /dev/null +++ b/src/udev/libudev-device-private.c @@ -0,0 +1,185 @@ +/* + * libudev - interface to udev device information + * + * Copyright (C) 2008-2010 Kay Sievers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libudev.h" +#include "libudev-private.h" + +static void udev_device_tag(struct udev_device *dev, const char *tag, bool add) +{ + const char *id; + struct udev *udev = udev_device_get_udev(dev); + char filename[UTIL_PATH_SIZE]; + + id = udev_device_get_id_filename(dev); + if (id == NULL) + return; + util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/tags/", tag, "/", id, NULL); + + if (add) { + int fd; + + util_create_path(udev, filename); + fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444); + if (fd >= 0) + close(fd); + } else { + unlink(filename); + } +} + +int udev_device_tag_index(struct udev_device *dev, struct udev_device *dev_old, bool add) +{ + struct udev_list_entry *list_entry; + bool found; + + if (add && dev_old != NULL) { + /* delete possible left-over tags */ + udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(dev_old)) { + const char *tag_old = udev_list_entry_get_name(list_entry); + struct udev_list_entry *list_entry_current; + + found = false; + udev_list_entry_foreach(list_entry_current, udev_device_get_tags_list_entry(dev)) { + const char *tag = udev_list_entry_get_name(list_entry_current); + + if (strcmp(tag, tag_old) == 0) { + found = true; + break; + } + } + if (!found) + udev_device_tag(dev_old, tag_old, false); + } + } + + udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(dev)) + udev_device_tag(dev, udev_list_entry_get_name(list_entry), add); + + return 0; +} + +static bool device_has_info(struct udev_device *udev_device) +{ + struct udev_list_entry *list_entry; + + if (udev_device_get_devlinks_list_entry(udev_device) != NULL) + return true; + if (udev_device_get_devlink_priority(udev_device) != 0) + return true; + udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev_device)) + if (udev_list_entry_get_num(list_entry)) + return true; + if (udev_device_get_tags_list_entry(udev_device) != NULL) + return true; + if (udev_device_get_watch_handle(udev_device) >= 0) + return true; + return false; +} + +int udev_device_update_db(struct udev_device *udev_device) +{ + bool has_info; + const char *id; + struct udev *udev = udev_device_get_udev(udev_device); + char filename[UTIL_PATH_SIZE]; + char filename_tmp[UTIL_PATH_SIZE]; + FILE *f; + + id = udev_device_get_id_filename(udev_device); + if (id == NULL) + return -1; + + has_info = device_has_info(udev_device); + util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/data/", id, NULL); + + /* do not store anything for otherwise empty devices */ + if (!has_info && + major(udev_device_get_devnum(udev_device)) == 0 && + udev_device_get_ifindex(udev_device) == 0) { + unlink(filename); + return 0; + } + + /* write a database file */ + util_strscpyl(filename_tmp, sizeof(filename_tmp), filename, ".tmp", NULL); + util_create_path(udev, filename_tmp); + f = fopen(filename_tmp, "we"); + if (f == NULL) { + err(udev, "unable to create temporary db file '%s': %m\n", filename_tmp); + return -1; + } + + /* + * set 'sticky' bit to indicate that we should not clean the + * database when we transition from initramfs to the real root + */ + if (udev_device_get_db_persist(udev_device)) + fchmod(fileno(f), 01644); + + if (has_info) { + struct udev_list_entry *list_entry; + + if (major(udev_device_get_devnum(udev_device)) > 0) { + size_t devlen = strlen(udev_get_dev_path(udev))+1; + + udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(udev_device)) + fprintf(f, "S:%s\n", &udev_list_entry_get_name(list_entry)[devlen]); + if (udev_device_get_devlink_priority(udev_device) != 0) + fprintf(f, "L:%i\n", udev_device_get_devlink_priority(udev_device)); + if (udev_device_get_watch_handle(udev_device) >= 0) + fprintf(f, "W:%i\n", udev_device_get_watch_handle(udev_device)); + } + + if (udev_device_get_usec_initialized(udev_device) > 0) + fprintf(f, "I:%llu\n", udev_device_get_usec_initialized(udev_device)); + + udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev_device)) { + if (!udev_list_entry_get_num(list_entry)) + continue; + fprintf(f, "E:%s=%s\n", + udev_list_entry_get_name(list_entry), + udev_list_entry_get_value(list_entry)); + } + + udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(udev_device)) + fprintf(f, "G:%s\n", udev_list_entry_get_name(list_entry)); + } + + fclose(f); + rename(filename_tmp, filename); + info(udev, "created %s file '%s' for '%s'\n", has_info ? "db" : "empty", + filename, udev_device_get_devpath(udev_device)); + return 0; +} + +int udev_device_delete_db(struct udev_device *udev_device) +{ + const char *id; + struct udev *udev = udev_device_get_udev(udev_device); + char filename[UTIL_PATH_SIZE]; + + id = udev_device_get_id_filename(udev_device); + if (id == NULL) + return -1; + util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/data/", id, NULL); + unlink(filename); + return 0; +} diff --git a/src/udev/libudev-device.c b/src/udev/libudev-device.c new file mode 100644 index 0000000000..10f28b8cd5 --- /dev/null +++ b/src/udev/libudev-device.c @@ -0,0 +1,1744 @@ +/* + * libudev - interface to udev device information + * + * Copyright (C) 2008-2010 Kay Sievers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libudev.h" +#include "libudev-private.h" + +/** + * SECTION:libudev-device + * @short_description: kernel sys devices + * + * Representation of kernel sys devices. Devices are uniquely identified + * by their syspath, every device has exactly one path in the kernel sys + * filesystem. Devices usually belong to a kernel subsystem, and and have + * a unique name inside that subsystem. + */ + +/** + * udev_device: + * + * Opaque object representing one kernel sys device. + */ +struct udev_device { + struct udev *udev; + struct udev_device *parent_device; + char *syspath; + const char *devpath; + char *sysname; + const char *sysnum; + char *devnode; + mode_t devnode_mode; + char *subsystem; + char *devtype; + char *driver; + char *action; + char *devpath_old; + char *id_filename; + char **envp; + char *monitor_buf; + size_t monitor_buf_len; + struct udev_list devlinks_list; + struct udev_list properties_list; + struct udev_list sysattr_value_list; + struct udev_list sysattr_list; + struct udev_list tags_list; + unsigned long long int seqnum; + unsigned long long int usec_initialized; + int devlink_priority; + int refcount; + dev_t devnum; + int ifindex; + int watch_handle; + int maj, min; + bool parent_set; + bool subsystem_set; + bool devtype_set; + bool devlinks_uptodate; + bool envp_uptodate; + bool tags_uptodate; + bool driver_set; + bool info_loaded; + bool db_loaded; + bool uevent_loaded; + bool is_initialized; + bool sysattr_list_read; + bool db_persist; +}; + +/** + * udev_device_get_seqnum: + * @udev_device: udev device + * + * This is only valid if the device was received through a monitor. Devices read from + * sys do not have a sequence number. + * + * Returns: the kernel event sequence number, or 0 if there is no sequence number available. + **/ +UDEV_EXPORT unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device) +{ + if (udev_device == NULL) + return 0; + return udev_device->seqnum; +} + +static int udev_device_set_seqnum(struct udev_device *udev_device, unsigned long long int seqnum) +{ + char num[32]; + + udev_device->seqnum = seqnum; + snprintf(num, sizeof(num), "%llu", seqnum); + udev_device_add_property(udev_device, "SEQNUM", num); + return 0; +} + +int udev_device_get_ifindex(struct udev_device *udev_device) +{ + if (!udev_device->info_loaded) + udev_device_read_uevent_file(udev_device); + return udev_device->ifindex; +} + +static int udev_device_set_ifindex(struct udev_device *udev_device, int ifindex) +{ + char num[32]; + + udev_device->ifindex = ifindex; + snprintf(num, sizeof(num), "%u", ifindex); + udev_device_add_property(udev_device, "IFINDEX", num); + return 0; +} + +/** + * udev_device_get_devnum: + * @udev_device: udev device + * + * Returns: the device major/minor number. + **/ +UDEV_EXPORT dev_t udev_device_get_devnum(struct udev_device *udev_device) +{ + if (udev_device == NULL) + return makedev(0, 0); + if (!udev_device->info_loaded) + udev_device_read_uevent_file(udev_device); + return udev_device->devnum; +} + +static int udev_device_set_devnum(struct udev_device *udev_device, dev_t devnum) +{ + char num[32]; + + udev_device->devnum = devnum; + + snprintf(num, sizeof(num), "%u", major(devnum)); + udev_device_add_property(udev_device, "MAJOR", num); + snprintf(num, sizeof(num), "%u", minor(devnum)); + udev_device_add_property(udev_device, "MINOR", num); + return 0; +} + +const char *udev_device_get_devpath_old(struct udev_device *udev_device) +{ + return udev_device->devpath_old; +} + +static int udev_device_set_devpath_old(struct udev_device *udev_device, const char *devpath_old) +{ + const char *pos; + + free(udev_device->devpath_old); + udev_device->devpath_old = strdup(devpath_old); + if (udev_device->devpath_old == NULL) + return -ENOMEM; + udev_device_add_property(udev_device, "DEVPATH_OLD", udev_device->devpath_old); + + pos = strrchr(udev_device->devpath_old, '/'); + if (pos == NULL) + return -EINVAL; + return 0; +} + +/** + * udev_device_get_driver: + * @udev_device: udev device + * + * Returns: the driver string, or #NULL if there is no driver attached. + **/ +UDEV_EXPORT const char *udev_device_get_driver(struct udev_device *udev_device) +{ + char driver[UTIL_NAME_SIZE]; + + if (udev_device == NULL) + return NULL; + if (!udev_device->driver_set) { + udev_device->driver_set = true; + if (util_get_sys_core_link_value(udev_device->udev, "driver", udev_device->syspath, driver, sizeof(driver)) > 0) + udev_device->driver = strdup(driver); + } + return udev_device->driver; +} + +static int udev_device_set_driver(struct udev_device *udev_device, const char *driver) +{ + free(udev_device->driver); + udev_device->driver = strdup(driver); + if (udev_device->driver == NULL) + return -ENOMEM; + udev_device->driver_set = true; + udev_device_add_property(udev_device, "DRIVER", udev_device->driver); + return 0; +} + +/** + * udev_device_get_devtype: + * @udev_device: udev device + * + * Retrieve the devtype string of the udev device. + * + * Returns: the devtype name of the udev device, or #NULL if it can not be determined + **/ +UDEV_EXPORT const char *udev_device_get_devtype(struct udev_device *udev_device) +{ + if (udev_device == NULL) + return NULL; + if (!udev_device->devtype_set) { + udev_device->devtype_set = true; + udev_device_read_uevent_file(udev_device); + } + return udev_device->devtype; +} + +static int udev_device_set_devtype(struct udev_device *udev_device, const char *devtype) +{ + free(udev_device->devtype); + udev_device->devtype = strdup(devtype); + if (udev_device->devtype == NULL) + return -ENOMEM; + udev_device->devtype_set = true; + udev_device_add_property(udev_device, "DEVTYPE", udev_device->devtype); + return 0; +} + +static int udev_device_set_subsystem(struct udev_device *udev_device, const char *subsystem) +{ + free(udev_device->subsystem); + udev_device->subsystem = strdup(subsystem); + if (udev_device->subsystem == NULL) + return -ENOMEM; + udev_device->subsystem_set = true; + udev_device_add_property(udev_device, "SUBSYSTEM", udev_device->subsystem); + return 0; +} + +/** + * udev_device_get_subsystem: + * @udev_device: udev device + * + * Retrieve the subsystem string of the udev device. The string does not + * contain any "/". + * + * Returns: the subsystem name of the udev device, or #NULL if it can not be determined + **/ +UDEV_EXPORT const char *udev_device_get_subsystem(struct udev_device *udev_device) +{ + char subsystem[UTIL_NAME_SIZE]; + + if (udev_device == NULL) + return NULL; + if (!udev_device->subsystem_set) { + udev_device->subsystem_set = true; + /* read "subsystem" link */ + if (util_get_sys_core_link_value(udev_device->udev, "subsystem", udev_device->syspath, subsystem, sizeof(subsystem)) > 0) { + udev_device_set_subsystem(udev_device, subsystem); + return udev_device->subsystem; + } + /* implicit names */ + if (strncmp(udev_device->devpath, "/module/", 8) == 0) { + udev_device_set_subsystem(udev_device, "module"); + return udev_device->subsystem; + } + if (strstr(udev_device->devpath, "/drivers/") != NULL) { + udev_device_set_subsystem(udev_device, "drivers"); + return udev_device->subsystem; + } + if (strncmp(udev_device->devpath, "/subsystem/", 11) == 0 || + strncmp(udev_device->devpath, "/class/", 7) == 0 || + strncmp(udev_device->devpath, "/bus/", 5) == 0) { + udev_device_set_subsystem(udev_device, "subsystem"); + return udev_device->subsystem; + } + } + return udev_device->subsystem; +} + +mode_t udev_device_get_devnode_mode(struct udev_device *udev_device) +{ + if (!udev_device->info_loaded) + udev_device_read_uevent_file(udev_device); + return udev_device->devnode_mode; +} + +static int udev_device_set_devnode_mode(struct udev_device *udev_device, mode_t mode) +{ + char num[32]; + + udev_device->devnode_mode = mode; + snprintf(num, sizeof(num), "%#o", mode); + udev_device_add_property(udev_device, "DEVMODE", num); + return 0; +} + +struct udev_list_entry *udev_device_add_property(struct udev_device *udev_device, const char *key, const char *value) +{ + udev_device->envp_uptodate = false; + if (value == NULL) { + struct udev_list_entry *list_entry; + + list_entry = udev_device_get_properties_list_entry(udev_device); + list_entry = udev_list_entry_get_by_name(list_entry, key); + if (list_entry != NULL) + udev_list_entry_delete(list_entry); + return NULL; + } + return udev_list_entry_add(&udev_device->properties_list, key, value); +} + +static struct udev_list_entry *udev_device_add_property_from_string(struct udev_device *udev_device, const char *property) +{ + char name[UTIL_LINE_SIZE]; + char *val; + + util_strscpy(name, sizeof(name), property); + val = strchr(name, '='); + if (val == NULL) + return NULL; + val[0] = '\0'; + val = &val[1]; + if (val[0] == '\0') + val = NULL; + return udev_device_add_property(udev_device, name, val); +} + +/* + * parse property string, and if needed, update internal values accordingly + * + * udev_device_add_property_from_string_parse_finish() needs to be + * called after adding properties, and its return value checked + * + * udev_device_set_info_loaded() needs to be set, to avoid trying + * to use a device without a DEVPATH set + */ +void udev_device_add_property_from_string_parse(struct udev_device *udev_device, const char *property) +{ + if (strncmp(property, "DEVPATH=", 8) == 0) { + char path[UTIL_PATH_SIZE]; + + util_strscpyl(path, sizeof(path), udev_get_sys_path(udev_device->udev), &property[8], NULL); + udev_device_set_syspath(udev_device, path); + } else if (strncmp(property, "SUBSYSTEM=", 10) == 0) { + udev_device_set_subsystem(udev_device, &property[10]); + } else if (strncmp(property, "DEVTYPE=", 8) == 0) { + udev_device_set_devtype(udev_device, &property[8]); + } else if (strncmp(property, "DEVNAME=", 8) == 0) { + udev_device_set_devnode(udev_device, &property[8]); + } else if (strncmp(property, "DEVLINKS=", 9) == 0) { + char devlinks[UTIL_PATH_SIZE]; + char *slink; + char *next; + + util_strscpy(devlinks, sizeof(devlinks), &property[9]); + slink = devlinks; + next = strchr(slink, ' '); + while (next != NULL) { + next[0] = '\0'; + udev_device_add_devlink(udev_device, slink, 0); + slink = &next[1]; + next = strchr(slink, ' '); + } + if (slink[0] != '\0') + udev_device_add_devlink(udev_device, slink, 0); + } else if (strncmp(property, "TAGS=", 5) == 0) { + char tags[UTIL_PATH_SIZE]; + char *next; + + util_strscpy(tags, sizeof(tags), &property[5]); + next = strchr(tags, ':'); + if (next != NULL) { + next++; + while (next[0] != '\0') { + char *tag; + + tag = next; + next = strchr(tag, ':'); + if (next == NULL) + break; + next[0] = '\0'; + next++; + udev_device_add_tag(udev_device, tag); + } + } + } else if (strncmp(property, "USEC_INITIALIZED=", 19) == 0) { + udev_device_set_usec_initialized(udev_device, strtoull(&property[19], NULL, 10)); + } else if (strncmp(property, "DRIVER=", 7) == 0) { + udev_device_set_driver(udev_device, &property[7]); + } else if (strncmp(property, "ACTION=", 7) == 0) { + udev_device_set_action(udev_device, &property[7]); + } else if (strncmp(property, "MAJOR=", 6) == 0) { + udev_device->maj = strtoull(&property[6], NULL, 10); + } else if (strncmp(property, "MINOR=", 6) == 0) { + udev_device->min = strtoull(&property[6], NULL, 10); + } else if (strncmp(property, "DEVPATH_OLD=", 12) == 0) { + udev_device_set_devpath_old(udev_device, &property[12]); + } else if (strncmp(property, "SEQNUM=", 7) == 0) { + udev_device_set_seqnum(udev_device, strtoull(&property[7], NULL, 10)); + } else if (strncmp(property, "IFINDEX=", 8) == 0) { + udev_device_set_ifindex(udev_device, strtoull(&property[8], NULL, 10)); + } else if (strncmp(property, "DEVMODE=", 8) == 0) { + udev_device_set_devnode_mode(udev_device, strtoul(&property[8], NULL, 8)); + } else { + udev_device_add_property_from_string(udev_device, property); + } +} + +int udev_device_add_property_from_string_parse_finish(struct udev_device *udev_device) +{ + if (udev_device->maj > 0) + udev_device_set_devnum(udev_device, makedev(udev_device->maj, udev_device->min)); + udev_device->maj = 0; + udev_device->min = 0; + + if (udev_device->devpath == NULL || udev_device->subsystem == NULL) + return -EINVAL; + return 0; +} + +/** + * udev_device_get_property_value: + * @udev_device: udev device + * @key: property name + * + * Returns: the value of a device property, or #NULL if there is no such property. + **/ +UDEV_EXPORT const char *udev_device_get_property_value(struct udev_device *udev_device, const char *key) +{ + struct udev_list_entry *list_entry; + + if (udev_device == NULL) + return NULL; + if (key == NULL) + return NULL; + + list_entry = udev_device_get_properties_list_entry(udev_device); + list_entry = udev_list_entry_get_by_name(list_entry, key); + return udev_list_entry_get_value(list_entry); +} + +int udev_device_read_db(struct udev_device *udev_device, const char *dbfile) +{ + char filename[UTIL_PATH_SIZE]; + char line[UTIL_LINE_SIZE]; + FILE *f; + + /* providing a database file will always force-load it */ + if (dbfile == NULL) { + const char *id; + + if (udev_device->db_loaded) + return 0; + udev_device->db_loaded = true; + + id = udev_device_get_id_filename(udev_device); + if (id == NULL) + return -1; + util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_device->udev), "/data/", id, NULL); + dbfile = filename; + } + + f = fopen(dbfile, "re"); + if (f == NULL) { + info(udev_device->udev, "no db file to read %s: %m\n", dbfile); + return -1; + } + udev_device->is_initialized = true; + + while (fgets(line, sizeof(line), f)) { + ssize_t len; + const char *val; + struct udev_list_entry *entry; + + len = strlen(line); + if (len < 4) + break; + line[len-1] = '\0'; + val = &line[2]; + switch(line[0]) { + case 'S': + util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_device->udev), "/", val, NULL); + udev_device_add_devlink(udev_device, filename, 0); + break; + case 'L': + udev_device_set_devlink_priority(udev_device, atoi(val)); + break; + case 'E': + entry = udev_device_add_property_from_string(udev_device, val); + udev_list_entry_set_num(entry, true); + break; + case 'G': + udev_device_add_tag(udev_device, val); + break; + case 'W': + udev_device_set_watch_handle(udev_device, atoi(val)); + break; + case 'I': + udev_device_set_usec_initialized(udev_device, strtoull(val, NULL, 10)); + break; + } + } + fclose(f); + + info(udev_device->udev, "device %p filled with db file data\n", udev_device); + return 0; +} + +int udev_device_read_uevent_file(struct udev_device *udev_device) +{ + char filename[UTIL_PATH_SIZE]; + FILE *f; + char line[UTIL_LINE_SIZE]; + int maj = 0; + int min = 0; + + if (udev_device->uevent_loaded) + return 0; + + util_strscpyl(filename, sizeof(filename), udev_device->syspath, "/uevent", NULL); + f = fopen(filename, "re"); + if (f == NULL) + return -1; + udev_device->uevent_loaded = true; + + while (fgets(line, sizeof(line), f)) { + char *pos; + + pos = strchr(line, '\n'); + if (pos == NULL) + continue; + pos[0] = '\0'; + + if (strncmp(line, "DEVTYPE=", 8) == 0) { + udev_device_set_devtype(udev_device, &line[8]); + continue; + } + if (strncmp(line, "IFINDEX=", 8) == 0) { + udev_device_set_ifindex(udev_device, strtoull(&line[8], NULL, 10)); + continue; + } + if (strncmp(line, "DEVNAME=", 8) == 0) { + udev_device_set_devnode(udev_device, &line[8]); + continue; + } + + if (strncmp(line, "MAJOR=", 6) == 0) + maj = strtoull(&line[6], NULL, 10); + else if (strncmp(line, "MINOR=", 6) == 0) + min = strtoull(&line[6], NULL, 10); + else if (strncmp(line, "DEVMODE=", 8) == 0) + udev_device->devnode_mode = strtoul(&line[8], NULL, 8); + + udev_device_add_property_from_string(udev_device, line); + } + + udev_device->devnum = makedev(maj, min); + fclose(f); + return 0; +} + +void udev_device_set_info_loaded(struct udev_device *device) +{ + device->info_loaded = true; +} + +struct udev_device *udev_device_new(struct udev *udev) +{ + struct udev_device *udev_device; + struct udev_list_entry *list_entry; + + if (udev == NULL) + return NULL; + + udev_device = calloc(1, sizeof(struct udev_device)); + if (udev_device == NULL) + return NULL; + udev_device->refcount = 1; + udev_device->udev = udev; + udev_list_init(udev, &udev_device->devlinks_list, true); + udev_list_init(udev, &udev_device->properties_list, true); + udev_list_init(udev, &udev_device->sysattr_value_list, true); + udev_list_init(udev, &udev_device->sysattr_list, false); + udev_list_init(udev, &udev_device->tags_list, true); + udev_device->watch_handle = -1; + /* copy global properties */ + udev_list_entry_foreach(list_entry, udev_get_properties_list_entry(udev)) + udev_device_add_property(udev_device, + udev_list_entry_get_name(list_entry), + udev_list_entry_get_value(list_entry)); + dbg(udev_device->udev, "udev_device: %p created\n", udev_device); + return udev_device; +} + +/** + * udev_device_new_from_syspath: + * @udev: udev library context + * @syspath: sys device path including sys directory + * + * Create new udev device, and fill in information from the sys + * device and the udev database entry. The syspath is the absolute + * path to the device, including the sys mount point. + * + * The initial refcount is 1, and needs to be decremented to + * release the resources of the udev device. + * + * Returns: a new udev device, or #NULL, if it does not exist + **/ +UDEV_EXPORT struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath) +{ + size_t len; + const char *subdir; + char path[UTIL_PATH_SIZE]; + char *pos; + struct stat statbuf; + struct udev_device *udev_device; + + if (udev == NULL) + return NULL; + if (syspath == NULL) + return NULL; + + /* path starts in sys */ + len = strlen(udev_get_sys_path(udev)); + if (strncmp(syspath, udev_get_sys_path(udev), len) != 0) { + info(udev, "not in sys :%s\n", syspath); + return NULL; + } + + /* path is not a root directory */ + subdir = &syspath[len+1]; + pos = strrchr(subdir, '/'); + if (pos == NULL || pos[1] == '\0' || pos < &subdir[2]) { + dbg(udev, "not a subdir :%s\n", syspath); + return NULL; + } + + /* resolve possible symlink to real path */ + util_strscpy(path, sizeof(path), syspath); + util_resolve_sys_link(udev, path, sizeof(path)); + + if (strncmp(&path[len], "/devices/", 9) == 0) { + char file[UTIL_PATH_SIZE]; + + /* all "devices" require a "uevent" file */ + util_strscpyl(file, sizeof(file), path, "/uevent", NULL); + if (stat(file, &statbuf) != 0) { + dbg(udev, "not a device: %s\n", syspath); + return NULL; + } + } else { + /* everything else just needs to be a directory */ + if (stat(path, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) { + dbg(udev, "directory not found: %s\n", syspath); + return NULL; + } + } + + udev_device = udev_device_new(udev); + if (udev_device == NULL) + return NULL; + + udev_device_set_syspath(udev_device, path); + info(udev, "device %p has devpath '%s'\n", udev_device, udev_device_get_devpath(udev_device)); + + return udev_device; +} + +/** + * udev_device_new_from_devnum: + * @udev: udev library context + * @type: char or block device + * @devnum: device major/minor number + * + * Create new udev device, and fill in information from the sys + * device and the udev database entry. The device is looked-up + * by its major/minor number and type. Character and block device + * numbers are not unique across the two types. + * + * The initial refcount is 1, and needs to be decremented to + * release the resources of the udev device. + * + * Returns: a new udev device, or #NULL, if it does not exist + **/ +UDEV_EXPORT struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum) +{ + char path[UTIL_PATH_SIZE]; + const char *type_str; + + if (type == 'b') + type_str = "block"; + else if (type == 'c') + type_str = "char"; + else + return NULL; + + /* use /sys/dev/{block,char}/: link */ + snprintf(path, sizeof(path), "%s/dev/%s/%u:%u", + udev_get_sys_path(udev), type_str, major(devnum), minor(devnum)); + return udev_device_new_from_syspath(udev, path); +} + +struct udev_device *udev_device_new_from_id_filename(struct udev *udev, char *id) +{ + char type; + int maj, min; + char subsys[UTIL_PATH_SIZE]; + char *sysname; + + switch(id[0]) { + case 'b': + case 'c': + if (sscanf(id, "%c%i:%i", &type, &maj, &min) != 3) + return NULL; + return udev_device_new_from_devnum(udev, type, makedev(maj, min)); + case 'n': { + int sk; + struct ifreq ifr; + struct udev_device *dev; + int ifindex; + + ifindex = strtoul(&id[1], NULL, 10); + if (ifindex <= 0) + return NULL; + + sk = socket(PF_INET, SOCK_DGRAM, 0); + if (sk < 0) + return NULL; + memset(&ifr, 0x00, sizeof(struct ifreq)); + ifr.ifr_ifindex = ifindex; + if (ioctl(sk, SIOCGIFNAME, &ifr) != 0) { + close(sk); + return NULL; + } + close(sk); + + dev = udev_device_new_from_subsystem_sysname(udev, "net", ifr.ifr_name); + if (dev == NULL) + return NULL; + if (udev_device_get_ifindex(dev) == ifindex) + return dev; + udev_device_unref(dev); + return NULL; + } + case '+': + util_strscpy(subsys, sizeof(subsys), &id[1]); + sysname = strchr(subsys, ':'); + if (sysname == NULL) + return NULL; + sysname[0] = '\0'; + sysname = &sysname[1]; + return udev_device_new_from_subsystem_sysname(udev, subsys, sysname); + default: + return NULL; + } +} + +/** + * udev_device_new_from_subsystem_sysname: + * @udev: udev library context + * @subsystem: the subsystem of the device + * @sysname: the name of the device + * + * Create new udev device, and fill in information from the sys device + * and the udev database entry. The device is looked up by the subsystem + * and name string of the device, like "mem" / "zero", or "block" / "sda". + * + * The initial refcount is 1, and needs to be decremented to + * release the resources of the udev device. + * + * Returns: a new udev device, or #NULL, if it does not exist + **/ +UDEV_EXPORT struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname) +{ + char path_full[UTIL_PATH_SIZE]; + char *path; + size_t l; + struct stat statbuf; + + path = path_full; + l = util_strpcpyl(&path, sizeof(path_full), udev_get_sys_path(udev), NULL); + + if (strcmp(subsystem, "subsystem") == 0) { + util_strscpyl(path, l, "/subsystem/", sysname, NULL); + if (stat(path_full, &statbuf) == 0) + goto found; + + util_strscpyl(path, l, "/bus/", sysname, NULL); + if (stat(path_full, &statbuf) == 0) + goto found; + + util_strscpyl(path, l, "/class/", sysname, NULL); + if (stat(path_full, &statbuf) == 0) + goto found; + goto out; + } + + if (strcmp(subsystem, "module") == 0) { + util_strscpyl(path, l, "/module/", sysname, NULL); + if (stat(path_full, &statbuf) == 0) + goto found; + goto out; + } + + if (strcmp(subsystem, "drivers") == 0) { + char subsys[UTIL_NAME_SIZE]; + char *driver; + + util_strscpy(subsys, sizeof(subsys), sysname); + driver = strchr(subsys, ':'); + if (driver != NULL) { + driver[0] = '\0'; + driver = &driver[1]; + + util_strscpyl(path, l, "/subsystem/", subsys, "/drivers/", driver, NULL); + if (stat(path_full, &statbuf) == 0) + goto found; + + util_strscpyl(path, l, "/bus/", subsys, "/drivers/", driver, NULL); + if (stat(path_full, &statbuf) == 0) + goto found; + } + goto out; + } + + util_strscpyl(path, l, "/subsystem/", subsystem, "/devices/", sysname, NULL); + if (stat(path_full, &statbuf) == 0) + goto found; + + util_strscpyl(path, l, "/bus/", subsystem, "/devices/", sysname, NULL); + if (stat(path_full, &statbuf) == 0) + goto found; + + util_strscpyl(path, l, "/class/", subsystem, "/", sysname, NULL); + if (stat(path_full, &statbuf) == 0) + goto found; +out: + return NULL; +found: + return udev_device_new_from_syspath(udev, path_full); +} + +/** + * udev_device_new_from_environment + * @udev: udev library context + * + * Create new udev device, and fill in information from the + * current process environment. This only works reliable if + * the process is called from a udev rule. It is usually used + * for tools executed from IMPORT= rules. + * + * The initial refcount is 1, and needs to be decremented to + * release the resources of the udev device. + * + * Returns: a new udev device, or #NULL, if it does not exist + **/ +UDEV_EXPORT struct udev_device *udev_device_new_from_environment(struct udev *udev) +{ + int i; + struct udev_device *udev_device; + + udev_device = udev_device_new(udev); + if (udev_device == NULL) + return NULL; + udev_device_set_info_loaded(udev_device); + + for (i = 0; environ[i] != NULL; i++) + udev_device_add_property_from_string_parse(udev_device, environ[i]); + + if (udev_device_add_property_from_string_parse_finish(udev_device) < 0) { + info(udev, "missing values, invalid device\n"); + udev_device_unref(udev_device); + udev_device = NULL; + } + + return udev_device; +} + +static struct udev_device *device_new_from_parent(struct udev_device *udev_device) +{ + struct udev_device *udev_device_parent = NULL; + char path[UTIL_PATH_SIZE]; + const char *subdir; + + util_strscpy(path, sizeof(path), udev_device->syspath); + subdir = &path[strlen(udev_get_sys_path(udev_device->udev))+1]; + for (;;) { + char *pos; + + pos = strrchr(subdir, '/'); + if (pos == NULL || pos < &subdir[2]) + break; + pos[0] = '\0'; + udev_device_parent = udev_device_new_from_syspath(udev_device->udev, path); + if (udev_device_parent != NULL) + return udev_device_parent; + } + return NULL; +} + +/** + * udev_device_get_parent: + * @udev_device: the device to start searching from + * + * Find the next parent device, and fill in information from the sys + * device and the udev database entry. + * + * The returned the device is not referenced. It is attached to the + * child device, and will be cleaned up when the child device + * is cleaned up. + * + * It is not necessarily just the upper level directory, empty or not + * recognized sys directories are ignored. + * + * It can be called as many times as needed, without caring about + * references. + * + * Returns: a new udev device, or #NULL, if it no parent exist. + **/ +UDEV_EXPORT struct udev_device *udev_device_get_parent(struct udev_device *udev_device) +{ + if (udev_device == NULL) + return NULL; + if (!udev_device->parent_set) { + udev_device->parent_set = true; + udev_device->parent_device = device_new_from_parent(udev_device); + } + if (udev_device->parent_device != NULL) + dbg(udev_device->udev, "returning existing parent %p\n", udev_device->parent_device); + return udev_device->parent_device; +} + +/** + * udev_device_get_parent_with_subsystem_devtype: + * @udev_device: udev device to start searching from + * @subsystem: the subsystem of the device + * @devtype: the type (DEVTYPE) of the device + * + * Find the next parent device, with a matching subsystem and devtype + * value, and fill in information from the sys device and the udev + * database entry. + * + * If devtype is #NULL, only subsystem is checked, and any devtype will + * match. + * + * The returned the device is not referenced. It is attached to the + * child device, and will be cleaned up when the child device + * is cleaned up. + * + * It can be called as many times as needed, without caring about + * references. + * + * Returns: a new udev device, or #NULL if no matching parent exists. + **/ +UDEV_EXPORT struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_device *udev_device, const char *subsystem, const char *devtype) +{ + struct udev_device *parent; + + if (subsystem == NULL) + return NULL; + + parent = udev_device_get_parent(udev_device); + while (parent != NULL) { + const char *parent_subsystem; + const char *parent_devtype; + + parent_subsystem = udev_device_get_subsystem(parent); + if (parent_subsystem != NULL && strcmp(parent_subsystem, subsystem) == 0) { + if (devtype == NULL) + break; + parent_devtype = udev_device_get_devtype(parent); + if (parent_devtype != NULL && strcmp(parent_devtype, devtype) == 0) + break; + } + parent = udev_device_get_parent(parent); + } + return parent; +} + +/** + * udev_device_get_udev: + * @udev_device: udev device + * + * Retrieve the udev library context the device was created with. + * + * Returns: the udev library context + **/ +UDEV_EXPORT struct udev *udev_device_get_udev(struct udev_device *udev_device) +{ + if (udev_device == NULL) + return NULL; + return udev_device->udev; +} + +/** + * udev_device_ref: + * @udev_device: udev device + * + * Take a reference of a udev device. + * + * Returns: the passed udev device + **/ +UDEV_EXPORT struct udev_device *udev_device_ref(struct udev_device *udev_device) +{ + if (udev_device == NULL) + return NULL; + udev_device->refcount++; + return udev_device; +} + +/** + * udev_device_unref: + * @udev_device: udev device + * + * Drop a reference of a udev device. If the refcount reaches zero, + * the resources of the device will be released. + * + **/ +UDEV_EXPORT void udev_device_unref(struct udev_device *udev_device) +{ + if (udev_device == NULL) + return; + udev_device->refcount--; + if (udev_device->refcount > 0) + return; + if (udev_device->parent_device != NULL) + udev_device_unref(udev_device->parent_device); + free(udev_device->syspath); + free(udev_device->sysname); + free(udev_device->devnode); + free(udev_device->subsystem); + free(udev_device->devtype); + udev_list_cleanup(&udev_device->devlinks_list); + udev_list_cleanup(&udev_device->properties_list); + udev_list_cleanup(&udev_device->sysattr_value_list); + udev_list_cleanup(&udev_device->sysattr_list); + udev_list_cleanup(&udev_device->tags_list); + free(udev_device->action); + free(udev_device->driver); + free(udev_device->devpath_old); + free(udev_device->id_filename); + free(udev_device->envp); + free(udev_device->monitor_buf); + dbg(udev_device->udev, "udev_device: %p released\n", udev_device); + free(udev_device); +} + +/** + * udev_device_get_devpath: + * @udev_device: udev device + * + * Retrieve the kernel devpath value of the udev device. The path + * does not contain the sys mount point, and starts with a '/'. + * + * Returns: the devpath of the udev device + **/ +UDEV_EXPORT const char *udev_device_get_devpath(struct udev_device *udev_device) +{ + if (udev_device == NULL) + return NULL; + return udev_device->devpath; +} + +/** + * udev_device_get_syspath: + * @udev_device: udev device + * + * Retrieve the sys path of the udev device. The path is an + * absolute path and starts with the sys mount point. + * + * Returns: the sys path of the udev device + **/ +UDEV_EXPORT const char *udev_device_get_syspath(struct udev_device *udev_device) +{ + if (udev_device == NULL) + return NULL; + return udev_device->syspath; +} + +/** + * udev_device_get_sysname: + * @udev_device: udev device + * + * Returns: the sys name of the device device + **/ +UDEV_EXPORT const char *udev_device_get_sysname(struct udev_device *udev_device) +{ + if (udev_device == NULL) + return NULL; + return udev_device->sysname; +} + +/** + * udev_device_get_sysnum: + * @udev_device: udev device + * + * Returns: the trailing number of of the device name + **/ +UDEV_EXPORT const char *udev_device_get_sysnum(struct udev_device *udev_device) +{ + if (udev_device == NULL) + return NULL; + return udev_device->sysnum; +} + +/** + * udev_device_get_devnode: + * @udev_device: udev device + * + * Retrieve the device node file name belonging to the udev device. + * The path is an absolute path, and starts with the device directory. + * + * Returns: the device node file name of the udev device, or #NULL if no device node exists + **/ +UDEV_EXPORT const char *udev_device_get_devnode(struct udev_device *udev_device) +{ + if (udev_device == NULL) + return NULL; + if (udev_device->devnode != NULL) + return udev_device->devnode; + if (!udev_device->info_loaded) + udev_device_read_uevent_file(udev_device); + return udev_device->devnode; +} + +/** + * udev_device_get_devlinks_list_entry: + * @udev_device: udev device + * + * Retrieve the list of device links pointing to the device file of + * the udev device. The next list entry can be retrieved with + * udev_list_entry_next(), which returns #NULL if no more entries exist. + * The devlink path can be retrieved from the list entry by + * udev_list_entry_get_name(). The path is an absolute path, and starts with + * the device directory. + * + * Returns: the first entry of the device node link list + **/ +UDEV_EXPORT struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev_device *udev_device) +{ + if (udev_device == NULL) + return NULL; + if (!udev_device->info_loaded) + udev_device_read_db(udev_device, NULL); + return udev_list_get_entry(&udev_device->devlinks_list); +} + +void udev_device_cleanup_devlinks_list(struct udev_device *udev_device) +{ + udev_device->devlinks_uptodate = false; + udev_list_cleanup(&udev_device->devlinks_list); +} + +/** + * udev_device_get_properties_list_entry: + * @udev_device: udev device + * + * Retrieve the list of key/value device properties of the udev + * device. The next list entry can be retrieved with udev_list_entry_next(), + * which returns #NULL if no more entries exist. The property name + * can be retrieved from the list entry by udev_list_get_name(), + * the property value by udev_list_get_value(). + * + * Returns: the first entry of the property list + **/ +UDEV_EXPORT struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device *udev_device) +{ + if (udev_device == NULL) + return NULL; + if (!udev_device->info_loaded) { + udev_device_read_uevent_file(udev_device); + udev_device_read_db(udev_device, NULL); + } + if (!udev_device->devlinks_uptodate) { + char symlinks[UTIL_PATH_SIZE]; + struct udev_list_entry *list_entry; + + udev_device->devlinks_uptodate = true; + list_entry = udev_device_get_devlinks_list_entry(udev_device); + if (list_entry != NULL) { + char *s; + size_t l; + + s = symlinks; + l = util_strpcpyl(&s, sizeof(symlinks), udev_list_entry_get_name(list_entry), NULL); + udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry)) + l = util_strpcpyl(&s, l, " ", udev_list_entry_get_name(list_entry), NULL); + udev_device_add_property(udev_device, "DEVLINKS", symlinks); + } + } + if (!udev_device->tags_uptodate) { + udev_device->tags_uptodate = true; + if (udev_device_get_tags_list_entry(udev_device) != NULL) { + char tags[UTIL_PATH_SIZE]; + struct udev_list_entry *list_entry; + char *s; + size_t l; + + s = tags; + l = util_strpcpyl(&s, sizeof(tags), ":", NULL); + udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(udev_device)) + l = util_strpcpyl(&s, l, udev_list_entry_get_name(list_entry), ":", NULL); + udev_device_add_property(udev_device, "TAGS", tags); + } + } + return udev_list_get_entry(&udev_device->properties_list); +} + +/** + * udev_device_get_action: + * @udev_device: udev device + * + * This is only valid if the device was received through a monitor. Devices read from + * sys do not have an action string. Usual actions are: add, remove, change, online, + * offline. + * + * Returns: the kernel action value, or #NULL if there is no action value available. + **/ +UDEV_EXPORT const char *udev_device_get_action(struct udev_device *udev_device) +{ + if (udev_device == NULL) + return NULL; + return udev_device->action; +} + +/** + * udev_device_get_usec_since_initialized: + * @udev_device: udev device + * + * Return the number of microseconds passed since udev set up the + * device for the first time. + * + * This is only implemented for devices with need to store properties + * in the udev database. All other devices return 0 here. + * + * Returns: the number of microseconds since the device was first seen. + **/ +UDEV_EXPORT unsigned long long int udev_device_get_usec_since_initialized(struct udev_device *udev_device) +{ + unsigned long long now; + + if (udev_device == NULL) + return 0; + if (!udev_device->info_loaded) + udev_device_read_db(udev_device, NULL); + if (udev_device->usec_initialized == 0) + return 0; + now = now_usec(); + if (now == 0) + return 0; + return now - udev_device->usec_initialized; +} + +unsigned long long udev_device_get_usec_initialized(struct udev_device *udev_device) +{ + return udev_device->usec_initialized; +} + +void udev_device_set_usec_initialized(struct udev_device *udev_device, unsigned long long usec_initialized) +{ + char num[32]; + + udev_device->usec_initialized = usec_initialized; + snprintf(num, sizeof(num), "%llu", usec_initialized); + udev_device_add_property(udev_device, "USEC_INITIALIZED", num); +} + +/** + * udev_device_get_sysattr_value: + * @udev_device: udev device + * @sysattr: attribute name + * + * The retrieved value is cached in the device. Repeated calls will return the same + * value and not open the attribute again. + * + * Returns: the content of a sys attribute file, or #NULL if there is no sys attribute value. + **/ +UDEV_EXPORT const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr) +{ + struct udev_list_entry *list_entry; + char path[UTIL_PATH_SIZE]; + char value[4096]; + struct stat statbuf; + int fd; + ssize_t size; + const char *val = NULL; + + if (udev_device == NULL) + return NULL; + if (sysattr == NULL) + return NULL; + + /* look for possibly already cached result */ + list_entry = udev_list_get_entry(&udev_device->sysattr_value_list); + list_entry = udev_list_entry_get_by_name(list_entry, sysattr); + if (list_entry != NULL) { + dbg(udev_device->udev, "got '%s' (%s) from cache\n", + sysattr, udev_list_entry_get_value(list_entry)); + return udev_list_entry_get_value(list_entry); + } + + util_strscpyl(path, sizeof(path), udev_device_get_syspath(udev_device), "/", sysattr, NULL); + if (lstat(path, &statbuf) != 0) { + dbg(udev_device->udev, "no attribute '%s', keep negative entry\n", path); + udev_list_entry_add(&udev_device->sysattr_value_list, sysattr, NULL); + goto out; + } + + if (S_ISLNK(statbuf.st_mode)) { + struct udev_device *dev; + + /* + * Some core links return only the last element of the target path, + * these are just values, the paths should not be exposed. + */ + if (strcmp(sysattr, "driver") == 0 || + strcmp(sysattr, "subsystem") == 0 || + strcmp(sysattr, "module") == 0) { + if (util_get_sys_core_link_value(udev_device->udev, sysattr, + udev_device->syspath, value, sizeof(value)) < 0) + return NULL; + dbg(udev_device->udev, "cache '%s' with link value '%s'\n", sysattr, value); + list_entry = udev_list_entry_add(&udev_device->sysattr_value_list, sysattr, value); + val = udev_list_entry_get_value(list_entry); + goto out; + } + + /* resolve link to a device and return its syspath */ + util_strscpyl(path, sizeof(path), udev_device->syspath, "/", sysattr, NULL); + dev = udev_device_new_from_syspath(udev_device->udev, path); + if (dev != NULL) { + list_entry = udev_list_entry_add(&udev_device->sysattr_value_list, sysattr, + udev_device_get_syspath(dev)); + val = udev_list_entry_get_value(list_entry); + udev_device_unref(dev); + } + + goto out; + } + + /* skip directories */ + if (S_ISDIR(statbuf.st_mode)) + goto out; + + /* skip non-readable files */ + if ((statbuf.st_mode & S_IRUSR) == 0) + goto out; + + /* read attribute value */ + fd = open(path, O_RDONLY|O_CLOEXEC); + if (fd < 0) { + dbg(udev_device->udev, "attribute '%s' can not be opened\n", path); + goto out; + } + size = read(fd, value, sizeof(value)); + close(fd); + if (size < 0) + goto out; + if (size == sizeof(value)) + goto out; + + /* got a valid value, store it in cache and return it */ + value[size] = '\0'; + util_remove_trailing_chars(value, '\n'); + dbg(udev_device->udev, "'%s' has attribute value '%s'\n", path, value); + list_entry = udev_list_entry_add(&udev_device->sysattr_value_list, sysattr, value); + val = udev_list_entry_get_value(list_entry); +out: + return val; +} + +static int udev_device_sysattr_list_read(struct udev_device *udev_device) +{ + struct dirent *dent; + DIR *dir; + int num = 0; + + if (udev_device == NULL) + return -1; + if (udev_device->sysattr_list_read) + return 0; + + dir = opendir(udev_device_get_syspath(udev_device)); + if (!dir) { + dbg(udev_device->udev, "sysfs dir '%s' can not be opened\n", + udev_device_get_syspath(udev_device)); + return -1; + } + + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + char path[UTIL_PATH_SIZE]; + struct stat statbuf; + + /* only handle symlinks and regular files */ + if (dent->d_type != DT_LNK && dent->d_type != DT_REG) + continue; + + util_strscpyl(path, sizeof(path), udev_device_get_syspath(udev_device), "/", dent->d_name, NULL); + if (lstat(path, &statbuf) != 0) + continue; + if ((statbuf.st_mode & S_IRUSR) == 0) + continue; + + udev_list_entry_add(&udev_device->sysattr_list, dent->d_name, NULL); + num++; + } + + closedir(dir); + dbg(udev_device->udev, "found %d sysattrs for '%s'\n", num, udev_device_get_syspath(udev_device)); + udev_device->sysattr_list_read = true; + + return num; +} + +/** + * udev_device_get_sysattr_list_entry: + * @udev_device: udev device + * + * Retrieve the list of available sysattrs, with value being empty; + * This just return all available sysfs attributes for a particular + * device without reading their values. + * + * Returns: the first entry of the property list + **/ +UDEV_EXPORT struct udev_list_entry *udev_device_get_sysattr_list_entry(struct udev_device *udev_device) +{ + if (!udev_device->sysattr_list_read) { + int ret; + ret = udev_device_sysattr_list_read(udev_device); + if (0 > ret) + return NULL; + } + + return udev_list_get_entry(&udev_device->sysattr_list); +} + +int udev_device_set_syspath(struct udev_device *udev_device, const char *syspath) +{ + const char *pos; + size_t len; + + free(udev_device->syspath); + udev_device->syspath = strdup(syspath); + if (udev_device->syspath == NULL) + return -ENOMEM; + udev_device->devpath = &udev_device->syspath[strlen(udev_get_sys_path(udev_device->udev))]; + udev_device_add_property(udev_device, "DEVPATH", udev_device->devpath); + + pos = strrchr(udev_device->syspath, '/'); + if (pos == NULL) + return -EINVAL; + udev_device->sysname = strdup(&pos[1]); + if (udev_device->sysname == NULL) + return -ENOMEM; + + /* some devices have '!' in their name, change that to '/' */ + len = 0; + while (udev_device->sysname[len] != '\0') { + if (udev_device->sysname[len] == '!') + udev_device->sysname[len] = '/'; + len++; + } + + /* trailing number */ + while (len > 0 && isdigit(udev_device->sysname[--len])) + udev_device->sysnum = &udev_device->sysname[len]; + + /* sysname is completely numeric */ + if (len == 0) + udev_device->sysnum = NULL; + + return 0; +} + +int udev_device_set_devnode(struct udev_device *udev_device, const char *devnode) +{ + free(udev_device->devnode); + if (devnode[0] != '/') { + if (asprintf(&udev_device->devnode, "%s/%s", udev_get_dev_path(udev_device->udev), devnode) < 0) + udev_device->devnode = NULL; + } else { + udev_device->devnode = strdup(devnode); + } + if (udev_device->devnode == NULL) + return -ENOMEM; + udev_device_add_property(udev_device, "DEVNAME", udev_device->devnode); + return 0; +} + +int udev_device_add_devlink(struct udev_device *udev_device, const char *devlink, int unique) +{ + struct udev_list_entry *list_entry; + + udev_device->devlinks_uptodate = false; + list_entry = udev_list_entry_add(&udev_device->devlinks_list, devlink, NULL); + if (list_entry == NULL) + return -ENOMEM; + if (unique) + udev_list_entry_set_num(list_entry, true); + return 0; +} + +const char *udev_device_get_id_filename(struct udev_device *udev_device) +{ + if (udev_device->id_filename == NULL) { + if (udev_device_get_subsystem(udev_device) == NULL) + return NULL; + + if (major(udev_device_get_devnum(udev_device)) > 0) { + /* use dev_t -- b259:131072, c254:0 */ + if (asprintf(&udev_device->id_filename, "%c%u:%u", + strcmp(udev_device_get_subsystem(udev_device), "block") == 0 ? 'b' : 'c', + major(udev_device_get_devnum(udev_device)), + minor(udev_device_get_devnum(udev_device))) < 0) + udev_device->id_filename = NULL; + } else if (udev_device_get_ifindex(udev_device) > 0) { + /* use netdev ifindex -- n3 */ + if (asprintf(&udev_device->id_filename, "n%u", udev_device_get_ifindex(udev_device)) < 0) + udev_device->id_filename = NULL; + } else { + /* + * use $subsys:$syname -- pci:0000:00:1f.2 + * sysname() has '!' translated, get it from devpath + */ + const char *sysname; + sysname = strrchr(udev_device->devpath, '/'); + if (sysname == NULL) + return NULL; + sysname = &sysname[1]; + if (asprintf(&udev_device->id_filename, "+%s:%s", udev_device_get_subsystem(udev_device), sysname) < 0) + udev_device->id_filename = NULL; + } + } + return udev_device->id_filename; +} + +/** + * udev_device_get_is_initialized: + * @udev_device: udev device + * + * Check if udev has already handled the device and has set up + * device node permissions and context, or has renamed a network + * device. + * + * This is only implemented for devices with a device node + * or network interfaces. All other devices return 1 here. + * + * Returns: 1 if the device is set up. 0 otherwise. + **/ +UDEV_EXPORT int udev_device_get_is_initialized(struct udev_device *udev_device) +{ + if (!udev_device->info_loaded) + udev_device_read_db(udev_device, NULL); + return udev_device->is_initialized; +} + +void udev_device_set_is_initialized(struct udev_device *udev_device) +{ + udev_device->is_initialized = true; +} + +int udev_device_add_tag(struct udev_device *udev_device, const char *tag) +{ + if (strchr(tag, ':') != NULL || strchr(tag, ' ') != NULL) + return -EINVAL; + udev_device->tags_uptodate = false; + if (udev_list_entry_add(&udev_device->tags_list, tag, NULL) != NULL) + return 0; + return -ENOMEM; +} + +void udev_device_cleanup_tags_list(struct udev_device *udev_device) +{ + udev_device->tags_uptodate = false; + udev_list_cleanup(&udev_device->tags_list); +} + +/** + * udev_device_get_tags_list_entry: + * @udev_device: udev device + * + * Retrieve the list of tags attached to the udev device. The next + * list entry can be retrieved with udev_list_entry_next(), + * which returns #NULL if no more entries exist. The tag string + * can be retrieved from the list entry by udev_list_get_name(). + * + * Returns: the first entry of the tag list + **/ +UDEV_EXPORT struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_device *udev_device) +{ + if (udev_device == NULL) + return NULL; + if (!udev_device->info_loaded) + udev_device_read_db(udev_device, NULL); + return udev_list_get_entry(&udev_device->tags_list); +} + +UDEV_EXPORT int udev_device_has_tag(struct udev_device *udev_device, const char *tag) +{ + struct udev_list_entry *list_entry; + + if (udev_device == NULL) + return false; + if (!udev_device->info_loaded) + udev_device_read_db(udev_device, NULL); + list_entry = udev_device_get_tags_list_entry(udev_device); + if (udev_list_entry_get_by_name(list_entry, tag) != NULL) + return true; + return false; +} + +#define ENVP_SIZE 128 +#define MONITOR_BUF_SIZE 4096 +static int update_envp_monitor_buf(struct udev_device *udev_device) +{ + struct udev_list_entry *list_entry; + char *s; + size_t l; + unsigned int i; + + /* monitor buffer of property strings */ + free(udev_device->monitor_buf); + udev_device->monitor_buf_len = 0; + udev_device->monitor_buf = malloc(MONITOR_BUF_SIZE); + if (udev_device->monitor_buf == NULL) + return -ENOMEM; + + /* envp array, strings will point into monitor buffer */ + if (udev_device->envp == NULL) + udev_device->envp = malloc(sizeof(char *) * ENVP_SIZE); + if (udev_device->envp == NULL) + return -ENOMEM; + + i = 0; + s = udev_device->monitor_buf; + l = MONITOR_BUF_SIZE; + udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev_device)) { + const char *key; + + key = udev_list_entry_get_name(list_entry); + /* skip private variables */ + if (key[0] == '.') + continue; + + /* add string to envp array */ + udev_device->envp[i++] = s; + if (i+1 >= ENVP_SIZE) + return -EINVAL; + + /* add property string to monitor buffer */ + l = util_strpcpyl(&s, l, key, "=", udev_list_entry_get_value(list_entry), NULL); + if (l == 0) + return -EINVAL; + /* advance past the trailing '\0' that util_strpcpyl() guarantees */ + s++; + l--; + } + udev_device->envp[i] = NULL; + udev_device->monitor_buf_len = s - udev_device->monitor_buf; + udev_device->envp_uptodate = true; + dbg(udev_device->udev, "filled envp/monitor buffer, %u properties, %zu bytes\n", + i, udev_device->monitor_buf_len); + return 0; +} + +char **udev_device_get_properties_envp(struct udev_device *udev_device) +{ + if (!udev_device->envp_uptodate) + if (update_envp_monitor_buf(udev_device) != 0) + return NULL; + return udev_device->envp; +} + +ssize_t udev_device_get_properties_monitor_buf(struct udev_device *udev_device, const char **buf) +{ + if (!udev_device->envp_uptodate) + if (update_envp_monitor_buf(udev_device) != 0) + return -EINVAL; + *buf = udev_device->monitor_buf; + return udev_device->monitor_buf_len; +} + +int udev_device_set_action(struct udev_device *udev_device, const char *action) +{ + free(udev_device->action); + udev_device->action = strdup(action); + if (udev_device->action == NULL) + return -ENOMEM; + udev_device_add_property(udev_device, "ACTION", udev_device->action); + return 0; +} + +int udev_device_get_devlink_priority(struct udev_device *udev_device) +{ + if (!udev_device->info_loaded) + udev_device_read_db(udev_device, NULL); + return udev_device->devlink_priority; +} + +int udev_device_set_devlink_priority(struct udev_device *udev_device, int prio) +{ + udev_device->devlink_priority = prio; + return 0; +} + +int udev_device_get_watch_handle(struct udev_device *udev_device) +{ + if (!udev_device->info_loaded) + udev_device_read_db(udev_device, NULL); + return udev_device->watch_handle; +} + +int udev_device_set_watch_handle(struct udev_device *udev_device, int handle) +{ + udev_device->watch_handle = handle; + return 0; +} + +bool udev_device_get_db_persist(struct udev_device *udev_device) +{ + return udev_device->db_persist; +} + +void udev_device_set_db_persist(struct udev_device *udev_device) +{ + udev_device->db_persist = true; +} diff --git a/src/udev/libudev-enumerate.c b/src/udev/libudev-enumerate.c new file mode 100644 index 0000000000..034d96feba --- /dev/null +++ b/src/udev/libudev-enumerate.c @@ -0,0 +1,947 @@ +/* + * libudev - interface to udev device information + * + * Copyright (C) 2008-2010 Kay Sievers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libudev.h" +#include "libudev-private.h" + +/** + * SECTION:libudev-enumerate + * @short_description: lookup and sort sys devices + * + * Lookup devices in the sys filesystem, filter devices by properties, + * and return a sorted list of devices. + */ + +struct syspath { + char *syspath; + size_t len; +}; + +/** + * udev_enumerate: + * + * Opaque object representing one device lookup/sort context. + */ +struct udev_enumerate { + struct udev *udev; + int refcount; + struct udev_list sysattr_match_list; + struct udev_list sysattr_nomatch_list; + struct udev_list subsystem_match_list; + struct udev_list subsystem_nomatch_list; + struct udev_list sysname_match_list; + struct udev_list properties_match_list; + struct udev_list tags_match_list; + struct udev_device *parent_match; + struct udev_list devices_list; + struct syspath *devices; + unsigned int devices_cur; + unsigned int devices_max; + bool devices_uptodate:1; + bool match_is_initialized; +}; + +/** + * udev_enumerate_new: + * @udev: udev library context + * + * Returns: an enumeration context + **/ +UDEV_EXPORT struct udev_enumerate *udev_enumerate_new(struct udev *udev) +{ + struct udev_enumerate *udev_enumerate; + + udev_enumerate = calloc(1, sizeof(struct udev_enumerate)); + if (udev_enumerate == NULL) + return NULL; + udev_enumerate->refcount = 1; + udev_enumerate->udev = udev; + udev_list_init(udev, &udev_enumerate->sysattr_match_list, false); + udev_list_init(udev, &udev_enumerate->sysattr_nomatch_list, false); + udev_list_init(udev, &udev_enumerate->subsystem_match_list, true); + udev_list_init(udev, &udev_enumerate->subsystem_nomatch_list, true); + udev_list_init(udev, &udev_enumerate->sysname_match_list, true); + udev_list_init(udev, &udev_enumerate->properties_match_list, false); + udev_list_init(udev, &udev_enumerate->tags_match_list, true); + udev_list_init(udev, &udev_enumerate->devices_list, false); + return udev_enumerate; +} + +/** + * udev_enumerate_ref: + * @udev_enumerate: context + * + * Take a reference of a enumeration context. + * + * Returns: the passed enumeration context + **/ +UDEV_EXPORT struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate) +{ + if (udev_enumerate == NULL) + return NULL; + udev_enumerate->refcount++; + return udev_enumerate; +} + +/** + * udev_enumerate_unref: + * @udev_enumerate: context + * + * Drop a reference of an enumeration context. If the refcount reaches zero, + * all resources of the enumeration context will be released. + **/ +UDEV_EXPORT void udev_enumerate_unref(struct udev_enumerate *udev_enumerate) +{ + unsigned int i; + + if (udev_enumerate == NULL) + return; + udev_enumerate->refcount--; + if (udev_enumerate->refcount > 0) + return; + udev_list_cleanup(&udev_enumerate->sysattr_match_list); + udev_list_cleanup(&udev_enumerate->sysattr_nomatch_list); + udev_list_cleanup(&udev_enumerate->subsystem_match_list); + udev_list_cleanup(&udev_enumerate->subsystem_nomatch_list); + udev_list_cleanup(&udev_enumerate->sysname_match_list); + udev_list_cleanup(&udev_enumerate->properties_match_list); + udev_list_cleanup(&udev_enumerate->tags_match_list); + udev_device_unref(udev_enumerate->parent_match); + udev_list_cleanup(&udev_enumerate->devices_list); + for (i = 0; i < udev_enumerate->devices_cur; i++) + free(udev_enumerate->devices[i].syspath); + free(udev_enumerate->devices); + free(udev_enumerate); +} + +/** + * udev_enumerate_get_udev: + * @udev_enumerate: context + * + * Returns: the udev library context. + */ +UDEV_EXPORT struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate) +{ + if (udev_enumerate == NULL) + return NULL; + return udev_enumerate->udev; +} + +static int syspath_add(struct udev_enumerate *udev_enumerate, const char *syspath) +{ + char *path; + struct syspath *entry; + + /* double array size if needed */ + if (udev_enumerate->devices_cur >= udev_enumerate->devices_max) { + struct syspath *buf; + unsigned int add; + + add = udev_enumerate->devices_max; + if (add < 1024) + add = 1024; + buf = realloc(udev_enumerate->devices, (udev_enumerate->devices_max + add) * sizeof(struct syspath)); + if (buf == NULL) + return -ENOMEM; + udev_enumerate->devices = buf; + udev_enumerate->devices_max += add; + } + + path = strdup(syspath); + if (path == NULL) + return -ENOMEM; + entry = &udev_enumerate->devices[udev_enumerate->devices_cur]; + entry->syspath = path; + entry->len = strlen(path); + udev_enumerate->devices_cur++; + udev_enumerate->devices_uptodate = false; + return 0; +} + +static int syspath_cmp(const void *p1, const void *p2) +{ + const struct syspath *path1 = p1; + const struct syspath *path2 = p2; + size_t len; + int ret; + + len = MIN(path1->len, path2->len); + ret = memcmp(path1->syspath, path2->syspath, len); + if (ret == 0) { + if (path1->len < path2->len) + ret = -1; + else if (path1->len > path2->len) + ret = 1; + } + return ret; +} + +/* For devices that should be moved to the absolute end of the list */ +static bool devices_delay_end(struct udev *udev, const char *syspath) +{ + static const char *delay_device_list[] = { + "/block/md", + "/block/dm-", + NULL + }; + size_t len; + int i; + + len = strlen(udev_get_sys_path(udev)); + for (i = 0; delay_device_list[i] != NULL; i++) { + if (strstr(&syspath[len], delay_device_list[i]) != NULL) { + dbg(udev, "delaying: %s\n", syspath); + return true; + } + } + return false; +} + +/* For devices that should just be moved a little bit later, just + * before the point where some common path prefix changes. Returns the + * number of characters that make up that common prefix */ +static size_t devices_delay_later(struct udev *udev, const char *syspath) +{ + const char *c; + + /* For sound cards the control device must be enumerated last + * to make sure it's the final device node that gets ACLs + * applied. Applications rely on this fact and use ACL changes + * on the control node as an indicator that the ACL change of + * the entire sound card completed. The kernel makes this + * guarantee when creating those devices, and hence we should + * too when enumerating them. */ + + if ((c = strstr(syspath, "/sound/card"))) { + c += 11; + c += strcspn(c, "/"); + + if (strncmp(c, "/controlC", 9) == 0) + return c - syspath + 1; + } + + return 0; +} + +/** + * udev_enumerate_get_list_entry: + * @udev_enumerate: context + * + * Returns: the first entry of the sorted list of device paths. + */ +UDEV_EXPORT struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate) +{ + if (udev_enumerate == NULL) + return NULL; + if (!udev_enumerate->devices_uptodate) { + unsigned int i; + unsigned int max; + struct syspath *prev = NULL, *move_later = NULL; + size_t move_later_prefix = 0; + + udev_list_cleanup(&udev_enumerate->devices_list); + qsort(udev_enumerate->devices, udev_enumerate->devices_cur, sizeof(struct syspath), syspath_cmp); + + max = udev_enumerate->devices_cur; + for (i = 0; i < max; i++) { + struct syspath *entry = &udev_enumerate->devices[i]; + + /* skip duplicated entries */ + if (prev != NULL && + entry->len == prev->len && + memcmp(entry->syspath, prev->syspath, entry->len) == 0) + continue; + prev = entry; + + /* skip to be delayed devices, and add them to the end of the list */ + if (devices_delay_end(udev_enumerate->udev, entry->syspath)) { + syspath_add(udev_enumerate, entry->syspath); + /* need to update prev here for the case realloc() gives a different address */ + prev = &udev_enumerate->devices[i]; + continue; + } + + /* skip to be delayed devices, and move the to + * the point where the prefix changes. We can + * only move one item at a time. */ + if (!move_later) { + move_later_prefix = devices_delay_later(udev_enumerate->udev, entry->syspath); + + if (move_later_prefix > 0) { + move_later = entry; + continue; + } + } + + if (move_later && + strncmp(entry->syspath, move_later->syspath, move_later_prefix) != 0) { + + udev_list_entry_add(&udev_enumerate->devices_list, move_later->syspath, NULL); + move_later = NULL; + } + + udev_list_entry_add(&udev_enumerate->devices_list, entry->syspath, NULL); + } + + if (move_later) + udev_list_entry_add(&udev_enumerate->devices_list, move_later->syspath, NULL); + + /* add and cleanup delayed devices from end of list */ + for (i = max; i < udev_enumerate->devices_cur; i++) { + struct syspath *entry = &udev_enumerate->devices[i]; + + udev_list_entry_add(&udev_enumerate->devices_list, entry->syspath, NULL); + free(entry->syspath); + } + udev_enumerate->devices_cur = max; + + udev_enumerate->devices_uptodate = true; + } + return udev_list_get_entry(&udev_enumerate->devices_list); +} + +/** + * udev_enumerate_add_match_subsystem: + * @udev_enumerate: context + * @subsystem: filter for a subsystem of the device to include in the list + * + * Returns: 0 on success, otherwise a negative error value. + */ +UDEV_EXPORT int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) +{ + if (udev_enumerate == NULL) + return -EINVAL; + if (subsystem == NULL) + return 0; + if (udev_list_entry_add(&udev_enumerate->subsystem_match_list, subsystem, NULL) == NULL) + return -ENOMEM; + return 0; +} + +/** + * udev_enumerate_add_nomatch_subsystem: + * @udev_enumerate: context + * @subsystem: filter for a subsystem of the device to exclude from the list + * + * Returns: 0 on success, otherwise a negative error value. + */ +UDEV_EXPORT int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) +{ + if (udev_enumerate == NULL) + return -EINVAL; + if (subsystem == NULL) + return 0; + if (udev_list_entry_add(&udev_enumerate->subsystem_nomatch_list, subsystem, NULL) == NULL) + return -ENOMEM; + return 0; +} + +/** + * udev_enumerate_add_match_sysattr: + * @udev_enumerate: context + * @sysattr: filter for a sys attribute at the device to include in the list + * @value: optional value of the sys attribute + * + * Returns: 0 on success, otherwise a negative error value. + */ +UDEV_EXPORT int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value) +{ + if (udev_enumerate == NULL) + return -EINVAL; + if (sysattr == NULL) + return 0; + if (udev_list_entry_add(&udev_enumerate->sysattr_match_list, sysattr, value) == NULL) + return -ENOMEM; + return 0; +} + +/** + * udev_enumerate_add_nomatch_sysattr: + * @udev_enumerate: context + * @sysattr: filter for a sys attribute at the device to exclude from the list + * @value: optional value of the sys attribute + * + * Returns: 0 on success, otherwise a negative error value. + */ +UDEV_EXPORT int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value) +{ + if (udev_enumerate == NULL) + return -EINVAL; + if (sysattr == NULL) + return 0; + if (udev_list_entry_add(&udev_enumerate->sysattr_nomatch_list, sysattr, value) == NULL) + return -ENOMEM; + return 0; +} + +static int match_sysattr_value(struct udev_device *dev, const char *sysattr, const char *match_val) +{ + const char *val = NULL; + bool match = false; + + val = udev_device_get_sysattr_value(dev, sysattr); + if (val == NULL) + goto exit; + if (match_val == NULL) { + match = true; + goto exit; + } + if (fnmatch(match_val, val, 0) == 0) { + match = true; + goto exit; + } +exit: + return match; +} + +/** + * udev_enumerate_add_match_property: + * @udev_enumerate: context + * @property: filter for a property of the device to include in the list + * @value: value of the property + * + * Returns: 0 on success, otherwise a negative error value. + */ +UDEV_EXPORT int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value) +{ + if (udev_enumerate == NULL) + return -EINVAL; + if (property == NULL) + return 0; + if (udev_list_entry_add(&udev_enumerate->properties_match_list, property, value) == NULL) + return -ENOMEM; + return 0; +} + +/** + * udev_enumerate_add_match_tag: + * @udev_enumerate: context + * @tag: filter for a tag of the device to include in the list + * + * Returns: 0 on success, otherwise a negative error value. + */ +UDEV_EXPORT int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag) +{ + if (udev_enumerate == NULL) + return -EINVAL; + if (tag == NULL) + return 0; + if (udev_list_entry_add(&udev_enumerate->tags_match_list, tag, NULL) == NULL) + return -ENOMEM; + return 0; +} + +/** + * udev_enumerate_add_match_parent: + * @udev_enumerate: context + * @parent: parent device where to start searching + * + * Return the devices on the subtree of one given device. The parent + * itself is included in the list. + * + * A reference for the device is held until the udev_enumerate context + * is cleaned up. + * + * Returns: 0 on success, otherwise a negative error value. + */ +UDEV_EXPORT int udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *parent) +{ + if (udev_enumerate == NULL) + return -EINVAL; + if (parent == NULL) + return 0; + if (udev_enumerate->parent_match != NULL) + udev_device_unref(udev_enumerate->parent_match); + udev_enumerate->parent_match = udev_device_ref(parent); + return 0; +} + +/** + * udev_enumerate_add_match_is_initialized: + * @udev_enumerate: context + * + * Match only devices which udev has set up already. This makes + * sure, that the device node permissions and context are properly set + * and that network devices are fully renamed. + * + * Usually, devices which are found in the kernel but not already + * handled by udev, have still pending events. Services should subscribe + * to monitor events and wait for these devices to become ready, instead + * of using uninitialized devices. + * + * For now, this will not affect devices which do not have a device node + * and are not network interfaces. + * + * Returns: 0 on success, otherwise a negative error value. + */ +UDEV_EXPORT int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate) +{ + if (udev_enumerate == NULL) + return -EINVAL; + udev_enumerate->match_is_initialized = true; + return 0; +} + +/** + * udev_enumerate_add_match_sysname: + * @udev_enumerate: context + * @sysname: filter for the name of the device to include in the list + * + * Returns: 0 on success, otherwise a negative error value. + */ +UDEV_EXPORT int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname) +{ + if (udev_enumerate == NULL) + return -EINVAL; + if (sysname == NULL) + return 0; + if (udev_list_entry_add(&udev_enumerate->sysname_match_list, sysname, NULL) == NULL) + return -ENOMEM; + return 0; +} + +static bool match_sysattr(struct udev_enumerate *udev_enumerate, struct udev_device *dev) +{ + struct udev_list_entry *list_entry; + + /* skip list */ + udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->sysattr_nomatch_list)) { + if (match_sysattr_value(dev, udev_list_entry_get_name(list_entry), + udev_list_entry_get_value(list_entry))) + return false; + } + /* include list */ + if (udev_list_get_entry(&udev_enumerate->sysattr_match_list) != NULL) { + udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->sysattr_match_list)) { + /* anything that does not match, will make it FALSE */ + if (!match_sysattr_value(dev, udev_list_entry_get_name(list_entry), + udev_list_entry_get_value(list_entry))) + return false; + } + return true; + } + return true; +} + +static bool match_property(struct udev_enumerate *udev_enumerate, struct udev_device *dev) +{ + struct udev_list_entry *list_entry; + bool match = false; + + /* no match always matches */ + if (udev_list_get_entry(&udev_enumerate->properties_match_list) == NULL) + return true; + + /* loop over matches */ + udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->properties_match_list)) { + const char *match_key = udev_list_entry_get_name(list_entry); + const char *match_value = udev_list_entry_get_value(list_entry); + struct udev_list_entry *property_entry; + + /* loop over device properties */ + udev_list_entry_foreach(property_entry, udev_device_get_properties_list_entry(dev)) { + const char *dev_key = udev_list_entry_get_name(property_entry); + const char *dev_value = udev_list_entry_get_value(property_entry); + + if (fnmatch(match_key, dev_key, 0) != 0) + continue; + if (match_value == NULL && dev_value == NULL) { + match = true; + goto out; + } + if (match_value == NULL || dev_value == NULL) + continue; + if (fnmatch(match_value, dev_value, 0) == 0) { + match = true; + goto out; + } + } + } +out: + return match; +} + +static bool match_tag(struct udev_enumerate *udev_enumerate, struct udev_device *dev) +{ + struct udev_list_entry *list_entry; + + /* no match always matches */ + if (udev_list_get_entry(&udev_enumerate->tags_match_list) == NULL) + return true; + + /* loop over matches */ + udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->tags_match_list)) + if (!udev_device_has_tag(dev, udev_list_entry_get_name(list_entry))) + return false; + + return true; +} + +static bool match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *dev) +{ + const char *parent; + + if (udev_enumerate->parent_match == NULL) + return true; + + parent = udev_device_get_devpath(udev_enumerate->parent_match); + return strncmp(parent, udev_device_get_devpath(dev), strlen(parent)) == 0; +} + +static bool match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname) +{ + struct udev_list_entry *list_entry; + + if (udev_list_get_entry(&udev_enumerate->sysname_match_list) == NULL) + return true; + + udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->sysname_match_list)) { + if (fnmatch(udev_list_entry_get_name(list_entry), sysname, 0) != 0) + continue; + return true; + } + return false; +} + +static int scan_dir_and_add_devices(struct udev_enumerate *udev_enumerate, + const char *basedir, const char *subdir1, const char *subdir2) +{ + struct udev *udev = udev_enumerate_get_udev(udev_enumerate); + char path[UTIL_PATH_SIZE]; + size_t l; + char *s; + DIR *dir; + struct dirent *dent; + + s = path; + l = util_strpcpyl(&s, sizeof(path), udev_get_sys_path(udev), "/", basedir, NULL); + if (subdir1 != NULL) + l = util_strpcpyl(&s, l, "/", subdir1, NULL); + if (subdir2 != NULL) + util_strpcpyl(&s, l, "/", subdir2, NULL); + dir = opendir(path); + if (dir == NULL) + return -ENOENT; + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + char syspath[UTIL_PATH_SIZE]; + struct udev_device *dev; + + if (dent->d_name[0] == '.') + continue; + + if (!match_sysname(udev_enumerate, dent->d_name)) + continue; + + util_strscpyl(syspath, sizeof(syspath), path, "/", dent->d_name, NULL); + dev = udev_device_new_from_syspath(udev_enumerate->udev, syspath); + if (dev == NULL) + continue; + + if (udev_enumerate->match_is_initialized) { + /* + * All devices with a device node or network interfaces + * possibly need udev to adjust the device node permission + * or context, or rename the interface before it can be + * reliably used from other processes. + * + * For now, we can only check these types of devices, we + * might not store a database, and have no way to find out + * for all other types of devices. + */ + if (!udev_device_get_is_initialized(dev) && + (major(udev_device_get_devnum(dev)) > 0 || udev_device_get_ifindex(dev) > 0)) + goto nomatch; + } + if (!match_parent(udev_enumerate, dev)) + goto nomatch; + if (!match_tag(udev_enumerate, dev)) + goto nomatch; + if (!match_property(udev_enumerate, dev)) + goto nomatch; + if (!match_sysattr(udev_enumerate, dev)) + goto nomatch; + + syspath_add(udev_enumerate, udev_device_get_syspath(dev)); +nomatch: + udev_device_unref(dev); + } + closedir(dir); + return 0; +} + +static bool match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) +{ + struct udev_list_entry *list_entry; + + udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->subsystem_nomatch_list)) { + if (fnmatch(udev_list_entry_get_name(list_entry), subsystem, 0) == 0) + return false; + } + if (udev_list_get_entry(&udev_enumerate->subsystem_match_list) != NULL) { + udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->subsystem_match_list)) { + if (fnmatch(udev_list_entry_get_name(list_entry), subsystem, 0) == 0) + return true; + } + return false; + } + return true; +} + +static int scan_dir(struct udev_enumerate *udev_enumerate, const char *basedir, const char *subdir, const char *subsystem) +{ + struct udev *udev = udev_enumerate_get_udev(udev_enumerate); + + char path[UTIL_PATH_SIZE]; + DIR *dir; + struct dirent *dent; + + util_strscpyl(path, sizeof(path), udev_get_sys_path(udev), "/", basedir, NULL); + dir = opendir(path); + if (dir == NULL) + return -1; + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + if (dent->d_name[0] == '.') + continue; + if (!match_subsystem(udev_enumerate, subsystem != NULL ? subsystem : dent->d_name)) + continue; + scan_dir_and_add_devices(udev_enumerate, basedir, dent->d_name, subdir); + } + closedir(dir); + return 0; +} + +/** + * udev_enumerate_add_syspath: + * @udev_enumerate: context + * @syspath: path of a device + * + * Add a device to the list of devices, to retrieve it back sorted in dependency order. + * + * Returns: 0 on success, otherwise a negative error value. + */ +UDEV_EXPORT int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath) +{ + struct udev_device *udev_device; + + if (udev_enumerate == NULL) + return -EINVAL; + if (syspath == NULL) + return 0; + /* resolve to real syspath */ + udev_device = udev_device_new_from_syspath(udev_enumerate->udev, syspath); + if (udev_device == NULL) + return -EINVAL; + syspath_add(udev_enumerate, udev_device_get_syspath(udev_device)); + udev_device_unref(udev_device); + return 0; +} + +static int scan_devices_tags(struct udev_enumerate *udev_enumerate) +{ + struct udev *udev = udev_enumerate_get_udev(udev_enumerate); + struct udev_list_entry *list_entry; + + /* scan only tagged devices, use tags reverse-index, instead of searching all devices in /sys */ + udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->tags_match_list)) { + DIR *dir; + struct dirent *dent; + char path[UTIL_PATH_SIZE]; + + util_strscpyl(path, sizeof(path), udev_get_run_path(udev), "/tags/", + udev_list_entry_get_name(list_entry), NULL); + dir = opendir(path); + if (dir == NULL) + continue; + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + struct udev_device *dev; + + if (dent->d_name[0] == '.') + continue; + + dev = udev_device_new_from_id_filename(udev_enumerate->udev, dent->d_name); + if (dev == NULL) + continue; + + if (!match_subsystem(udev_enumerate, udev_device_get_subsystem(dev))) + goto nomatch; + if (!match_sysname(udev_enumerate, udev_device_get_sysname(dev))) + goto nomatch; + if (!match_parent(udev_enumerate, dev)) + goto nomatch; + if (!match_property(udev_enumerate, dev)) + goto nomatch; + if (!match_sysattr(udev_enumerate, dev)) + goto nomatch; + + syspath_add(udev_enumerate, udev_device_get_syspath(dev)); +nomatch: + udev_device_unref(dev); + } + closedir(dir); + } + return 0; +} + +static int parent_add_child(struct udev_enumerate *enumerate, const char *path) +{ + struct udev_device *dev; + + dev = udev_device_new_from_syspath(enumerate->udev, path); + if (dev == NULL) + return -ENODEV; + + if (!match_subsystem(enumerate, udev_device_get_subsystem(dev))) + return 0; + if (!match_sysname(enumerate, udev_device_get_sysname(dev))) + return 0; + if (!match_property(enumerate, dev)) + return 0; + if (!match_sysattr(enumerate, dev)) + return 0; + + syspath_add(enumerate, udev_device_get_syspath(dev)); + udev_device_unref(dev); + return 1; +} + +static int parent_crawl_children(struct udev_enumerate *enumerate, const char *path, int maxdepth) +{ + DIR *d; + struct dirent *dent; + + d = opendir(path); + if (d == NULL) + return -errno; + + for (dent = readdir(d); dent != NULL; dent = readdir(d)) { + char *child; + + if (dent->d_name[0] == '.') + continue; + if (dent->d_type != DT_DIR) + continue; + if (asprintf(&child, "%s/%s", path, dent->d_name) < 0) + continue; + parent_add_child(enumerate, child); + if (maxdepth > 0) + parent_crawl_children(enumerate, child, maxdepth-1); + free(child); + } + + closedir(d); + return 0; +} + +static int scan_devices_children(struct udev_enumerate *enumerate) +{ + const char *path; + + path = udev_device_get_syspath(enumerate->parent_match); + parent_add_child(enumerate, path); + return parent_crawl_children(enumerate, path, 256); +} + +static int scan_devices_all(struct udev_enumerate *udev_enumerate) +{ + struct udev *udev = udev_enumerate_get_udev(udev_enumerate); + char base[UTIL_PATH_SIZE]; + struct stat statbuf; + + util_strscpyl(base, sizeof(base), udev_get_sys_path(udev), "/subsystem", NULL); + if (stat(base, &statbuf) == 0) { + /* we have /subsystem/, forget all the old stuff */ + dbg(udev, "searching '/subsystem/*/devices/*' dir\n"); + scan_dir(udev_enumerate, "subsystem", "devices", NULL); + } else { + dbg(udev, "searching '/bus/*/devices/*' dir\n"); + scan_dir(udev_enumerate, "bus", "devices", NULL); + dbg(udev, "searching '/class/*' dir\n"); + scan_dir(udev_enumerate, "class", NULL, NULL); + } + return 0; +} + +/** + * udev_enumerate_scan_devices: + * @udev_enumerate: udev enumeration context + * + * Returns: 0 on success, otherwise a negative error value. + **/ +UDEV_EXPORT int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate) +{ + if (udev_enumerate == NULL) + return -EINVAL; + + /* efficiently lookup tags only, we maintain a reverse-index */ + if (udev_list_get_entry(&udev_enumerate->tags_match_list) != NULL) + return scan_devices_tags(udev_enumerate); + + /* walk the subtree of one parent device only */ + if (udev_enumerate->parent_match != NULL) + return scan_devices_children(udev_enumerate); + + /* scan devices of all subsystems */ + return scan_devices_all(udev_enumerate); +} + +/** + * udev_enumerate_scan_subsystems: + * @udev_enumerate: udev enumeration context + * + * Returns: 0 on success, otherwise a negative error value. + **/ +UDEV_EXPORT int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate) +{ + struct udev *udev = udev_enumerate_get_udev(udev_enumerate); + char base[UTIL_PATH_SIZE]; + struct stat statbuf; + const char *subsysdir; + + if (udev_enumerate == NULL) + return -EINVAL; + + /* all kernel modules */ + if (match_subsystem(udev_enumerate, "module")) { + dbg(udev, "searching 'modules/*' dir\n"); + scan_dir_and_add_devices(udev_enumerate, "module", NULL, NULL); + } + + util_strscpyl(base, sizeof(base), udev_get_sys_path(udev), "/subsystem", NULL); + if (stat(base, &statbuf) == 0) + subsysdir = "subsystem"; + else + subsysdir = "bus"; + + /* all subsystems (only buses support coldplug) */ + if (match_subsystem(udev_enumerate, "subsystem")) { + dbg(udev, "searching '%s/*' dir\n", subsysdir); + scan_dir_and_add_devices(udev_enumerate, subsysdir, NULL, NULL); + } + + /* all subsystem drivers */ + if (match_subsystem(udev_enumerate, "drivers")) { + dbg(udev, "searching '%s/*/drivers/*' dir\n", subsysdir); + scan_dir(udev_enumerate, subsysdir, "drivers", "drivers"); + } + return 0; +} diff --git a/src/udev/libudev-list.c b/src/udev/libudev-list.c new file mode 100644 index 0000000000..4bdef35ae8 --- /dev/null +++ b/src/udev/libudev-list.c @@ -0,0 +1,344 @@ +/* + * libudev - interface to udev device information + * + * Copyright (C) 2008 Kay Sievers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#include "libudev.h" +#include "libudev-private.h" + +/** + * SECTION:libudev-list + * @short_description: list operation + * + * Libudev list operations. + */ + +/** + * udev_list_entry: + * + * Opaque object representing one entry in a list. An entry contains + * contains a name, and optionally a value. + */ +struct udev_list_entry { + struct udev_list_node node; + struct udev_list *list; + char *name; + char *value; + int num; +}; + +/* the list's head points to itself if empty */ +void udev_list_node_init(struct udev_list_node *list) +{ + list->next = list; + list->prev = list; +} + +int udev_list_node_is_empty(struct udev_list_node *list) +{ + return list->next == list; +} + +static void udev_list_node_insert_between(struct udev_list_node *new, + struct udev_list_node *prev, + struct udev_list_node *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +void udev_list_node_append(struct udev_list_node *new, struct udev_list_node *list) +{ + udev_list_node_insert_between(new, list->prev, list); +} + +void udev_list_node_remove(struct udev_list_node *entry) +{ + struct udev_list_node *prev = entry->prev; + struct udev_list_node *next = entry->next; + + next->prev = prev; + prev->next = next; + + entry->prev = NULL; + entry->next = NULL; +} + +/* return list entry which embeds this node */ +static struct udev_list_entry *list_node_to_entry(struct udev_list_node *node) +{ + char *list; + + list = (char *)node; + list -= offsetof(struct udev_list_entry, node); + return (struct udev_list_entry *)list; +} + +void udev_list_init(struct udev *udev, struct udev_list *list, bool unique) +{ + memset(list, 0x00, sizeof(struct udev_list)); + list->udev = udev; + list->unique = unique; + udev_list_node_init(&list->node); +} + +/* insert entry into a list as the last element */ +void udev_list_entry_append(struct udev_list_entry *new, struct udev_list *list) +{ + /* inserting before the list head make the node the last node in the list */ + udev_list_node_insert_between(&new->node, list->node.prev, &list->node); + new->list = list; +} + +/* insert entry into a list, before a given existing entry */ +void udev_list_entry_insert_before(struct udev_list_entry *new, struct udev_list_entry *entry) +{ + udev_list_node_insert_between(&new->node, entry->node.prev, &entry->node); + new->list = entry->list; +} + +/* binary search in sorted array */ +static int list_search(struct udev_list *list, const char *name) +{ + unsigned int first, last; + + first = 0; + last = list->entries_cur; + while (first < last) { + unsigned int i; + int cmp; + + i = (first + last)/2; + cmp = strcmp(name, list->entries[i]->name); + if (cmp < 0) + last = i; + else if (cmp > 0) + first = i+1; + else + return i; + } + + /* not found, return negative insertion-index+1 */ + return -(first+1); +} + +struct udev_list_entry *udev_list_entry_add(struct udev_list *list, const char *name, const char *value) +{ + struct udev_list_entry *entry; + int i = 0; + + if (list->unique) { + /* lookup existing name or insertion-index */ + i = list_search(list, name); + if (i >= 0) { + entry = list->entries[i]; + + dbg(list->udev, "'%s' is already in the list\n", name); + free(entry->value); + if (value == NULL) { + entry->value = NULL; + dbg(list->udev, "'%s' value unset\n", name); + return entry; + } + entry->value = strdup(value); + if (entry->value == NULL) + return NULL; + dbg(list->udev, "'%s' value replaced with '%s'\n", name, value); + return entry; + } + } + + /* add new name */ + entry = calloc(1, sizeof(struct udev_list_entry)); + if (entry == NULL) + return NULL; + entry->name = strdup(name); + if (entry->name == NULL) { + free(entry); + return NULL; + } + if (value != NULL) { + entry->value = strdup(value); + if (entry->value == NULL) { + free(entry->name); + free(entry); + return NULL; + } + } + + if (list->unique) { + /* allocate or enlarge sorted array if needed */ + if (list->entries_cur >= list->entries_max) { + unsigned int add; + + add = list->entries_max; + if (add < 1) + add = 64; + list->entries = realloc(list->entries, (list->entries_max + add) * sizeof(struct udev_list_entry *)); + if (list->entries == NULL) { + free(entry->name); + free(entry->value); + return NULL; + } + list->entries_max += add; + } + + /* the negative i returned the insertion index */ + i = (-i)-1; + + /* insert into sorted list */ + if ((unsigned int)i < list->entries_cur) + udev_list_entry_insert_before(entry, list->entries[i]); + else + udev_list_entry_append(entry, list); + + /* insert into sorted array */ + memmove(&list->entries[i+1], &list->entries[i], + (list->entries_cur - i) * sizeof(struct udev_list_entry *)); + list->entries[i] = entry; + list->entries_cur++; + } else { + udev_list_entry_append(entry, list); + } + + dbg(list->udev, "'%s=%s' added\n", entry->name, entry->value); + return entry; +} + +void udev_list_entry_delete(struct udev_list_entry *entry) +{ + if (entry->list->entries != NULL) { + int i; + struct udev_list *list = entry->list; + + /* remove entry from sorted array */ + i = list_search(list, entry->name); + if (i >= 0) { + memmove(&list->entries[i], &list->entries[i+1], + ((list->entries_cur-1) - i) * sizeof(struct udev_list_entry *)); + list->entries_cur--; + } + } + + udev_list_node_remove(&entry->node); + free(entry->name); + free(entry->value); + free(entry); +} + +void udev_list_cleanup(struct udev_list *list) +{ + struct udev_list_entry *entry_loop; + struct udev_list_entry *entry_tmp; + + free(list->entries); + list->entries = NULL; + list->entries_cur = 0; + list->entries_max = 0; + udev_list_entry_foreach_safe(entry_loop, entry_tmp, udev_list_get_entry(list)) + udev_list_entry_delete(entry_loop); +} + +struct udev_list_entry *udev_list_get_entry(struct udev_list *list) +{ + if (udev_list_node_is_empty(&list->node)) + return NULL; + return list_node_to_entry(list->node.next); +} + +/** + * udev_list_entry_get_next: + * @list_entry: current entry + * + * Returns: the next entry from the list, #NULL is no more entries are found. + */ +UDEV_EXPORT struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry) +{ + struct udev_list_node *next; + + if (list_entry == NULL) + return NULL; + next = list_entry->node.next; + /* empty list or no more entries */ + if (next == &list_entry->list->node) + return NULL; + return list_node_to_entry(next); +} + +/** + * udev_list_entry_get_by_name: + * @list_entry: current entry + * @name: name string to match + * + * Returns: the entry where @name matched, #NULL if no matching entry is found. + */ +UDEV_EXPORT struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name) +{ + int i; + + if (list_entry == NULL) + return NULL; + + if (!list_entry->list->unique) + return NULL; + + i = list_search(list_entry->list, name); + if (i < 0) + return NULL; + return list_entry->list->entries[i]; +} + +/** + * udev_list_entry_get_name: + * @list_entry: current entry + * + * Returns: the name string of this entry. + */ +UDEV_EXPORT const char *udev_list_entry_get_name(struct udev_list_entry *list_entry) +{ + if (list_entry == NULL) + return NULL; + return list_entry->name; +} + +/** + * udev_list_entry_get_value: + * @list_entry: current entry + * + * Returns: the value string of this entry. + */ +UDEV_EXPORT const char *udev_list_entry_get_value(struct udev_list_entry *list_entry) +{ + if (list_entry == NULL) + return NULL; + return list_entry->value; +} + +int udev_list_entry_get_num(struct udev_list_entry *list_entry) +{ + if (list_entry == NULL) + return -EINVAL; + return list_entry->num; +} + +void udev_list_entry_set_num(struct udev_list_entry *list_entry, int num) +{ + if (list_entry == NULL) + return; + list_entry->num = num; +} diff --git a/src/udev/libudev-monitor.c b/src/udev/libudev-monitor.c new file mode 100644 index 0000000000..77dc55572f --- /dev/null +++ b/src/udev/libudev-monitor.c @@ -0,0 +1,874 @@ +/* + * libudev - interface to udev device information + * + * Copyright (C) 2008-2010 Kay Sievers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libudev.h" +#include "libudev-private.h" + +/** + * SECTION:libudev-monitor + * @short_description: device event source + * + * Connects to a device event source. + */ + +/** + * udev_monitor: + * + * Opaque object handling an event source. + */ +struct udev_monitor { + struct udev *udev; + int refcount; + int sock; + struct sockaddr_nl snl; + struct sockaddr_nl snl_trusted_sender; + struct sockaddr_nl snl_destination; + struct sockaddr_un sun; + socklen_t addrlen; + struct udev_list filter_subsystem_list; + struct udev_list filter_tag_list; + bool bound; +}; + +enum udev_monitor_netlink_group { + UDEV_MONITOR_NONE, + UDEV_MONITOR_KERNEL, + UDEV_MONITOR_UDEV, +}; + +#define UDEV_MONITOR_MAGIC 0xfeedcafe +struct udev_monitor_netlink_header { + /* "libudev" prefix to distinguish libudev and kernel messages */ + char prefix[8]; + /* + * magic to protect against daemon <-> library message format mismatch + * used in the kernel from socket filter rules; needs to be stored in network order + */ + unsigned int magic; + /* total length of header structure known to the sender */ + unsigned int header_size; + /* properties string buffer */ + unsigned int properties_off; + unsigned int properties_len; + /* + * hashes of primary device properties strings, to let libudev subscribers + * use in-kernel socket filters; values need to be stored in network order + */ + unsigned int filter_subsystem_hash; + unsigned int filter_devtype_hash; + unsigned int filter_tag_bloom_hi; + unsigned int filter_tag_bloom_lo; +}; + +static struct udev_monitor *udev_monitor_new(struct udev *udev) +{ + struct udev_monitor *udev_monitor; + + udev_monitor = calloc(1, sizeof(struct udev_monitor)); + if (udev_monitor == NULL) + return NULL; + udev_monitor->refcount = 1; + udev_monitor->udev = udev; + udev_list_init(udev, &udev_monitor->filter_subsystem_list, false); + udev_list_init(udev, &udev_monitor->filter_tag_list, true); + return udev_monitor; +} + +/** + * udev_monitor_new_from_socket: + * @udev: udev library context + * @socket_path: unix socket path + * + * This function should not be used in any new application. The + * kernel's netlink socket multiplexes messages to all interested + * clients. Creating custom sockets from udev to applications + * should be avoided. + * + * Create a new udev monitor and connect to a specified socket. The + * path to a socket either points to an existing socket file, or if + * the socket path starts with a '@' character, an abstract namespace + * socket will be used. + * + * A socket file will not be created. If it does not already exist, + * it will fall-back and connect to an abstract namespace socket with + * the given path. The permissions adjustment of a socket file, as + * well as the later cleanup, needs to be done by the caller. + * + * The initial refcount is 1, and needs to be decremented to + * release the resources of the udev monitor. + * + * Returns: a new udev monitor, or #NULL, in case of an error + **/ +UDEV_EXPORT struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char *socket_path) +{ + struct udev_monitor *udev_monitor; + struct stat statbuf; + + if (udev == NULL) + return NULL; + if (socket_path == NULL) + return NULL; + udev_monitor = udev_monitor_new(udev); + if (udev_monitor == NULL) + return NULL; + + udev_monitor->sun.sun_family = AF_LOCAL; + if (socket_path[0] == '@') { + /* translate leading '@' to abstract namespace */ + util_strscpy(udev_monitor->sun.sun_path, sizeof(udev_monitor->sun.sun_path), socket_path); + udev_monitor->sun.sun_path[0] = '\0'; + udev_monitor->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(socket_path); + } else if (stat(socket_path, &statbuf) == 0 && S_ISSOCK(statbuf.st_mode)) { + /* existing socket file */ + util_strscpy(udev_monitor->sun.sun_path, sizeof(udev_monitor->sun.sun_path), socket_path); + udev_monitor->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(socket_path); + } else { + /* no socket file, assume abstract namespace socket */ + util_strscpy(&udev_monitor->sun.sun_path[1], sizeof(udev_monitor->sun.sun_path)-1, socket_path); + udev_monitor->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(socket_path)+1; + } + udev_monitor->sock = socket(AF_LOCAL, SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); + if (udev_monitor->sock == -1) { + err(udev, "error getting socket: %m\n"); + free(udev_monitor); + return NULL; + } + + dbg(udev, "monitor %p created with '%s'\n", udev_monitor, socket_path); + return udev_monitor; +} + +struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const char *name, int fd) +{ + struct udev_monitor *udev_monitor; + unsigned int group; + + if (udev == NULL) + return NULL; + + if (name == NULL) + group = UDEV_MONITOR_NONE; + else if (strcmp(name, "udev") == 0) + group = UDEV_MONITOR_UDEV; + else if (strcmp(name, "kernel") == 0) + group = UDEV_MONITOR_KERNEL; + else + return NULL; + + udev_monitor = udev_monitor_new(udev); + if (udev_monitor == NULL) + return NULL; + + if (fd < 0) { + udev_monitor->sock = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_KOBJECT_UEVENT); + if (udev_monitor->sock == -1) { + err(udev, "error getting socket: %m\n"); + free(udev_monitor); + return NULL; + } + } else { + udev_monitor->bound = true; + udev_monitor->sock = fd; + } + + udev_monitor->snl.nl_family = AF_NETLINK; + udev_monitor->snl.nl_groups = group; + + /* default destination for sending */ + udev_monitor->snl_destination.nl_family = AF_NETLINK; + udev_monitor->snl_destination.nl_groups = UDEV_MONITOR_UDEV; + + dbg(udev, "monitor %p created with NETLINK_KOBJECT_UEVENT (%u)\n", udev_monitor, group); + return udev_monitor; +} + +/** + * udev_monitor_new_from_netlink: + * @udev: udev library context + * @name: name of event source + * + * Create new udev monitor and connect to a specified event + * source. Valid sources identifiers are "udev" and "kernel". + * + * Applications should usually not connect directly to the + * "kernel" events, because the devices might not be useable + * at that time, before udev has configured them, and created + * device nodes. Accessing devices at the same time as udev, + * might result in unpredictable behavior. The "udev" events + * are sent out after udev has finished its event processing, + * all rules have been processed, and needed device nodes are + * created. + * + * The initial refcount is 1, and needs to be decremented to + * release the resources of the udev monitor. + * + * Returns: a new udev monitor, or #NULL, in case of an error + **/ +UDEV_EXPORT struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name) +{ + return udev_monitor_new_from_netlink_fd(udev, name, -1); +} + +static inline void bpf_stmt(struct sock_filter *inss, unsigned int *i, + unsigned short code, unsigned int data) +{ + struct sock_filter *ins = &inss[*i]; + + ins->code = code; + ins->k = data; + (*i)++; +} + +static inline void bpf_jmp(struct sock_filter *inss, unsigned int *i, + unsigned short code, unsigned int data, + unsigned short jt, unsigned short jf) +{ + struct sock_filter *ins = &inss[*i]; + + ins->code = code; + ins->jt = jt; + ins->jf = jf; + ins->k = data; + (*i)++; +} + +/** + * udev_monitor_filter_update: + * @udev_monitor: monitor + * + * Update the installed socket filter. This is only needed, + * if the filter was removed or changed. + * + * Returns: 0 on success, otherwise a negative error value. + */ +UDEV_EXPORT int udev_monitor_filter_update(struct udev_monitor *udev_monitor) +{ + struct sock_filter ins[512]; + struct sock_fprog filter; + unsigned int i; + struct udev_list_entry *list_entry; + int err; + + if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) == NULL && + udev_list_get_entry(&udev_monitor->filter_tag_list) == NULL) + return 0; + + memset(ins, 0x00, sizeof(ins)); + i = 0; + + /* load magic in A */ + bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, magic)); + /* jump if magic matches */ + bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, UDEV_MONITOR_MAGIC, 1, 0); + /* wrong magic, pass packet */ + bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff); + + if (udev_list_get_entry(&udev_monitor->filter_tag_list) != NULL) { + int tag_matches; + + /* count tag matches, to calculate end of tag match block */ + tag_matches = 0; + udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_tag_list)) + tag_matches++; + + /* add all tags matches */ + udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_tag_list)) { + uint64_t tag_bloom_bits = util_string_bloom64(udev_list_entry_get_name(list_entry)); + uint32_t tag_bloom_hi = tag_bloom_bits >> 32; + uint32_t tag_bloom_lo = tag_bloom_bits & 0xffffffff; + + /* load device bloom bits in A */ + bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_tag_bloom_hi)); + /* clear bits (tag bits & bloom bits) */ + bpf_stmt(ins, &i, BPF_ALU|BPF_AND|BPF_K, tag_bloom_hi); + /* jump to next tag if it does not match */ + bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, tag_bloom_hi, 0, 3); + + /* load device bloom bits in A */ + bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_tag_bloom_lo)); + /* clear bits (tag bits & bloom bits) */ + bpf_stmt(ins, &i, BPF_ALU|BPF_AND|BPF_K, tag_bloom_lo); + /* jump behind end of tag match block if tag matches */ + tag_matches--; + bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, tag_bloom_lo, 1 + (tag_matches * 6), 0); + } + + /* nothing matched, drop packet */ + bpf_stmt(ins, &i, BPF_RET|BPF_K, 0); + } + + /* add all subsystem matches */ + if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) != NULL) { + udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_subsystem_list)) { + unsigned int hash = util_string_hash32(udev_list_entry_get_name(list_entry)); + + /* load device subsystem value in A */ + bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_subsystem_hash)); + if (udev_list_entry_get_value(list_entry) == NULL) { + /* jump if subsystem does not match */ + bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1); + } else { + /* jump if subsystem does not match */ + bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 3); + + /* load device devtype value in A */ + bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_devtype_hash)); + /* jump if value does not match */ + hash = util_string_hash32(udev_list_entry_get_value(list_entry)); + bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1); + } + + /* matched, pass packet */ + bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff); + + if (i+1 >= ARRAY_SIZE(ins)) + return -1; + } + + /* nothing matched, drop packet */ + bpf_stmt(ins, &i, BPF_RET|BPF_K, 0); + } + + /* matched, pass packet */ + bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff); + + /* install filter */ + memset(&filter, 0x00, sizeof(filter)); + filter.len = i; + filter.filter = ins; + err = setsockopt(udev_monitor->sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)); + return err; +} + +int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct udev_monitor *sender) +{ + udev_monitor->snl_trusted_sender.nl_pid = sender->snl.nl_pid; + return 0; +} +/** + * udev_monitor_enable_receiving: + * @udev_monitor: the monitor which should receive events + * + * Binds the @udev_monitor socket to the event source. + * + * Returns: 0 on success, otherwise a negative error value. + */ +UDEV_EXPORT int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) +{ + int err = 0; + const int on = 1; + + if (udev_monitor->sun.sun_family != 0) { + if (!udev_monitor->bound) { + err = bind(udev_monitor->sock, + (struct sockaddr *)&udev_monitor->sun, udev_monitor->addrlen); + if (err == 0) + udev_monitor->bound = true; + } + } else if (udev_monitor->snl.nl_family != 0) { + udev_monitor_filter_update(udev_monitor); + if (!udev_monitor->bound) { + err = bind(udev_monitor->sock, + (struct sockaddr *)&udev_monitor->snl, sizeof(struct sockaddr_nl)); + if (err == 0) + udev_monitor->bound = true; + } + if (err == 0) { + struct sockaddr_nl snl; + socklen_t addrlen; + + /* + * get the address the kernel has assigned us + * it is usually, but not necessarily the pid + */ + addrlen = sizeof(struct sockaddr_nl); + err = getsockname(udev_monitor->sock, (struct sockaddr *)&snl, &addrlen); + if (err == 0) + udev_monitor->snl.nl_pid = snl.nl_pid; + } + } else { + return -EINVAL; + } + + if (err < 0) { + err(udev_monitor->udev, "bind failed: %m\n"); + return err; + } + + /* enable receiving of sender credentials */ + setsockopt(udev_monitor->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); + return 0; +} + +/** + * udev_monitor_set_receive_buffer_size: + * @udev_monitor: the monitor which should receive events + * @size: the size in bytes + * + * Set the size of the kernel socket buffer. This call needs the + * appropriate privileges to succeed. + * + * Returns: 0 on success, otherwise -1 on error. + */ +UDEV_EXPORT int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size) +{ + if (udev_monitor == NULL) + return -1; + return setsockopt(udev_monitor->sock, SOL_SOCKET, SO_RCVBUFFORCE, &size, sizeof(size)); +} + +int udev_monitor_disconnect(struct udev_monitor *udev_monitor) +{ + int err; + + err = close(udev_monitor->sock); + udev_monitor->sock = -1; + return err; +} + +/** + * udev_monitor_ref: + * @udev_monitor: udev monitor + * + * Take a reference of a udev monitor. + * + * Returns: the passed udev monitor + **/ +UDEV_EXPORT struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor) +{ + if (udev_monitor == NULL) + return NULL; + udev_monitor->refcount++; + return udev_monitor; +} + +/** + * udev_monitor_unref: + * @udev_monitor: udev monitor + * + * Drop a reference of a udev monitor. If the refcount reaches zero, + * the bound socket will be closed, and the resources of the monitor + * will be released. + * + **/ +UDEV_EXPORT void udev_monitor_unref(struct udev_monitor *udev_monitor) +{ + if (udev_monitor == NULL) + return; + udev_monitor->refcount--; + if (udev_monitor->refcount > 0) + return; + if (udev_monitor->sock >= 0) + close(udev_monitor->sock); + udev_list_cleanup(&udev_monitor->filter_subsystem_list); + udev_list_cleanup(&udev_monitor->filter_tag_list); + dbg(udev_monitor->udev, "monitor %p released\n", udev_monitor); + free(udev_monitor); +} + +/** + * udev_monitor_get_udev: + * @udev_monitor: udev monitor + * + * Retrieve the udev library context the monitor was created with. + * + * Returns: the udev library context + **/ +UDEV_EXPORT struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor) +{ + if (udev_monitor == NULL) + return NULL; + return udev_monitor->udev; +} + +/** + * udev_monitor_get_fd: + * @udev_monitor: udev monitor + * + * Retrieve the socket file descriptor associated with the monitor. + * + * Returns: the socket file descriptor + **/ +UDEV_EXPORT int udev_monitor_get_fd(struct udev_monitor *udev_monitor) +{ + if (udev_monitor == NULL) + return -1; + return udev_monitor->sock; +} + +static int passes_filter(struct udev_monitor *udev_monitor, struct udev_device *udev_device) +{ + struct udev_list_entry *list_entry; + + if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) == NULL) + goto tag; + udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_subsystem_list)) { + const char *subsys = udev_list_entry_get_name(list_entry); + const char *dsubsys = udev_device_get_subsystem(udev_device); + const char *devtype; + const char *ddevtype; + + if (strcmp(dsubsys, subsys) != 0) + continue; + + devtype = udev_list_entry_get_value(list_entry); + if (devtype == NULL) + goto tag; + ddevtype = udev_device_get_devtype(udev_device); + if (ddevtype == NULL) + continue; + if (strcmp(ddevtype, devtype) == 0) + goto tag; + } + return 0; + +tag: + if (udev_list_get_entry(&udev_monitor->filter_tag_list) == NULL) + return 1; + udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_tag_list)) { + const char *tag = udev_list_entry_get_name(list_entry); + + if (udev_device_has_tag(udev_device, tag)) + return 1; + } + return 0; +} + +/** + * udev_monitor_receive_device: + * @udev_monitor: udev monitor + * + * Receive data from the udev monitor socket, allocate a new udev + * device, fill in the received data, and return the device. + * + * Only socket connections with uid=0 are accepted. + * + * The initial refcount is 1, and needs to be decremented to + * release the resources of the udev device. + * + * Returns: a new udev device, or #NULL, in case of an error + **/ +UDEV_EXPORT struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor) +{ + struct udev_device *udev_device; + struct msghdr smsg; + struct iovec iov; + char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; + struct cmsghdr *cmsg; + struct sockaddr_nl snl; + struct ucred *cred; + char buf[8192]; + ssize_t buflen; + ssize_t bufpos; + struct udev_monitor_netlink_header *nlh; + +retry: + if (udev_monitor == NULL) + return NULL; + iov.iov_base = &buf; + iov.iov_len = sizeof(buf); + memset (&smsg, 0x00, sizeof(struct msghdr)); + smsg.msg_iov = &iov; + smsg.msg_iovlen = 1; + smsg.msg_control = cred_msg; + smsg.msg_controllen = sizeof(cred_msg); + + if (udev_monitor->snl.nl_family != 0) { + smsg.msg_name = &snl; + smsg.msg_namelen = sizeof(snl); + } + + buflen = recvmsg(udev_monitor->sock, &smsg, 0); + if (buflen < 0) { + if (errno != EINTR) + info(udev_monitor->udev, "unable to receive message\n"); + return NULL; + } + + if (buflen < 32 || (size_t)buflen >= sizeof(buf)) { + info(udev_monitor->udev, "invalid message length\n"); + return NULL; + } + + if (udev_monitor->snl.nl_family != 0) { + if (snl.nl_groups == 0) { + /* unicast message, check if we trust the sender */ + if (udev_monitor->snl_trusted_sender.nl_pid == 0 || + snl.nl_pid != udev_monitor->snl_trusted_sender.nl_pid) { + info(udev_monitor->udev, "unicast netlink message ignored\n"); + return NULL; + } + } else if (snl.nl_groups == UDEV_MONITOR_KERNEL) { + if (snl.nl_pid > 0) { + info(udev_monitor->udev, "multicast kernel netlink message from pid %d ignored\n", + snl.nl_pid); + return NULL; + } + } + } + + cmsg = CMSG_FIRSTHDR(&smsg); + if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { + info(udev_monitor->udev, "no sender credentials received, message ignored\n"); + return NULL; + } + + cred = (struct ucred *)CMSG_DATA(cmsg); + if (cred->uid != 0) { + info(udev_monitor->udev, "sender uid=%d, message ignored\n", cred->uid); + return NULL; + } + + if (memcmp(buf, "libudev", 8) == 0) { + /* udev message needs proper version magic */ + nlh = (struct udev_monitor_netlink_header *) buf; + if (nlh->magic != htonl(UDEV_MONITOR_MAGIC)) { + err(udev_monitor->udev, "unrecognized message signature (%x != %x)\n", + nlh->magic, htonl(UDEV_MONITOR_MAGIC)); + return NULL; + } + if (nlh->properties_off+32 > buflen) + return NULL; + bufpos = nlh->properties_off; + } else { + /* kernel message with header */ + bufpos = strlen(buf) + 1; + if ((size_t)bufpos < sizeof("a@/d") || bufpos >= buflen) { + info(udev_monitor->udev, "invalid message length\n"); + return NULL; + } + + /* check message header */ + if (strstr(buf, "@/") == NULL) { + info(udev_monitor->udev, "unrecognized message header\n"); + return NULL; + } + } + + udev_device = udev_device_new(udev_monitor->udev); + if (udev_device == NULL) + return NULL; + udev_device_set_info_loaded(udev_device); + + while (bufpos < buflen) { + char *key; + size_t keylen; + + key = &buf[bufpos]; + keylen = strlen(key); + if (keylen == 0) + break; + bufpos += keylen + 1; + udev_device_add_property_from_string_parse(udev_device, key); + } + + if (udev_device_add_property_from_string_parse_finish(udev_device) < 0) { + info(udev_monitor->udev, "missing values, invalid device\n"); + udev_device_unref(udev_device); + return NULL; + } + + /* skip device, if it does not pass the current filter */ + if (!passes_filter(udev_monitor, udev_device)) { + struct pollfd pfd[1]; + int rc; + + udev_device_unref(udev_device); + + /* if something is queued, get next device */ + pfd[0].fd = udev_monitor->sock; + pfd[0].events = POLLIN; + rc = poll(pfd, 1, 0); + if (rc > 0) + goto retry; + return NULL; + } + + return udev_device; +} + +int udev_monitor_send_device(struct udev_monitor *udev_monitor, + struct udev_monitor *destination, struct udev_device *udev_device) +{ + const char *buf; + ssize_t blen; + ssize_t count; + + blen = udev_device_get_properties_monitor_buf(udev_device, &buf); + if (blen < 32) + return -EINVAL; + + if (udev_monitor->sun.sun_family != 0) { + struct msghdr smsg; + struct iovec iov[2]; + const char *action; + char header[2048]; + char *s; + + /* header @ */ + action = udev_device_get_action(udev_device); + if (action == NULL) + return -EINVAL; + s = header; + if (util_strpcpyl(&s, sizeof(header), action, "@", udev_device_get_devpath(udev_device), NULL) == 0) + return -EINVAL; + iov[0].iov_base = header; + iov[0].iov_len = (s - header)+1; + + /* add properties list */ + iov[1].iov_base = (char *)buf; + iov[1].iov_len = blen; + + memset(&smsg, 0x00, sizeof(struct msghdr)); + smsg.msg_iov = iov; + smsg.msg_iovlen = 2; + smsg.msg_name = &udev_monitor->sun; + smsg.msg_namelen = udev_monitor->addrlen; + count = sendmsg(udev_monitor->sock, &smsg, 0); + info(udev_monitor->udev, "passed %zi bytes to socket monitor %p\n", count, udev_monitor); + return count; + } + + if (udev_monitor->snl.nl_family != 0) { + struct msghdr smsg; + struct iovec iov[2]; + const char *val; + struct udev_monitor_netlink_header nlh; + struct udev_list_entry *list_entry; + uint64_t tag_bloom_bits; + + /* add versioned header */ + memset(&nlh, 0x00, sizeof(struct udev_monitor_netlink_header)); + memcpy(nlh.prefix, "libudev", 8); + nlh.magic = htonl(UDEV_MONITOR_MAGIC); + nlh.header_size = sizeof(struct udev_monitor_netlink_header); + val = udev_device_get_subsystem(udev_device); + nlh.filter_subsystem_hash = htonl(util_string_hash32(val)); + val = udev_device_get_devtype(udev_device); + if (val != NULL) + nlh.filter_devtype_hash = htonl(util_string_hash32(val)); + iov[0].iov_base = &nlh; + iov[0].iov_len = sizeof(struct udev_monitor_netlink_header); + + /* add tag bloom filter */ + tag_bloom_bits = 0; + udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(udev_device)) + tag_bloom_bits |= util_string_bloom64(udev_list_entry_get_name(list_entry)); + if (tag_bloom_bits > 0) { + nlh.filter_tag_bloom_hi = htonl(tag_bloom_bits >> 32); + nlh.filter_tag_bloom_lo = htonl(tag_bloom_bits & 0xffffffff); + } + + /* add properties list */ + nlh.properties_off = iov[0].iov_len; + nlh.properties_len = blen; + iov[1].iov_base = (char *)buf; + iov[1].iov_len = blen; + + memset(&smsg, 0x00, sizeof(struct msghdr)); + smsg.msg_iov = iov; + smsg.msg_iovlen = 2; + /* + * Use custom address for target, or the default one. + * + * If we send to a multicast group, we will get + * ECONNREFUSED, which is expected. + */ + if (destination != NULL) + smsg.msg_name = &destination->snl; + else + smsg.msg_name = &udev_monitor->snl_destination; + smsg.msg_namelen = sizeof(struct sockaddr_nl); + count = sendmsg(udev_monitor->sock, &smsg, 0); + info(udev_monitor->udev, "passed %zi bytes to netlink monitor %p\n", count, udev_monitor); + return count; + } + + return -EINVAL; +} + +/** + * udev_monitor_filter_add_match_subsystem_devtype: + * @udev_monitor: the monitor + * @subsystem: the subsystem value to match the incoming devices against + * @devtype: the devtype value to match the incoming devices against + * + * This filter is efficiently executed inside the kernel, and libudev subscribers + * will usually not be woken up for devices which do not match. + * + * The filter must be installed before the monitor is switched to listening mode. + * + * Returns: 0 on success, otherwise a negative error value. + */ +UDEV_EXPORT int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype) +{ + if (udev_monitor == NULL) + return -EINVAL; + if (subsystem == NULL) + return -EINVAL; + if (udev_list_entry_add(&udev_monitor->filter_subsystem_list, subsystem, devtype) == NULL) + return -ENOMEM; + return 0; +} + +/** + * udev_monitor_filter_add_match_tag: + * @udev_monitor: the monitor + * @tag: the name of a tag + * + * This filter is efficiently executed inside the kernel, and libudev subscribers + * will usually not be woken up for devices which do not match. + * + * The filter must be installed before the monitor is switched to listening mode. + * + * Returns: 0 on success, otherwise a negative error value. + */ +UDEV_EXPORT int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag) +{ + if (udev_monitor == NULL) + return -EINVAL; + if (tag == NULL) + return -EINVAL; + if (udev_list_entry_add(&udev_monitor->filter_tag_list, tag, NULL) == NULL) + return -ENOMEM; + return 0; +} + +/** + * udev_monitor_filter_remove: + * @udev_monitor: monitor + * + * Remove all filters from monitor. + * + * Returns: 0 on success, otherwise a negative error value. + */ +UDEV_EXPORT int udev_monitor_filter_remove(struct udev_monitor *udev_monitor) +{ + static struct sock_fprog filter = { 0, NULL }; + + udev_list_cleanup(&udev_monitor->filter_subsystem_list); + return setsockopt(udev_monitor->sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)); +} diff --git a/src/udev/libudev-private.h b/src/udev/libudev-private.h new file mode 100644 index 0000000000..4bbd8422fb --- /dev/null +++ b/src/udev/libudev-private.h @@ -0,0 +1,213 @@ +/* + * libudev - interface to udev device information + * + * Copyright (C) 2008-2010 Kay Sievers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +#ifndef _LIBUDEV_PRIVATE_H_ +#define _LIBUDEV_PRIVATE_H_ + +#include +#include +#include +#include +#include "libudev.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#define READ_END 0 +#define WRITE_END 1 + +static inline void __attribute__((always_inline, format(printf, 2, 3))) +udev_log_null(struct udev *udev, const char *format, ...) {} + +#define udev_log_cond(udev, prio, arg...) \ + do { \ + if (udev_get_log_priority(udev) >= prio) \ + udev_log(udev, prio, __FILE__, __LINE__, __FUNCTION__, ## arg); \ + } while (0) + +#ifdef ENABLE_LOGGING +# ifdef ENABLE_DEBUG +# define dbg(udev, arg...) udev_log_cond(udev, LOG_DEBUG, ## arg) +# else +# define dbg(udev, arg...) udev_log_null(udev, ## arg) +# endif +# define info(udev, arg...) udev_log_cond(udev, LOG_INFO, ## arg) +# define err(udev, arg...) udev_log_cond(udev, LOG_ERR, ## arg) +#else +# define dbg(udev, arg...) udev_log_null(udev, ## arg) +# define info(udev, arg...) udev_log_null(udev, ## arg) +# define err(udev, arg...) udev_log_null(udev, ## arg) +#endif + +#define UDEV_EXPORT __attribute__ ((visibility("default"))) + +static inline void udev_log_init(const char *program_name) +{ + openlog(program_name, LOG_PID | LOG_CONS, LOG_DAEMON); +} + +static inline void udev_log_close(void) +{ + closelog(); +} + +/* libudev.c */ +void udev_log(struct udev *udev, + int priority, const char *file, int line, const char *fn, + const char *format, ...) + __attribute__((format(printf, 6, 7))); +int udev_get_rules_path(struct udev *udev, char **path[], unsigned long long *ts_usec[]); +struct udev_list_entry *udev_add_property(struct udev *udev, const char *key, const char *value); +struct udev_list_entry *udev_get_properties_list_entry(struct udev *udev); + +/* libudev-device.c */ +struct udev_device *udev_device_new(struct udev *udev); +struct udev_device *udev_device_new_from_id_filename(struct udev *udev, char *id); +mode_t udev_device_get_devnode_mode(struct udev_device *udev_device); +int udev_device_set_syspath(struct udev_device *udev_device, const char *syspath); +int udev_device_set_devnode(struct udev_device *udev_device, const char *devnode); +int udev_device_add_devlink(struct udev_device *udev_device, const char *devlink, int unique); +void udev_device_cleanup_devlinks_list(struct udev_device *udev_device); +struct udev_list_entry *udev_device_add_property(struct udev_device *udev_device, const char *key, const char *value); +void udev_device_add_property_from_string_parse(struct udev_device *udev_device, const char *property); +int udev_device_add_property_from_string_parse_finish(struct udev_device *udev_device); +char **udev_device_get_properties_envp(struct udev_device *udev_device); +ssize_t udev_device_get_properties_monitor_buf(struct udev_device *udev_device, const char **buf); +int udev_device_read_db(struct udev_device *udev_device, const char *dbfile); +int udev_device_read_uevent_file(struct udev_device *udev_device); +int udev_device_set_action(struct udev_device *udev_device, const char *action); +const char *udev_device_get_devpath_old(struct udev_device *udev_device); +const char *udev_device_get_id_filename(struct udev_device *udev_device); +void udev_device_set_is_initialized(struct udev_device *udev_device); +int udev_device_add_tag(struct udev_device *udev_device, const char *tag); +void udev_device_cleanup_tags_list(struct udev_device *udev_device); +unsigned long long udev_device_get_usec_initialized(struct udev_device *udev_device); +void udev_device_set_usec_initialized(struct udev_device *udev_device, unsigned long long usec_initialized); +int udev_device_get_devlink_priority(struct udev_device *udev_device); +int udev_device_set_devlink_priority(struct udev_device *udev_device, int prio); +int udev_device_get_watch_handle(struct udev_device *udev_device); +int udev_device_set_watch_handle(struct udev_device *udev_device, int handle); +int udev_device_get_ifindex(struct udev_device *udev_device); +void udev_device_set_info_loaded(struct udev_device *device); +bool udev_device_get_db_persist(struct udev_device *udev_device); +void udev_device_set_db_persist(struct udev_device *udev_device); + +/* libudev-device-private.c */ +int udev_device_update_db(struct udev_device *udev_device); +int udev_device_delete_db(struct udev_device *udev_device); +int udev_device_tag_index(struct udev_device *dev, struct udev_device *dev_old, bool add); + +/* libudev-monitor.c - netlink/unix socket communication */ +int udev_monitor_disconnect(struct udev_monitor *udev_monitor); +int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct udev_monitor *sender); +int udev_monitor_send_device(struct udev_monitor *udev_monitor, + struct udev_monitor *destination, struct udev_device *udev_device); +struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const char *name, int fd); + +/* libudev-list.c */ +struct udev_list_node { + struct udev_list_node *next, *prev; +}; +struct udev_list { + struct udev *udev; + struct udev_list_node node; + struct udev_list_entry **entries; + unsigned int entries_cur; + unsigned int entries_max; + bool unique; +}; +#define UDEV_LIST(list) struct udev_list_node list = { &(list), &(list) } +void udev_list_node_init(struct udev_list_node *list); +int udev_list_node_is_empty(struct udev_list_node *list); +void udev_list_node_append(struct udev_list_node *new, struct udev_list_node *list); +void udev_list_node_remove(struct udev_list_node *entry); +#define udev_list_node_foreach(node, list) \ + for (node = (list)->next; \ + node != list; \ + node = (node)->next) +#define udev_list_node_foreach_safe(node, tmp, list) \ + for (node = (list)->next, tmp = (node)->next; \ + node != list; \ + node = tmp, tmp = (tmp)->next) +void udev_list_init(struct udev *udev, struct udev_list *list, bool unique); +void udev_list_cleanup(struct udev_list *list); +struct udev_list_entry *udev_list_get_entry(struct udev_list *list); +struct udev_list_entry *udev_list_entry_add(struct udev_list *list, const char *name, const char *value); +void udev_list_entry_delete(struct udev_list_entry *entry); +void udev_list_entry_insert_before(struct udev_list_entry *new, struct udev_list_entry *entry); +void udev_list_entry_append(struct udev_list_entry *new, struct udev_list *list); +int udev_list_entry_get_num(struct udev_list_entry *list_entry); +void udev_list_entry_set_num(struct udev_list_entry *list_entry, int num); +#define udev_list_entry_foreach_safe(entry, tmp, first) \ + for (entry = first, tmp = udev_list_entry_get_next(entry); \ + entry != NULL; \ + entry = tmp, tmp = udev_list_entry_get_next(tmp)) + +/* libudev-queue.c */ +unsigned long long int udev_get_kernel_seqnum(struct udev *udev); +int udev_queue_read_seqnum(FILE *queue_file, unsigned long long int *seqnum); +ssize_t udev_queue_read_devpath(FILE *queue_file, char *devpath, size_t size); +ssize_t udev_queue_skip_devpath(FILE *queue_file); + +/* libudev-queue-private.c */ +struct udev_queue_export *udev_queue_export_new(struct udev *udev); +struct udev_queue_export *udev_queue_export_unref(struct udev_queue_export *udev_queue_export); +void udev_queue_export_cleanup(struct udev_queue_export *udev_queue_export); +int udev_queue_export_device_queued(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device); +int udev_queue_export_device_finished(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device); + +/* libudev-util.c */ +#define UTIL_PATH_SIZE 1024 +#define UTIL_NAME_SIZE 512 +#define UTIL_LINE_SIZE 16384 +#define UDEV_ALLOWED_CHARS_INPUT "/ $%?," +ssize_t util_get_sys_core_link_value(struct udev *udev, const char *slink, const char *syspath, char *value, size_t size); +int util_resolve_sys_link(struct udev *udev, char *syspath, size_t size); +int util_log_priority(const char *priority); +size_t util_path_encode(const char *src, char *dest, size_t size); +size_t util_path_decode(char *s); +void util_remove_trailing_chars(char *path, char c); +size_t util_strpcpy(char **dest, size_t size, const char *src); +size_t util_strpcpyl(char **dest, size_t size, const char *src, ...) __attribute__((sentinel)); +size_t util_strscpy(char *dest, size_t size, const char *src); +size_t util_strscpyl(char *dest, size_t size, const char *src, ...) __attribute__((sentinel)); +int util_replace_whitespace(const char *str, char *to, size_t len); +int util_replace_chars(char *str, const char *white); +unsigned int util_string_hash32(const char *key); +uint64_t util_string_bloom64(const char *str); + +/* libudev-util-private.c */ +int util_create_path(struct udev *udev, const char *path); +int util_create_path_selinux(struct udev *udev, const char *path); +int util_delete_path(struct udev *udev, const char *path); +uid_t util_lookup_user(struct udev *udev, const char *user); +gid_t util_lookup_group(struct udev *udev, const char *group); +int util_resolve_subsys_kernel(struct udev *udev, const char *string, + char *result, size_t maxsize, int read_value); +unsigned long long ts_usec(const struct timespec *ts); +unsigned long long now_usec(void); + +/* libudev-selinux-private.c */ +#ifndef HAVE_SELINUX +static inline void udev_selinux_init(struct udev *udev) {} +static inline void udev_selinux_exit(struct udev *udev) {} +static inline void udev_selinux_lsetfilecon(struct udev *udev, const char *file, unsigned int mode) {} +static inline void udev_selinux_setfscreatecon(struct udev *udev, const char *file, unsigned int mode) {} +static inline void udev_selinux_setfscreateconat(struct udev *udev, int dfd, const char *file, unsigned int mode) {} +static inline void udev_selinux_resetfscreatecon(struct udev *udev) {} +#else +void udev_selinux_init(struct udev *udev); +void udev_selinux_exit(struct udev *udev); +void udev_selinux_lsetfilecon(struct udev *udev, const char *file, unsigned int mode); +void udev_selinux_setfscreatecon(struct udev *udev, const char *file, unsigned int mode); +void udev_selinux_setfscreateconat(struct udev *udev, int dfd, const char *file, unsigned int mode); +void udev_selinux_resetfscreatecon(struct udev *udev); +#endif + +#endif diff --git a/src/udev/libudev-queue-private.c b/src/udev/libudev-queue-private.c new file mode 100644 index 0000000000..71771950aa --- /dev/null +++ b/src/udev/libudev-queue-private.c @@ -0,0 +1,412 @@ +/* + * libudev - interface to udev device information + * + * Copyright (C) 2008 Kay Sievers + * Copyright (C) 2009 Alan Jenkins + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +/* + * DISCLAIMER - The file format mentioned here is private to udev/libudev, + * and may be changed without notice. + * + * The udev event queue is exported as a binary log file. + * Each log record consists of a sequence number followed by the device path. + * + * When a new event is queued, its details are appended to the log. + * When the event finishes, a second record is appended to the log + * with the same sequence number but a devpath len of 0. + * + * Example: + * { 0x0000000000000001 } + * { 0x0000000000000001, 0x0019, "/devices/virtual/mem/null" }, + * { 0x0000000000000002, 0x001b, "/devices/virtual/mem/random" }, + * { 0x0000000000000001, 0x0000 }, + * { 0x0000000000000003, 0x0019, "/devices/virtual/mem/zero" }, + * + * Events 2 and 3 are still queued, but event 1 has finished. + * + * The queue does not grow indefinitely. It is periodically re-created + * to remove finished events. Atomic rename() makes this transparent to readers. + * + * The queue file starts with a single sequence number which specifies the + * minimum sequence number in the log that follows. Any events prior to this + * sequence number have already finished. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libudev.h" +#include "libudev-private.h" + +static int rebuild_queue_file(struct udev_queue_export *udev_queue_export); + +struct udev_queue_export { + struct udev *udev; + int queued_count; /* number of unfinished events exported in queue file */ + FILE *queue_file; + unsigned long long int seqnum_max; /* earliest sequence number in queue file */ + unsigned long long int seqnum_min; /* latest sequence number in queue file */ + int waste_bytes; /* queue file bytes wasted on finished events */ +}; + +struct udev_queue_export *udev_queue_export_new(struct udev *udev) +{ + struct udev_queue_export *udev_queue_export; + unsigned long long int initial_seqnum; + + if (udev == NULL) + return NULL; + + udev_queue_export = calloc(1, sizeof(struct udev_queue_export)); + if (udev_queue_export == NULL) + return NULL; + udev_queue_export->udev = udev; + + initial_seqnum = udev_get_kernel_seqnum(udev); + udev_queue_export->seqnum_min = initial_seqnum; + udev_queue_export->seqnum_max = initial_seqnum; + + udev_queue_export_cleanup(udev_queue_export); + if (rebuild_queue_file(udev_queue_export) != 0) { + free(udev_queue_export); + return NULL; + } + + return udev_queue_export; +} + +struct udev_queue_export *udev_queue_export_unref(struct udev_queue_export *udev_queue_export) +{ + if (udev_queue_export == NULL) + return NULL; + if (udev_queue_export->queue_file != NULL) + fclose(udev_queue_export->queue_file); + free(udev_queue_export); + return NULL; +} + +void udev_queue_export_cleanup(struct udev_queue_export *udev_queue_export) +{ + char filename[UTIL_PATH_SIZE]; + + if (udev_queue_export == NULL) + return; + util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_queue_export->udev), "/queue.tmp", NULL); + unlink(filename); + util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_queue_export->udev), "/queue.bin", NULL); + unlink(filename); +} + +static int skip_to(FILE *file, long offset) +{ + long old_offset; + + /* fseek may drop buffered data, avoid it for small seeks */ + old_offset = ftell(file); + if (offset > old_offset && offset - old_offset <= BUFSIZ) { + size_t skip_bytes = offset - old_offset; + char buf[skip_bytes]; + + if (fread(buf, skip_bytes, 1, file) != skip_bytes) + return -1; + } + + return fseek(file, offset, SEEK_SET); +} + +struct queue_devpaths { + unsigned int devpaths_first; /* index of first queued event */ + unsigned int devpaths_size; + long devpaths[]; /* seqnum -> offset of devpath in queue file (or 0) */ +}; + +/* + * Returns a table mapping seqnum to devpath file offset for currently queued events. + * devpaths[i] represents the event with seqnum = i + udev_queue_export->seqnum_min. + */ +static struct queue_devpaths *build_index(struct udev_queue_export *udev_queue_export) +{ + struct queue_devpaths *devpaths; + unsigned long long int range; + long devpath_offset; + ssize_t devpath_len; + unsigned long long int seqnum; + unsigned long long int n; + unsigned int i; + + /* seek to the first event in the file */ + rewind(udev_queue_export->queue_file); + udev_queue_read_seqnum(udev_queue_export->queue_file, &seqnum); + + /* allocate the table */ + range = udev_queue_export->seqnum_min - udev_queue_export->seqnum_max; + if (range - 1 > INT_MAX) { + err(udev_queue_export->udev, "queue file overflow\n"); + return NULL; + } + devpaths = calloc(1, sizeof(struct queue_devpaths) + (range + 1) * sizeof(long)); + if (devpaths == NULL) + return NULL; + devpaths->devpaths_size = range + 1; + + /* read all records and populate the table */ + for (;;) { + if (udev_queue_read_seqnum(udev_queue_export->queue_file, &seqnum) < 0) + break; + n = seqnum - udev_queue_export->seqnum_max; + if (n >= devpaths->devpaths_size) + goto read_error; + + devpath_offset = ftell(udev_queue_export->queue_file); + devpath_len = udev_queue_skip_devpath(udev_queue_export->queue_file); + if (devpath_len < 0) + goto read_error; + + if (devpath_len > 0) + devpaths->devpaths[n] = devpath_offset; + else + devpaths->devpaths[n] = 0; + } + + /* find first queued event */ + for (i = 0; i < devpaths->devpaths_size; i++) { + if (devpaths->devpaths[i] != 0) + break; + } + devpaths->devpaths_first = i; + + return devpaths; + +read_error: + err(udev_queue_export->udev, "queue file corrupted\n"); + free(devpaths); + return NULL; +} + +static int rebuild_queue_file(struct udev_queue_export *udev_queue_export) +{ + unsigned long long int seqnum; + struct queue_devpaths *devpaths = NULL; + char filename[UTIL_PATH_SIZE]; + char filename_tmp[UTIL_PATH_SIZE]; + FILE *new_queue_file = NULL; + unsigned int i; + + /* read old queue file */ + if (udev_queue_export->queue_file != NULL) { + dbg(udev_queue_export->udev, "compacting queue file, freeing %d bytes\n", + udev_queue_export->waste_bytes); + + devpaths = build_index(udev_queue_export); + if (devpaths != NULL) + udev_queue_export->seqnum_max += devpaths->devpaths_first; + } + if (devpaths == NULL) { + dbg(udev_queue_export->udev, "creating empty queue file\n"); + udev_queue_export->queued_count = 0; + udev_queue_export->seqnum_max = udev_queue_export->seqnum_min; + } + + /* create new queue file */ + util_strscpyl(filename_tmp, sizeof(filename_tmp), udev_get_run_path(udev_queue_export->udev), "/queue.tmp", NULL); + new_queue_file = fopen(filename_tmp, "w+"); + if (new_queue_file == NULL) + goto error; + seqnum = udev_queue_export->seqnum_max; + fwrite(&seqnum, 1, sizeof(unsigned long long int), new_queue_file); + + /* copy unfinished events only to the new file */ + if (devpaths != NULL) { + for (i = devpaths->devpaths_first; i < devpaths->devpaths_size; i++) { + char devpath[UTIL_PATH_SIZE]; + int err; + unsigned short devpath_len; + + if (devpaths->devpaths[i] != 0) + { + skip_to(udev_queue_export->queue_file, devpaths->devpaths[i]); + err = udev_queue_read_devpath(udev_queue_export->queue_file, devpath, sizeof(devpath)); + devpath_len = err; + + fwrite(&seqnum, sizeof(unsigned long long int), 1, new_queue_file); + fwrite(&devpath_len, sizeof(unsigned short), 1, new_queue_file); + fwrite(devpath, 1, devpath_len, new_queue_file); + } + seqnum++; + } + free(devpaths); + devpaths = NULL; + } + fflush(new_queue_file); + if (ferror(new_queue_file)) + goto error; + + /* rename the new file on top of the old one */ + util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_queue_export->udev), "/queue.bin", NULL); + if (rename(filename_tmp, filename) != 0) + goto error; + + if (udev_queue_export->queue_file != NULL) + fclose(udev_queue_export->queue_file); + udev_queue_export->queue_file = new_queue_file; + udev_queue_export->waste_bytes = 0; + + return 0; + +error: + err(udev_queue_export->udev, "failed to create queue file: %m\n"); + udev_queue_export_cleanup(udev_queue_export); + + if (udev_queue_export->queue_file != NULL) { + fclose(udev_queue_export->queue_file); + udev_queue_export->queue_file = NULL; + } + if (new_queue_file != NULL) + fclose(new_queue_file); + + if (devpaths != NULL) + free(devpaths); + udev_queue_export->queued_count = 0; + udev_queue_export->waste_bytes = 0; + udev_queue_export->seqnum_max = udev_queue_export->seqnum_min; + + return -1; +} + +static int write_queue_record(struct udev_queue_export *udev_queue_export, + unsigned long long int seqnum, const char *devpath, size_t devpath_len) +{ + unsigned short len; + + if (udev_queue_export->queue_file == NULL) { + dbg(udev_queue_export->udev, "can't record event: queue file not available\n"); + return -1; + } + + if (fwrite(&seqnum, sizeof(unsigned long long int), 1, udev_queue_export->queue_file) != 1) + goto write_error; + + len = (devpath_len < USHRT_MAX) ? devpath_len : USHRT_MAX; + if (fwrite(&len, sizeof(unsigned short), 1, udev_queue_export->queue_file) != 1) + goto write_error; + if (len > 0) { + if (fwrite(devpath, 1, len, udev_queue_export->queue_file) != len) + goto write_error; + } + + /* *must* flush output; caller may fork */ + if (fflush(udev_queue_export->queue_file) != 0) + goto write_error; + + return 0; + +write_error: + /* if we failed half way through writing a record to a file, + we should not try to write any further records to it. */ + err(udev_queue_export->udev, "error writing to queue file: %m\n"); + fclose(udev_queue_export->queue_file); + udev_queue_export->queue_file = NULL; + + return -1; +} + +enum device_state { + DEVICE_QUEUED, + DEVICE_FINISHED, +}; + +static inline size_t queue_record_size(size_t devpath_len) +{ + return sizeof(unsigned long long int) + sizeof(unsigned short int) + devpath_len; +} + +static int update_queue(struct udev_queue_export *udev_queue_export, + struct udev_device *udev_device, enum device_state state) +{ + unsigned long long int seqnum = udev_device_get_seqnum(udev_device); + const char *devpath = NULL; + size_t devpath_len = 0; + int bytes; + int err; + + /* FINISHED records have a zero length devpath */ + if (state == DEVICE_QUEUED) { + devpath = udev_device_get_devpath(udev_device); + devpath_len = strlen(devpath); + } + + /* recover from an earlier failed rebuild */ + if (udev_queue_export->queue_file == NULL) { + if (rebuild_queue_file(udev_queue_export) != 0) + return -1; + } + + /* if we're removing the last event from the queue, that's the best time to rebuild it */ + if (state != DEVICE_QUEUED && udev_queue_export->queued_count == 1) { + /* we don't need to read the old queue file */ + fclose(udev_queue_export->queue_file); + udev_queue_export->queue_file = NULL; + rebuild_queue_file(udev_queue_export); + return 0; + } + + /* try to rebuild the queue files before they grow larger than one page. */ + bytes = ftell(udev_queue_export->queue_file) + queue_record_size(devpath_len); + if ((udev_queue_export->waste_bytes > bytes / 2) && bytes > 4096) + rebuild_queue_file(udev_queue_export); + + /* don't record a finished event, if we already dropped the event in a failed rebuild */ + if (seqnum < udev_queue_export->seqnum_max) + return 0; + + /* now write to the queue */ + if (state == DEVICE_QUEUED) { + udev_queue_export->queued_count++; + udev_queue_export->seqnum_min = seqnum; + } else { + udev_queue_export->waste_bytes += queue_record_size(devpath_len) + queue_record_size(0); + udev_queue_export->queued_count--; + } + err = write_queue_record(udev_queue_export, seqnum, devpath, devpath_len); + + /* try to handle ENOSPC */ + if (err != 0 && udev_queue_export->queued_count == 0) { + udev_queue_export_cleanup(udev_queue_export); + err = rebuild_queue_file(udev_queue_export); + } + + return err; +} + +static int update(struct udev_queue_export *udev_queue_export, + struct udev_device *udev_device, enum device_state state) +{ + if (update_queue(udev_queue_export, udev_device, state) != 0) + return -1; + + return 0; +} + +int udev_queue_export_device_queued(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device) +{ + return update(udev_queue_export, udev_device, DEVICE_QUEUED); +} + +int udev_queue_export_device_finished(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device) +{ + return update(udev_queue_export, udev_device, DEVICE_FINISHED); +} diff --git a/src/udev/libudev-queue.c b/src/udev/libudev-queue.c new file mode 100644 index 0000000000..0e82cb6ae8 --- /dev/null +++ b/src/udev/libudev-queue.c @@ -0,0 +1,474 @@ +/* + * libudev - interface to udev device information + * + * Copyright (C) 2008 Kay Sievers + * Copyright (C) 2009 Alan Jenkins + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libudev.h" +#include "libudev-private.h" + +/** + * SECTION:libudev-queue + * @short_description: access to currently active events + * + * The udev daemon processes events asynchronously. All events which do not have + * interdependencies run in parallel. This exports the current state of the + * event processing queue, and the current event sequence numbers from the kernel + * and the udev daemon. + */ + +/** + * udev_queue: + * + * Opaque object representing the current event queue in the udev daemon. + */ +struct udev_queue { + struct udev *udev; + int refcount; + struct udev_list queue_list; +}; + +/** + * udev_queue_new: + * @udev: udev library context + * + * The initial refcount is 1, and needs to be decremented to + * release the resources of the udev queue context. + * + * Returns: the udev queue context, or #NULL on error. + **/ +UDEV_EXPORT struct udev_queue *udev_queue_new(struct udev *udev) +{ + struct udev_queue *udev_queue; + + if (udev == NULL) + return NULL; + + udev_queue = calloc(1, sizeof(struct udev_queue)); + if (udev_queue == NULL) + return NULL; + udev_queue->refcount = 1; + udev_queue->udev = udev; + udev_list_init(udev, &udev_queue->queue_list, false); + return udev_queue; +} + +/** + * udev_queue_ref: + * @udev_queue: udev queue context + * + * Take a reference of a udev queue context. + * + * Returns: the same udev queue context. + **/ +UDEV_EXPORT struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue) +{ + if (udev_queue == NULL) + return NULL; + udev_queue->refcount++; + return udev_queue; +} + +/** + * udev_queue_unref: + * @udev_queue: udev queue context + * + * Drop a reference of a udev queue context. If the refcount reaches zero, + * the resources of the queue context will be released. + **/ +UDEV_EXPORT void udev_queue_unref(struct udev_queue *udev_queue) +{ + if (udev_queue == NULL) + return; + udev_queue->refcount--; + if (udev_queue->refcount > 0) + return; + udev_list_cleanup(&udev_queue->queue_list); + free(udev_queue); +} + +/** + * udev_queue_get_udev: + * @udev_queue: udev queue context + * + * Retrieve the udev library context the queue context was created with. + * + * Returns: the udev library context. + **/ +UDEV_EXPORT struct udev *udev_queue_get_udev(struct udev_queue *udev_queue) +{ + if (udev_queue == NULL) + return NULL; + return udev_queue->udev; +} + +unsigned long long int udev_get_kernel_seqnum(struct udev *udev) +{ + char filename[UTIL_PATH_SIZE]; + unsigned long long int seqnum; + int fd; + char buf[32]; + ssize_t len; + + util_strscpyl(filename, sizeof(filename), udev_get_sys_path(udev), "/kernel/uevent_seqnum", NULL); + fd = open(filename, O_RDONLY|O_CLOEXEC); + if (fd < 0) + return 0; + len = read(fd, buf, sizeof(buf)); + close(fd); + if (len <= 2) + return 0; + buf[len-1] = '\0'; + seqnum = strtoull(buf, NULL, 10); + return seqnum; +} + +/** + * udev_queue_get_kernel_seqnum: + * @udev_queue: udev queue context + * + * Returns: the current kernel event sequence number. + **/ +UDEV_EXPORT unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue) +{ + unsigned long long int seqnum; + + if (udev_queue == NULL) + return -EINVAL; + + seqnum = udev_get_kernel_seqnum(udev_queue->udev); + dbg(udev_queue->udev, "seqnum=%llu\n", seqnum); + return seqnum; +} + +int udev_queue_read_seqnum(FILE *queue_file, unsigned long long int *seqnum) +{ + if (fread(seqnum, sizeof(unsigned long long int), 1, queue_file) != 1) + return -1; + + return 0; +} + +ssize_t udev_queue_skip_devpath(FILE *queue_file) +{ + unsigned short int len; + + if (fread(&len, sizeof(unsigned short int), 1, queue_file) == 1) { + char devpath[len]; + + /* use fread to skip, fseek might drop buffered data */ + if (fread(devpath, 1, len, queue_file) == len) + return len; + } + + return -1; +} + +ssize_t udev_queue_read_devpath(FILE *queue_file, char *devpath, size_t size) +{ + unsigned short int read_bytes = 0; + unsigned short int len; + + if (fread(&len, sizeof(unsigned short int), 1, queue_file) != 1) + return -1; + + read_bytes = (len < size - 1) ? len : size - 1; + if (fread(devpath, 1, read_bytes, queue_file) != read_bytes) + return -1; + devpath[read_bytes] = '\0'; + + /* if devpath was too long, skip unread characters */ + if (read_bytes != len) { + unsigned short int skip_bytes = len - read_bytes; + char buf[skip_bytes]; + + if (fread(buf, 1, skip_bytes, queue_file) != skip_bytes) + return -1; + } + + return read_bytes; +} + +static FILE *open_queue_file(struct udev_queue *udev_queue, unsigned long long int *seqnum_start) +{ + char filename[UTIL_PATH_SIZE]; + FILE *queue_file; + + util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_queue->udev), "/queue.bin", NULL); + queue_file = fopen(filename, "re"); + if (queue_file == NULL) + return NULL; + + if (udev_queue_read_seqnum(queue_file, seqnum_start) < 0) { + err(udev_queue->udev, "corrupt queue file\n"); + fclose(queue_file); + return NULL; + } + + return queue_file; +} + +/** + * udev_queue_get_udev_seqnum: + * @udev_queue: udev queue context + * + * Returns: the last known udev event sequence number. + **/ +UDEV_EXPORT unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue) +{ + unsigned long long int seqnum_udev; + FILE *queue_file; + + queue_file = open_queue_file(udev_queue, &seqnum_udev); + if (queue_file == NULL) + return 0; + + for (;;) { + unsigned long long int seqnum; + ssize_t devpath_len; + + if (udev_queue_read_seqnum(queue_file, &seqnum) < 0) + break; + devpath_len = udev_queue_skip_devpath(queue_file); + if (devpath_len < 0) + break; + if (devpath_len > 0) + seqnum_udev = seqnum; + } + + fclose(queue_file); + return seqnum_udev; +} + +/** + * udev_queue_get_udev_is_active: + * @udev_queue: udev queue context + * + * Returns: a flag indicating if udev is active. + **/ +UDEV_EXPORT int udev_queue_get_udev_is_active(struct udev_queue *udev_queue) +{ + unsigned long long int seqnum_start; + FILE *queue_file; + + queue_file = open_queue_file(udev_queue, &seqnum_start); + if (queue_file == NULL) + return 0; + + fclose(queue_file); + return 1; +} + +/** + * udev_queue_get_queue_is_empty: + * @udev_queue: udev queue context + * + * Returns: a flag indicating if udev is currently handling events. + **/ +UDEV_EXPORT int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue) +{ + unsigned long long int seqnum_kernel; + unsigned long long int seqnum_udev = 0; + int queued = 0; + int is_empty = 0; + FILE *queue_file; + + if (udev_queue == NULL) + return -EINVAL; + queue_file = open_queue_file(udev_queue, &seqnum_udev); + if (queue_file == NULL) + return 1; + + for (;;) { + unsigned long long int seqnum; + ssize_t devpath_len; + + if (udev_queue_read_seqnum(queue_file, &seqnum) < 0) + break; + devpath_len = udev_queue_skip_devpath(queue_file); + if (devpath_len < 0) + break; + + if (devpath_len > 0) { + queued++; + seqnum_udev = seqnum; + } else { + queued--; + } + } + + if (queued > 0) { + dbg(udev_queue->udev, "queue is not empty\n"); + goto out; + } + + seqnum_kernel = udev_queue_get_kernel_seqnum(udev_queue); + if (seqnum_udev < seqnum_kernel) { + dbg(udev_queue->udev, "queue is empty but kernel events still pending [%llu]<->[%llu]\n", + seqnum_kernel, seqnum_udev); + goto out; + } + + dbg(udev_queue->udev, "queue is empty\n"); + is_empty = 1; + +out: + fclose(queue_file); + return is_empty; +} + +/** + * udev_queue_get_seqnum_sequence_is_finished: + * @udev_queue: udev queue context + * @start: first event sequence number + * @end: last event sequence number + * + * Returns: a flag indicating if any of the sequence numbers in the given range is currently active. + **/ +UDEV_EXPORT int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue, + unsigned long long int start, unsigned long long int end) +{ + unsigned long long int seqnum; + ssize_t devpath_len; + int unfinished; + FILE *queue_file; + + if (udev_queue == NULL) + return -EINVAL; + queue_file = open_queue_file(udev_queue, &seqnum); + if (queue_file == NULL) + return 1; + if (start < seqnum) + start = seqnum; + if (start > end) { + fclose(queue_file); + return 1; + } + if (end - start > INT_MAX - 1) { + fclose(queue_file); + return -EOVERFLOW; + } + + /* + * we might start with 0, and handle the initial seqnum + * only when we find an entry in the queue file + **/ + unfinished = end - start; + + do { + if (udev_queue_read_seqnum(queue_file, &seqnum) < 0) + break; + devpath_len = udev_queue_skip_devpath(queue_file); + if (devpath_len < 0) + break; + + /* + * we might start with an empty or re-build queue file, where + * the initial seqnum is not recorded as finished + */ + if (start == seqnum && devpath_len > 0) + unfinished++; + + if (devpath_len == 0) { + if (seqnum >= start && seqnum <= end) + unfinished--; + } + } while (unfinished > 0); + + fclose(queue_file); + + return (unfinished == 0); +} + +/** + * udev_queue_get_seqnum_is_finished: + * @udev_queue: udev queue context + * @seqnum: sequence number + * + * Returns: a flag indicating if the given sequence number is currently active. + **/ +UDEV_EXPORT int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum) +{ + if (!udev_queue_get_seqnum_sequence_is_finished(udev_queue, seqnum, seqnum)) + return 0; + + dbg(udev_queue->udev, "seqnum: %llu finished\n", seqnum); + return 1; +} + +/** + * udev_queue_get_queued_list_entry: + * @udev_queue: udev queue context + * + * Returns: the first entry of the list of queued events. + **/ +UDEV_EXPORT struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue) +{ + unsigned long long int seqnum; + FILE *queue_file; + + if (udev_queue == NULL) + return NULL; + udev_list_cleanup(&udev_queue->queue_list); + + queue_file = open_queue_file(udev_queue, &seqnum); + if (queue_file == NULL) + return NULL; + + for (;;) { + char syspath[UTIL_PATH_SIZE]; + char *s; + size_t l; + ssize_t len; + char seqnum_str[32]; + struct udev_list_entry *list_entry; + + if (udev_queue_read_seqnum(queue_file, &seqnum) < 0) + break; + snprintf(seqnum_str, sizeof(seqnum_str), "%llu", seqnum); + + s = syspath; + l = util_strpcpyl(&s, sizeof(syspath), udev_get_sys_path(udev_queue->udev), NULL); + len = udev_queue_read_devpath(queue_file, s, l); + if (len < 0) + break; + + if (len > 0) { + udev_list_entry_add(&udev_queue->queue_list, syspath, seqnum_str); + } else { + udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_queue->queue_list)) { + if (strcmp(seqnum_str, udev_list_entry_get_value(list_entry)) == 0) { + udev_list_entry_delete(list_entry); + break; + } + } + } + } + fclose(queue_file); + + return udev_list_get_entry(&udev_queue->queue_list); +} + +struct udev_list_entry *udev_queue_get_failed_list_entry(struct udev_queue *udev_queue); +UDEV_EXPORT struct udev_list_entry *udev_queue_get_failed_list_entry(struct udev_queue *udev_queue) +{ + errno = ENOSYS; + return NULL; +} diff --git a/src/udev/libudev-selinux-private.c b/src/udev/libudev-selinux-private.c new file mode 100644 index 0000000000..0f2a617b18 --- /dev/null +++ b/src/udev/libudev-selinux-private.c @@ -0,0 +1,109 @@ +/* + * libudev - interface to udev device information + * + * Copyright (C) 2008 Kay Sievers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#include "libudev.h" +#include "libudev-private.h" + +static int selinux_enabled; +security_context_t selinux_prev_scontext; + +void udev_selinux_init(struct udev *udev) +{ + /* record the present security context */ + selinux_enabled = (is_selinux_enabled() > 0); + info(udev, "selinux=%i\n", selinux_enabled); + if (!selinux_enabled) + return; + matchpathcon_init_prefix(NULL, udev_get_dev_path(udev)); + if (getfscreatecon(&selinux_prev_scontext) < 0) { + err(udev, "getfscreatecon failed\n"); + selinux_prev_scontext = NULL; + } +} + +void udev_selinux_exit(struct udev *udev) +{ + if (!selinux_enabled) + return; + freecon(selinux_prev_scontext); + selinux_prev_scontext = NULL; +} + +void udev_selinux_lsetfilecon(struct udev *udev, const char *file, unsigned int mode) +{ + security_context_t scontext = NULL; + + if (!selinux_enabled) + return; + if (matchpathcon(file, mode, &scontext) < 0) { + err(udev, "matchpathcon(%s) failed\n", file); + return; + } + if (lsetfilecon(file, scontext) < 0) + err(udev, "setfilecon %s failed: %m\n", file); + freecon(scontext); +} + +void udev_selinux_setfscreatecon(struct udev *udev, const char *file, unsigned int mode) +{ + security_context_t scontext = NULL; + + if (!selinux_enabled) + return; + + if (matchpathcon(file, mode, &scontext) < 0) { + err(udev, "matchpathcon(%s) failed\n", file); + return; + } + if (setfscreatecon(scontext) < 0) + err(udev, "setfscreatecon %s failed: %m\n", file); + freecon(scontext); +} + +void udev_selinux_resetfscreatecon(struct udev *udev) +{ + if (!selinux_enabled) + return; + if (setfscreatecon(selinux_prev_scontext) < 0) + err(udev, "setfscreatecon failed: %m\n"); +} + +void udev_selinux_setfscreateconat(struct udev *udev, int dfd, const char *file, unsigned int mode) +{ + char filename[UTIL_PATH_SIZE]; + + if (!selinux_enabled) + return; + + /* resolve relative filename */ + if (file[0] != '/') { + char procfd[UTIL_PATH_SIZE]; + char target[UTIL_PATH_SIZE]; + ssize_t len; + + snprintf(procfd, sizeof(procfd), "/proc/%u/fd/%u", getpid(), dfd); + len = readlink(procfd, target, sizeof(target)); + if (len <= 0 || len == sizeof(target)) + return; + target[len] = '\0'; + + util_strscpyl(filename, sizeof(filename), target, "/", file, NULL); + file = filename; + } + udev_selinux_setfscreatecon(udev, file, mode); +} diff --git a/src/udev/libudev-util-private.c b/src/udev/libudev-util-private.c new file mode 100644 index 0000000000..08f0ba2228 --- /dev/null +++ b/src/udev/libudev-util-private.c @@ -0,0 +1,242 @@ +/* + * libudev - interface to udev device information + * + * Copyright (C) 2003-2009 Kay Sievers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libudev.h" +#include "libudev-private.h" + +static int create_path(struct udev *udev, const char *path, bool selinux) +{ + char p[UTIL_PATH_SIZE]; + char *pos; + struct stat stats; + int err; + + util_strscpy(p, sizeof(p), path); + pos = strrchr(p, '/'); + if (pos == NULL) + return 0; + while (pos != p && pos[-1] == '/') + pos--; + if (pos == p) + return 0; + pos[0] = '\0'; + + dbg(udev, "stat '%s'\n", p); + if (stat(p, &stats) == 0) { + if ((stats.st_mode & S_IFMT) == S_IFDIR) + return 0; + else + return -ENOTDIR; + } + + err = util_create_path(udev, p); + if (err != 0) + return err; + + dbg(udev, "mkdir '%s'\n", p); + if (selinux) + udev_selinux_setfscreatecon(udev, p, S_IFDIR|0755); + err = mkdir(p, 0755); + if (err != 0) { + err = -errno; + if (err == -EEXIST && stat(p, &stats) == 0) { + if ((stats.st_mode & S_IFMT) == S_IFDIR) + err = 0; + else + err = -ENOTDIR; + } + } + if (selinux) + udev_selinux_resetfscreatecon(udev); + return err; +} + +int util_create_path(struct udev *udev, const char *path) +{ + return create_path(udev, path, false); +} + +int util_create_path_selinux(struct udev *udev, const char *path) +{ + return create_path(udev, path, true); +} + +int util_delete_path(struct udev *udev, const char *path) +{ + char p[UTIL_PATH_SIZE]; + char *pos; + int err = 0; + + if (path[0] == '/') + while(path[1] == '/') + path++; + util_strscpy(p, sizeof(p), path); + pos = strrchr(p, '/'); + if (pos == p || pos == NULL) + return 0; + + for (;;) { + *pos = '\0'; + pos = strrchr(p, '/'); + + /* don't remove the last one */ + if ((pos == p) || (pos == NULL)) + break; + + err = rmdir(p); + if (err < 0) { + if (errno == ENOENT) + err = 0; + break; + } + } + return err; +} + +uid_t util_lookup_user(struct udev *udev, const char *user) +{ + char *endptr; + size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX); + char buf[buflen]; + struct passwd pwbuf; + struct passwd *pw; + uid_t uid; + + if (strcmp(user, "root") == 0) + return 0; + uid = strtoul(user, &endptr, 10); + if (endptr[0] == '\0') + return uid; + + errno = getpwnam_r(user, &pwbuf, buf, buflen, &pw); + if (pw != NULL) + return pw->pw_uid; + if (errno == 0 || errno == ENOENT || errno == ESRCH) + err(udev, "specified user '%s' unknown\n", user); + else + err(udev, "error resolving user '%s': %m\n", user); + return 0; +} + +gid_t util_lookup_group(struct udev *udev, const char *group) +{ + char *endptr; + size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX); + char *buf; + struct group grbuf; + struct group *gr; + gid_t gid = 0; + + if (strcmp(group, "root") == 0) + return 0; + gid = strtoul(group, &endptr, 10); + if (endptr[0] == '\0') + return gid; + buf = NULL; + gid = 0; + for (;;) { + char *newbuf; + + newbuf = realloc(buf, buflen); + if (!newbuf) + break; + buf = newbuf; + errno = getgrnam_r(group, &grbuf, buf, buflen, &gr); + if (gr != NULL) { + gid = gr->gr_gid; + } else if (errno == ERANGE) { + buflen *= 2; + continue; + } else if (errno == 0 || errno == ENOENT || errno == ESRCH) { + err(udev, "specified group '%s' unknown\n", group); + } else { + err(udev, "error resolving group '%s': %m\n", group); + } + break; + } + free(buf); + return gid; +} + +/* handle "[/]" format */ +int util_resolve_subsys_kernel(struct udev *udev, const char *string, + char *result, size_t maxsize, int read_value) +{ + char temp[UTIL_PATH_SIZE]; + char *subsys; + char *sysname; + struct udev_device *dev; + char *attr; + + if (string[0] != '[') + return -1; + + util_strscpy(temp, sizeof(temp), string); + + subsys = &temp[1]; + + sysname = strchr(subsys, '/'); + if (sysname == NULL) + return -1; + sysname[0] = '\0'; + sysname = &sysname[1]; + + attr = strchr(sysname, ']'); + if (attr == NULL) + return -1; + attr[0] = '\0'; + attr = &attr[1]; + if (attr[0] == '/') + attr = &attr[1]; + if (attr[0] == '\0') + attr = NULL; + + if (read_value && attr == NULL) + return -1; + + dev = udev_device_new_from_subsystem_sysname(udev, subsys, sysname); + if (dev == NULL) + return -1; + + if (read_value) { + const char *val; + + val = udev_device_get_sysattr_value(dev, attr); + if (val != NULL) + util_strscpy(result, maxsize, val); + else + result[0] = '\0'; + info(udev, "value '[%s/%s]%s' is '%s'\n", subsys, sysname, attr, result); + } else { + size_t l; + char *s; + + s = result; + l = util_strpcpyl(&s, maxsize, udev_device_get_syspath(dev), NULL); + if (attr != NULL) + util_strpcpyl(&s, l, "/", attr, NULL); + info(udev, "path '[%s/%s]%s' is '%s'\n", subsys, sysname, attr, result); + } + udev_device_unref(dev); + return 0; +} diff --git a/src/udev/libudev-util.c b/src/udev/libudev-util.c new file mode 100644 index 0000000000..7e345f0fb6 --- /dev/null +++ b/src/udev/libudev-util.c @@ -0,0 +1,570 @@ +/* + * libudev - interface to udev device information + * + * Copyright (C) 2008-2011 Kay Sievers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libudev.h" +#include "libudev-private.h" + +/** + * SECTION:libudev-util + * @short_description: utils + */ + +ssize_t util_get_sys_core_link_value(struct udev *udev, const char *slink, const char *syspath, char *value, size_t size) +{ + char path[UTIL_PATH_SIZE]; + char target[UTIL_PATH_SIZE]; + ssize_t len; + const char *pos; + + util_strscpyl(path, sizeof(path), syspath, "/", slink, NULL); + len = readlink(path, target, sizeof(target)); + if (len <= 0 || len == (ssize_t)sizeof(target)) + return -1; + target[len] = '\0'; + pos = strrchr(target, '/'); + if (pos == NULL) + return -1; + pos = &pos[1]; + dbg(udev, "resolved link to: '%s'\n", pos); + return util_strscpy(value, size, pos); +} + +int util_resolve_sys_link(struct udev *udev, char *syspath, size_t size) +{ + char link_target[UTIL_PATH_SIZE]; + + ssize_t len; + int i; + int back; + char *base = NULL; + + len = readlink(syspath, link_target, sizeof(link_target)); + if (len <= 0 || len == (ssize_t)sizeof(link_target)) + return -1; + link_target[len] = '\0'; + dbg(udev, "path link '%s' points to '%s'\n", syspath, link_target); + + for (back = 0; strncmp(&link_target[back * 3], "../", 3) == 0; back++) + ; + dbg(udev, "base '%s', tail '%s', back %i\n", syspath, &link_target[back * 3], back); + for (i = 0; i <= back; i++) { + base = strrchr(syspath, '/'); + if (base == NULL) + return -EINVAL; + base[0] = '\0'; + } + if (base == NULL) + return -EINVAL; + dbg(udev, "after moving back '%s'\n", syspath); + util_strscpyl(base, size - (base - syspath), "/", &link_target[back * 3], NULL); + return 0; +} + +int util_log_priority(const char *priority) +{ + char *endptr; + int prio; + + prio = strtol(priority, &endptr, 10); + if (endptr[0] == '\0' || isspace(endptr[0])) + return prio; + if (strncmp(priority, "err", 3) == 0) + return LOG_ERR; + if (strncmp(priority, "info", 4) == 0) + return LOG_INFO; + if (strncmp(priority, "debug", 5) == 0) + return LOG_DEBUG; + return 0; +} + +size_t util_path_encode(const char *src, char *dest, size_t size) +{ + size_t i, j; + + for (i = 0, j = 0; src[i] != '\0'; i++) { + if (src[i] == '/') { + if (j+4 >= size) { + j = 0; + break; + } + memcpy(&dest[j], "\\x2f", 4); + j += 4; + } else if (src[i] == '\\') { + if (j+4 >= size) { + j = 0; + break; + } + memcpy(&dest[j], "\\x5c", 4); + j += 4; + } else { + if (j+1 >= size) { + j = 0; + break; + } + dest[j] = src[i]; + j++; + } + } + dest[j] = '\0'; + return j; +} + +size_t util_path_decode(char *s) +{ + size_t i, j; + + for (i = 0, j = 0; s[i] != '\0'; j++) { + if (memcmp(&s[i], "\\x2f", 4) == 0) { + s[j] = '/'; + i += 4; + } else if (memcmp(&s[i], "\\x5c", 4) == 0) { + s[j] = '\\'; + i += 4; + } else { + s[j] = s[i]; + i++; + } + } + s[j] = '\0'; + return j; +} + +void util_remove_trailing_chars(char *path, char c) +{ + size_t len; + + if (path == NULL) + return; + len = strlen(path); + while (len > 0 && path[len-1] == c) + path[--len] = '\0'; +} + +/* + * Concatenates strings. In any case, terminates in _all_ cases with '\0' + * and moves the @dest pointer forward to the added '\0'. Returns the + * remaining size, and 0 if the string was truncated. + */ +size_t util_strpcpy(char **dest, size_t size, const char *src) +{ + size_t len; + + len = strlen(src); + if (len >= size) { + if (size > 1) + *dest = mempcpy(*dest, src, size-1); + size = 0; + *dest[0] = '\0'; + } else { + if (len > 0) { + *dest = mempcpy(*dest, src, len); + size -= len; + } + *dest[0] = '\0'; + } + return size; +} + +/* concatenates list of strings, moves dest forward */ +size_t util_strpcpyl(char **dest, size_t size, const char *src, ...) +{ + va_list va; + + va_start(va, src); + do { + size = util_strpcpy(dest, size, src); + src = va_arg(va, char *); + } while (src != NULL); + va_end(va); + + return size; +} + +/* copies string */ +size_t util_strscpy(char *dest, size_t size, const char *src) +{ + char *s; + + s = dest; + return util_strpcpy(&s, size, src); +} + +/* concatenates list of strings */ +size_t util_strscpyl(char *dest, size_t size, const char *src, ...) +{ + va_list va; + char *s; + + va_start(va, src); + s = dest; + do { + size = util_strpcpy(&s, size, src); + src = va_arg(va, char *); + } while (src != NULL); + va_end(va); + + return size; +} + +/* count of characters used to encode one unicode char */ +static int utf8_encoded_expected_len(const char *str) +{ + unsigned char c = (unsigned char)str[0]; + + if (c < 0x80) + return 1; + if ((c & 0xe0) == 0xc0) + return 2; + if ((c & 0xf0) == 0xe0) + return 3; + if ((c & 0xf8) == 0xf0) + return 4; + if ((c & 0xfc) == 0xf8) + return 5; + if ((c & 0xfe) == 0xfc) + return 6; + return 0; +} + +/* decode one unicode char */ +static int utf8_encoded_to_unichar(const char *str) +{ + int unichar; + int len; + int i; + + len = utf8_encoded_expected_len(str); + switch (len) { + case 1: + return (int)str[0]; + case 2: + unichar = str[0] & 0x1f; + break; + case 3: + unichar = (int)str[0] & 0x0f; + break; + case 4: + unichar = (int)str[0] & 0x07; + break; + case 5: + unichar = (int)str[0] & 0x03; + break; + case 6: + unichar = (int)str[0] & 0x01; + break; + default: + return -1; + } + + for (i = 1; i < len; i++) { + if (((int)str[i] & 0xc0) != 0x80) + return -1; + unichar <<= 6; + unichar |= (int)str[i] & 0x3f; + } + + return unichar; +} + +/* expected size used to encode one unicode char */ +static int utf8_unichar_to_encoded_len(int unichar) +{ + if (unichar < 0x80) + return 1; + if (unichar < 0x800) + return 2; + if (unichar < 0x10000) + return 3; + if (unichar < 0x200000) + return 4; + if (unichar < 0x4000000) + return 5; + return 6; +} + +/* check if unicode char has a valid numeric range */ +static int utf8_unichar_valid_range(int unichar) +{ + if (unichar > 0x10ffff) + return 0; + if ((unichar & 0xfffff800) == 0xd800) + return 0; + if ((unichar > 0xfdcf) && (unichar < 0xfdf0)) + return 0; + if ((unichar & 0xffff) == 0xffff) + return 0; + return 1; +} + +/* validate one encoded unicode char and return its length */ +static int utf8_encoded_valid_unichar(const char *str) +{ + int len; + int unichar; + int i; + + len = utf8_encoded_expected_len(str); + if (len == 0) + return -1; + + /* ascii is valid */ + if (len == 1) + return 1; + + /* check if expected encoded chars are available */ + for (i = 0; i < len; i++) + if ((str[i] & 0x80) != 0x80) + return -1; + + unichar = utf8_encoded_to_unichar(str); + + /* check if encoded length matches encoded value */ + if (utf8_unichar_to_encoded_len(unichar) != len) + return -1; + + /* check if value has valid range */ + if (!utf8_unichar_valid_range(unichar)) + return -1; + + return len; +} + +int util_replace_whitespace(const char *str, char *to, size_t len) +{ + size_t i, j; + + /* strip trailing whitespace */ + len = strnlen(str, len); + while (len && isspace(str[len-1])) + len--; + + /* strip leading whitespace */ + i = 0; + while (isspace(str[i]) && (i < len)) + i++; + + j = 0; + while (i < len) { + /* substitute multiple whitespace with a single '_' */ + if (isspace(str[i])) { + while (isspace(str[i])) + i++; + to[j++] = '_'; + } + to[j++] = str[i++]; + } + to[j] = '\0'; + return 0; +} + +static int is_whitelisted(char c, const char *white) +{ + if ((c >= '0' && c <= '9') || + (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || + strchr("#+-.:=@_", c) != NULL || + (white != NULL && strchr(white, c) != NULL)) + return 1; + return 0; +} + +/* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */ +int util_replace_chars(char *str, const char *white) +{ + size_t i = 0; + int replaced = 0; + + while (str[i] != '\0') { + int len; + + if (is_whitelisted(str[i], white)) { + i++; + continue; + } + + /* accept hex encoding */ + if (str[i] == '\\' && str[i+1] == 'x') { + i += 2; + continue; + } + + /* accept valid utf8 */ + len = utf8_encoded_valid_unichar(&str[i]); + if (len > 1) { + i += len; + continue; + } + + /* if space is allowed, replace whitespace with ordinary space */ + if (isspace(str[i]) && white != NULL && strchr(white, ' ') != NULL) { + str[i] = ' '; + i++; + replaced++; + continue; + } + + /* everything else is replaced with '_' */ + str[i] = '_'; + i++; + replaced++; + } + return replaced; +} + +/** + * udev_util_encode_string: + * @str: input string to be encoded + * @str_enc: output string to store the encoded input string + * @len: maximum size of the output string, which may be + * four times as long as the input string + * + * Encode all potentially unsafe characters of a string to the + * corresponding 2 char hex value prefixed by '\x'. + * + * Returns: 0 if the entire string was copied, non-zero otherwise. + **/ +UDEV_EXPORT int udev_util_encode_string(const char *str, char *str_enc, size_t len) +{ + size_t i, j; + + if (str == NULL || str_enc == NULL) + return -1; + + for (i = 0, j = 0; str[i] != '\0'; i++) { + int seqlen; + + seqlen = utf8_encoded_valid_unichar(&str[i]); + if (seqlen > 1) { + if (len-j < (size_t)seqlen) + goto err; + memcpy(&str_enc[j], &str[i], seqlen); + j += seqlen; + i += (seqlen-1); + } else if (str[i] == '\\' || !is_whitelisted(str[i], NULL)) { + if (len-j < 4) + goto err; + sprintf(&str_enc[j], "\\x%02x", (unsigned char) str[i]); + j += 4; + } else { + if (len-j < 1) + goto err; + str_enc[j] = str[i]; + j++; + } + } + if (len-j < 1) + goto err; + str_enc[j] = '\0'; + return 0; +err: + return -1; +} + +/* + * http://sites.google.com/site/murmurhash/ + * + * All code is released to the public domain. For business purposes, + * Murmurhash is under the MIT license. + * + */ +static unsigned int murmur_hash2(const char *key, int len, unsigned int seed) +{ + /* + * 'm' and 'r' are mixing constants generated offline. + * They're not really 'magic', they just happen to work well. + */ + const unsigned int m = 0x5bd1e995; + const int r = 24; + + /* initialize the hash to a 'random' value */ + unsigned int h = seed ^ len; + + /* mix 4 bytes at a time into the hash */ + const unsigned char * data = (const unsigned char *)key; + + while(len >= 4) { + unsigned int k = *(unsigned int *)data; + + k *= m; + k ^= k >> r; + k *= m; + h *= m; + h ^= k; + + data += 4; + len -= 4; + } + + /* handle the last few bytes of the input array */ + switch(len) { + case 3: + h ^= data[2] << 16; + case 2: + h ^= data[1] << 8; + case 1: + h ^= data[0]; + h *= m; + }; + + /* do a few final mixes of the hash to ensure the last few bytes are well-incorporated */ + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + return h; +} + +unsigned int util_string_hash32(const char *str) +{ + return murmur_hash2(str, strlen(str), 0); +} + +/* get a bunch of bit numbers out of the hash, and set the bits in our bit field */ +uint64_t util_string_bloom64(const char *str) +{ + uint64_t bits = 0; + unsigned int hash = util_string_hash32(str); + + bits |= 1LLU << (hash & 63); + bits |= 1LLU << ((hash >> 6) & 63); + bits |= 1LLU << ((hash >> 12) & 63); + bits |= 1LLU << ((hash >> 18) & 63); + return bits; +} + +#define USEC_PER_SEC 1000000ULL +#define NSEC_PER_USEC 1000ULL +unsigned long long ts_usec(const struct timespec *ts) +{ + return (unsigned long long) ts->tv_sec * USEC_PER_SEC + + (unsigned long long) ts->tv_nsec / NSEC_PER_USEC; +} + +unsigned long long now_usec(void) +{ + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) + return 0; + return ts_usec(&ts); +} diff --git a/src/udev/libudev.c b/src/udev/libudev.c new file mode 100644 index 0000000000..dcba15de41 --- /dev/null +++ b/src/udev/libudev.c @@ -0,0 +1,457 @@ +/* + * libudev - interface to udev device information + * + * Copyright (C) 2008-2010 Kay Sievers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libudev.h" +#include "libudev-private.h" + +/** + * SECTION:libudev + * @short_description: libudev context + * + * The context contains the default values read from the udev config file, + * and is passed to all library operations. + */ + +/** + * udev: + * + * Opaque object representing the library context. + */ +struct udev { + int refcount; + void (*log_fn)(struct udev *udev, + int priority, const char *file, int line, const char *fn, + const char *format, va_list args); + void *userdata; + char *sys_path; + char *dev_path; + char *rules_path[4]; + unsigned long long rules_path_ts[4]; + int rules_path_count; + char *run_path; + struct udev_list properties_list; + int log_priority; +}; + +void udev_log(struct udev *udev, + int priority, const char *file, int line, const char *fn, + const char *format, ...) +{ + va_list args; + + va_start(args, format); + udev->log_fn(udev, priority, file, line, fn, format, args); + va_end(args); +} + +static void log_stderr(struct udev *udev, + int priority, const char *file, int line, const char *fn, + const char *format, va_list args) +{ + fprintf(stderr, "libudev: %s: ", fn); + vfprintf(stderr, format, args); +} + +/** + * udev_get_userdata: + * @udev: udev library context + * + * Retrieve stored data pointer from library context. This might be useful + * to access from callbacks like a custom logging function. + * + * Returns: stored userdata + **/ +UDEV_EXPORT void *udev_get_userdata(struct udev *udev) +{ + if (udev == NULL) + return NULL; + return udev->userdata; +} + +/** + * udev_set_userdata: + * @udev: udev library context + * @userdata: data pointer + * + * Store custom @userdata in the library context. + **/ +UDEV_EXPORT void udev_set_userdata(struct udev *udev, void *userdata) +{ + if (udev == NULL) + return; + udev->userdata = userdata; +} + +static char *set_value(char **s, const char *v) +{ + free(*s); + *s = strdup(v); + util_remove_trailing_chars(*s, '/'); + return *s; +} + +/** + * udev_new: + * + * Create udev library context. This reads the udev configuration + * file, and fills in the default values. + * + * The initial refcount is 1, and needs to be decremented to + * release the resources of the udev library context. + * + * Returns: a new udev library context + **/ +UDEV_EXPORT struct udev *udev_new(void) +{ + struct udev *udev; + const char *env; + char *config_file = NULL; + FILE *f; + + udev = calloc(1, sizeof(struct udev)); + if (udev == NULL) + return NULL; + udev->refcount = 1; + udev->log_fn = log_stderr; + udev->log_priority = LOG_ERR; + udev_list_init(udev, &udev->properties_list, true); + + /* custom config file */ + env = getenv("UDEV_CONFIG_FILE"); + if (env != NULL) { + if (set_value(&config_file, env) == NULL) + goto err; + udev_add_property(udev, "UDEV_CONFIG_FILE", config_file); + } + + /* default config file */ + if (config_file == NULL) + config_file = strdup(SYSCONFDIR "/udev/udev.conf"); + if (config_file == NULL) + goto err; + + f = fopen(config_file, "re"); + if (f != NULL) { + char line[UTIL_LINE_SIZE]; + int line_nr = 0; + + while (fgets(line, sizeof(line), f)) { + size_t len; + char *key; + char *val; + + line_nr++; + + /* find key */ + key = line; + while (isspace(key[0])) + key++; + + /* comment or empty line */ + if (key[0] == '#' || key[0] == '\0') + continue; + + /* split key/value */ + val = strchr(key, '='); + if (val == NULL) { + err(udev, "missing = in '%s'[%i], skip line\n", config_file, line_nr); + continue; + } + val[0] = '\0'; + val++; + + /* find value */ + while (isspace(val[0])) + val++; + + /* terminate key */ + len = strlen(key); + if (len == 0) + continue; + while (isspace(key[len-1])) + len--; + key[len] = '\0'; + + /* terminate value */ + len = strlen(val); + if (len == 0) + continue; + while (isspace(val[len-1])) + len--; + val[len] = '\0'; + + if (len == 0) + continue; + + /* unquote */ + if (val[0] == '"' || val[0] == '\'') { + if (val[len-1] != val[0]) { + err(udev, "inconsistent quoting in '%s'[%i], skip line\n", config_file, line_nr); + continue; + } + val[len-1] = '\0'; + val++; + } + + if (strcmp(key, "udev_log") == 0) { + udev_set_log_priority(udev, util_log_priority(val)); + continue; + } + if (strcmp(key, "udev_root") == 0) { + set_value(&udev->dev_path, val); + continue; + } + if (strcmp(key, "udev_run") == 0) { + set_value(&udev->run_path, val); + continue; + } + if (strcmp(key, "udev_sys") == 0) { + set_value(&udev->sys_path, val); + continue; + } + if (strcmp(key, "udev_rules") == 0) { + set_value(&udev->rules_path[0], val); + udev->rules_path_count = 1; + continue; + } + } + fclose(f); + } + + /* environment overrides config */ + env = getenv("UDEV_LOG"); + if (env != NULL) + udev_set_log_priority(udev, util_log_priority(env)); + + /* set defaults */ + if (udev->dev_path == NULL) + if (set_value(&udev->dev_path, "/dev") == NULL) + goto err; + + if (udev->sys_path == NULL) + if (set_value(&udev->sys_path, "/sys") == NULL) + goto err; + + if (udev->run_path == NULL) + if (set_value(&udev->run_path, "/run/udev") == NULL) + goto err; + + if (udev->rules_path[0] == NULL) { + /* /usr/lib/udev -- system rules */ + udev->rules_path[0] = strdup(UDEVLIBEXECDIR "/rules.d"); + if (!udev->rules_path[0]) + goto err; + + /* /run/udev -- runtime rules */ + if (asprintf(&udev->rules_path[2], "%s/rules.d", udev->run_path) < 0) + goto err; + + /* /etc/udev -- local administration rules */ + udev->rules_path[1] = strdup(SYSCONFDIR "/udev/rules.d"); + if (!udev->rules_path[1]) + goto err; + + udev->rules_path_count = 3; + } + + dbg(udev, "context %p created\n", udev); + dbg(udev, "log_priority=%d\n", udev->log_priority); + dbg(udev, "config_file='%s'\n", config_file); + dbg(udev, "dev_path='%s'\n", udev->dev_path); + dbg(udev, "sys_path='%s'\n", udev->sys_path); + dbg(udev, "run_path='%s'\n", udev->run_path); + dbg(udev, "rules_path='%s':'%s':'%s'\n", udev->rules_path[0], udev->rules_path[1], udev->rules_path[2]); + free(config_file); + return udev; +err: + free(config_file); + err(udev, "context creation failed\n"); + udev_unref(udev); + return NULL; +} + +/** + * udev_ref: + * @udev: udev library context + * + * Take a reference of the udev library context. + * + * Returns: the passed udev library context + **/ +UDEV_EXPORT struct udev *udev_ref(struct udev *udev) +{ + if (udev == NULL) + return NULL; + udev->refcount++; + return udev; +} + +/** + * udev_unref: + * @udev: udev library context + * + * Drop a reference of the udev library context. If the refcount + * reaches zero, the resources of the context will be released. + * + **/ +UDEV_EXPORT void udev_unref(struct udev *udev) +{ + if (udev == NULL) + return; + udev->refcount--; + if (udev->refcount > 0) + return; + udev_list_cleanup(&udev->properties_list); + free(udev->dev_path); + free(udev->sys_path); + free(udev->rules_path[0]); + free(udev->rules_path[1]); + free(udev->rules_path[2]); + free(udev->run_path); + dbg(udev, "context %p released\n", udev); + free(udev); +} + +/** + * udev_set_log_fn: + * @udev: udev library context + * @log_fn: function to be called for logging messages + * + * The built-in logging writes to stderr. It can be + * overridden by a custom function, to plug log messages + * into the users' logging functionality. + * + **/ +UDEV_EXPORT void udev_set_log_fn(struct udev *udev, + void (*log_fn)(struct udev *udev, + int priority, const char *file, int line, const char *fn, + const char *format, va_list args)) +{ + udev->log_fn = log_fn; + info(udev, "custom logging function %p registered\n", log_fn); +} + +/** + * udev_get_log_priority: + * @udev: udev library context + * + * The initial logging priority is read from the udev config file + * at startup. + * + * Returns: the current logging priority + **/ +UDEV_EXPORT int udev_get_log_priority(struct udev *udev) +{ + return udev->log_priority; +} + +/** + * udev_set_log_priority: + * @udev: udev library context + * @priority: the new logging priority + * + * Set the current logging priority. The value controls which messages + * are logged. + **/ +UDEV_EXPORT void udev_set_log_priority(struct udev *udev, int priority) +{ + char num[32]; + + udev->log_priority = priority; + snprintf(num, sizeof(num), "%u", udev->log_priority); + udev_add_property(udev, "UDEV_LOG", num); +} + +int udev_get_rules_path(struct udev *udev, char **path[], unsigned long long *stamp_usec[]) +{ + *path = udev->rules_path; + if (stamp_usec) + *stamp_usec = udev->rules_path_ts; + return udev->rules_path_count; +} + +/** + * udev_get_sys_path: + * @udev: udev library context + * + * Retrieve the sysfs mount point. The default is "/sys". For + * testing purposes, it can be overridden with udev_sys= + * in the udev configuration file. + * + * Returns: the sys mount point + **/ +UDEV_EXPORT const char *udev_get_sys_path(struct udev *udev) +{ + if (udev == NULL) + return NULL; + return udev->sys_path; +} + +/** + * udev_get_dev_path: + * @udev: udev library context + * + * Retrieve the device directory path. The default value is "/dev", + * the actual value may be overridden in the udev configuration + * file. + * + * Returns: the device directory path + **/ +UDEV_EXPORT const char *udev_get_dev_path(struct udev *udev) +{ + if (udev == NULL) + return NULL; + return udev->dev_path; +} + +/** + * udev_get_run_path: + * @udev: udev library context + * + * Retrieve the udev runtime directory path. The default is "/run/udev". + * + * Returns: the runtime directory path + **/ +UDEV_EXPORT const char *udev_get_run_path(struct udev *udev) +{ + if (udev == NULL) + return NULL; + return udev->run_path; +} + +struct udev_list_entry *udev_add_property(struct udev *udev, const char *key, const char *value) +{ + if (value == NULL) { + struct udev_list_entry *list_entry; + + list_entry = udev_get_properties_list_entry(udev); + list_entry = udev_list_entry_get_by_name(list_entry, key); + if (list_entry != NULL) + udev_list_entry_delete(list_entry); + return NULL; + } + return udev_list_entry_add(&udev->properties_list, key, value); +} + +struct udev_list_entry *udev_get_properties_list_entry(struct udev *udev) +{ + return udev_list_get_entry(&udev->properties_list); +} diff --git a/src/udev/libudev.h b/src/udev/libudev.h new file mode 100644 index 0000000000..10e098d4f7 --- /dev/null +++ b/src/udev/libudev.h @@ -0,0 +1,189 @@ +/* + * libudev - interface to udev device information + * + * Copyright (C) 2008-2011 Kay Sievers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +#ifndef _LIBUDEV_H_ +#define _LIBUDEV_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * udev - library context + * + * reads the udev config and system environment + * allows custom logging + */ +struct udev; +struct udev *udev_ref(struct udev *udev); +void udev_unref(struct udev *udev); +struct udev *udev_new(void); +void udev_set_log_fn(struct udev *udev, + void (*log_fn)(struct udev *udev, + int priority, const char *file, int line, const char *fn, + const char *format, va_list args)); +int udev_get_log_priority(struct udev *udev); +void udev_set_log_priority(struct udev *udev, int priority); +const char *udev_get_sys_path(struct udev *udev); +const char *udev_get_dev_path(struct udev *udev); +const char *udev_get_run_path(struct udev *udev); +void *udev_get_userdata(struct udev *udev); +void udev_set_userdata(struct udev *udev, void *userdata); + +/* + * udev_list + * + * access to libudev generated lists + */ +struct udev_list_entry; +struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry); +struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name); +const char *udev_list_entry_get_name(struct udev_list_entry *list_entry); +const char *udev_list_entry_get_value(struct udev_list_entry *list_entry); +/** + * udev_list_entry_foreach: + * @list_entry: entry to store the current position + * @first_entry: first entry to start with + * + * Helper to iterate over all entries of a list. + */ +#define udev_list_entry_foreach(list_entry, first_entry) \ + for (list_entry = first_entry; \ + list_entry != NULL; \ + list_entry = udev_list_entry_get_next(list_entry)) + +/* + * udev_device + * + * access to sysfs/kernel devices + */ +struct udev_device; +struct udev_device *udev_device_ref(struct udev_device *udev_device); +void udev_device_unref(struct udev_device *udev_device); +struct udev *udev_device_get_udev(struct udev_device *udev_device); +struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath); +struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum); +struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname); +struct udev_device *udev_device_new_from_environment(struct udev *udev); +/* udev_device_get_parent_*() does not take a reference on the returned device, it is automatically unref'd with the parent */ +struct udev_device *udev_device_get_parent(struct udev_device *udev_device); +struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_device *udev_device, + const char *subsystem, const char *devtype); +/* retrieve device properties */ +const char *udev_device_get_devpath(struct udev_device *udev_device); +const char *udev_device_get_subsystem(struct udev_device *udev_device); +const char *udev_device_get_devtype(struct udev_device *udev_device); +const char *udev_device_get_syspath(struct udev_device *udev_device); +const char *udev_device_get_sysname(struct udev_device *udev_device); +const char *udev_device_get_sysnum(struct udev_device *udev_device); +const char *udev_device_get_devnode(struct udev_device *udev_device); +int udev_device_get_is_initialized(struct udev_device *udev_device); +struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev_device *udev_device); +struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device *udev_device); +struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_device *udev_device); +struct udev_list_entry *udev_device_get_sysattr_list_entry(struct udev_device *udev_device); +const char *udev_device_get_property_value(struct udev_device *udev_device, const char *key); +const char *udev_device_get_driver(struct udev_device *udev_device); +dev_t udev_device_get_devnum(struct udev_device *udev_device); +const char *udev_device_get_action(struct udev_device *udev_device); +unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device); +unsigned long long int udev_device_get_usec_since_initialized(struct udev_device *udev_device); +const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr); +int udev_device_has_tag(struct udev_device *udev_device, const char *tag); + +/* + * udev_monitor + * + * access to kernel uevents and udev events + */ +struct udev_monitor; +struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor); +void udev_monitor_unref(struct udev_monitor *udev_monitor); +struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor); +/* kernel and udev generated events over netlink */ +struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name); +/* custom socket (use netlink and filters instead) */ +struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char *socket_path); +/* bind socket */ +int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor); +int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size); +int udev_monitor_get_fd(struct udev_monitor *udev_monitor); +struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor); +/* in-kernel socket filters to select messages that get delivered to a listener */ +int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, + const char *subsystem, const char *devtype); +int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag); +int udev_monitor_filter_update(struct udev_monitor *udev_monitor); +int udev_monitor_filter_remove(struct udev_monitor *udev_monitor); + +/* + * udev_enumerate + * + * search sysfs for specific devices and provide a sorted list + */ +struct udev_enumerate; +struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate); +void udev_enumerate_unref(struct udev_enumerate *udev_enumerate); +struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate); +struct udev_enumerate *udev_enumerate_new(struct udev *udev); +/* device properties filter */ +int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem); +int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem); +int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value); +int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value); +int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value); +int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname); +int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag); +int udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *parent); +int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate); +int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath); +/* run enumeration with active filters */ +int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate); +int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate); +/* return device list */ +struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate); + +/* + * udev_queue + * + * access to the currently running udev events + */ +struct udev_queue; +struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue); +void udev_queue_unref(struct udev_queue *udev_queue); +struct udev *udev_queue_get_udev(struct udev_queue *udev_queue); +struct udev_queue *udev_queue_new(struct udev *udev); +unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue); +unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue); +int udev_queue_get_udev_is_active(struct udev_queue *udev_queue); +int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue); +int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum); +int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue, + unsigned long long int start, unsigned long long int end); +struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue); + +/* + * udev_util + * + * udev specific utilities + */ +int udev_util_encode_string(const char *str, char *str_enc, size_t len); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/udev/libudev.pc.in b/src/udev/libudev.pc.in new file mode 100644 index 0000000000..c9a47fc9b8 --- /dev/null +++ b/src/udev/libudev.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libudev +Description: Library to access udev device information +Version: @VERSION@ +Libs: -L${libdir} -ludev -lrt +Libs.private: +Cflags: -I${includedir} diff --git a/src/udev/m4/.gitignore b/src/udev/m4/.gitignore deleted file mode 100644 index 0ca2c03722..0000000000 --- a/src/udev/m4/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -libtool.m4 -lt*m4 -gtk-doc.m4 - diff --git a/src/udev/mtd_probe/75-probe_mtd.rules b/src/udev/mtd_probe/75-probe_mtd.rules new file mode 100644 index 0000000000..c0e0839785 --- /dev/null +++ b/src/udev/mtd_probe/75-probe_mtd.rules @@ -0,0 +1,8 @@ +# do not edit this file, it will be overwritten on update + +ACTION!="add", GOTO="mtd_probe_end" + +KERNEL=="mtd*ro", IMPORT{program}="mtd_probe $devnode" +KERNEL=="mtd*ro", ENV{MTD_FTL}=="smartmedia", IMPORT{builtin}="kmod load sm_ftl" + +LABEL="mtd_probe_end" diff --git a/src/udev/mtd_probe/mtd_probe.c b/src/udev/mtd_probe/mtd_probe.c new file mode 100644 index 0000000000..1aa08d3851 --- /dev/null +++ b/src/udev/mtd_probe/mtd_probe.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010 - Maxim Levitsky + * + * mtd_probe is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mtd_probe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mtd_probe; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +#include "mtd_probe.h" +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + if (argc != 2) { + printf("usage: mtd_probe /dev/mtd[n]\n"); + return 1; + } + + int mtd_fd = open(argv[1], O_RDONLY); + if (mtd_fd == -1) { + perror("open"); + exit(-1); + } + + mtd_info_t mtd_info; + int error = ioctl(mtd_fd, MEMGETINFO, &mtd_info); + if (error == -1) { + perror("ioctl"); + exit(-1); + } + + probe_smart_media(mtd_fd, &mtd_info); + return -1; +} diff --git a/src/udev/mtd_probe/mtd_probe.h b/src/udev/mtd_probe/mtd_probe.h new file mode 100644 index 0000000000..2a37ede578 --- /dev/null +++ b/src/udev/mtd_probe/mtd_probe.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2010 - Maxim Levitsky + * + * mtd_probe is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mtd_probe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mtd_probe; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include + +/* Full oob structure as written on the flash */ +struct sm_oob { + uint32_t reserved; + uint8_t data_status; + uint8_t block_status; + uint8_t lba_copy1[2]; + uint8_t ecc2[3]; + uint8_t lba_copy2[2]; + uint8_t ecc1[3]; +} __attribute__((packed)); + + +/* one sector is always 512 bytes, but it can consist of two nand pages */ +#define SM_SECTOR_SIZE 512 + +/* oob area is also 16 bytes, but might be from two pages */ +#define SM_OOB_SIZE 16 + +/* This is maximum zone size, and all devices that have more that one zone + have this size */ +#define SM_MAX_ZONE_SIZE 1024 + +/* support for small page nand */ +#define SM_SMALL_PAGE 256 +#define SM_SMALL_OOB_SIZE 8 + + +void probe_smart_media(int mtd_fd, mtd_info_t *info); diff --git a/src/udev/mtd_probe/probe_smartmedia.c b/src/udev/mtd_probe/probe_smartmedia.c new file mode 100644 index 0000000000..b3cdefc633 --- /dev/null +++ b/src/udev/mtd_probe/probe_smartmedia.c @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2010 - Maxim Levitsky + * + * mtd_probe is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * mtd_probe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with mtd_probe; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mtd_probe.h" + +static const uint8_t cis_signature[] = { + 0x01, 0x03, 0xD9, 0x01, 0xFF, 0x18, 0x02, 0xDF, 0x01, 0x20 +}; + + +void probe_smart_media(int mtd_fd, mtd_info_t* info) +{ + char* cis_buffer = malloc(SM_SECTOR_SIZE); + + if (!cis_buffer) + return; + + if (info->type != MTD_NANDFLASH) + goto exit; + + int sector_size = info->writesize; + int block_size = info->erasesize; + int size_in_megs = info->size / (1024 * 1024); + int spare_count; + + + if (sector_size != SM_SECTOR_SIZE && sector_size != SM_SMALL_PAGE) + goto exit; + + switch(size_in_megs) { + case 1: + case 2: + spare_count = 6; + break; + case 4: + spare_count = 12; + break; + default: + spare_count = 24; + break; + } + + + int offset; + int cis_found = 0; + + for (offset = 0 ; offset < block_size * spare_count ; + offset += sector_size) { + + lseek(mtd_fd, SEEK_SET, offset); + if (read(mtd_fd, cis_buffer, SM_SECTOR_SIZE) == SM_SECTOR_SIZE){ + cis_found = 1; + break; + } + } + + if (!cis_found) + goto exit; + + if (memcmp(cis_buffer, cis_signature, sizeof(cis_signature)) != 0 && + (memcmp(cis_buffer + SM_SMALL_PAGE, cis_signature, + sizeof(cis_signature)) != 0)) + goto exit; + + printf("MTD_FTL=smartmedia\n"); + free(cis_buffer); + exit(0); +exit: + free(cis_buffer); + return; +} diff --git a/src/udev/rules/42-usb-hid-pm.rules b/src/udev/rules/42-usb-hid-pm.rules deleted file mode 100644 index d5d5897c31..0000000000 --- a/src/udev/rules/42-usb-hid-pm.rules +++ /dev/null @@ -1,49 +0,0 @@ -# -# Enable autosuspend for qemu emulated usb hid devices. -# -# Note that there are buggy qemu versions which advertise remote -# wakeup support but don't actually implement it correctly. This -# is the reason why we need a match for the serial number here. -# The serial number "42" is used to tag the implementations where -# remote wakeup is working. -# - -ACTION=="add", SUBSYSTEM=="usb", ATTR{product}=="QEMU USB Mouse", ATTR{serial}=="42", TEST=="power/control", ATTR{power/control}="auto" -ACTION=="add", SUBSYSTEM=="usb", ATTR{product}=="QEMU USB Tablet", ATTR{serial}=="42", TEST=="power/control", ATTR{power/control}="auto" -ACTION=="add", SUBSYSTEM=="usb", ATTR{product}=="QEMU USB Keyboard", ATTR{serial}=="42", TEST=="power/control", ATTR{power/control}="auto" - -# -# Enable autosuspend for KVM and iLO usb hid devices. These are -# effectively self-powered (despite what some claim in their USB -# profiles) and so it's safe to do so. -# - -# AMI 046b:ff10 -ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="046b", ATTR{idProduct}=="ff10", TEST=="power/control", ATTR{power/control}="auto" - -# -# Catch-all for Avocent HID devices. Keyed off interface in order to only -# trigger on HID class devices. -# -ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="0624", ATTR{bInterfaceClass}=="03", TEST=="../power/control", ATTR{../power/control}="auto" - -# Dell DRAC 4 -ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="413c", ATTR{idProduct}=="2500", TEST=="power/control", ATTR{power/control}="auto" - -# Dell DRAC 5 -ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="413c", ATTR{idProduct}=="0000", TEST=="power/control", ATTR{power/control}="auto" - -# Hewlett Packard iLO -ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="03f0", ATTR{idProduct}=="7029", TEST=="power/control", ATTR{power/control}="auto" -ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="03f0", ATTR{idProduct}=="1027", TEST=="power/control", ATTR{power/control}="auto" - -# IBM remote access -ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="04b3", ATTR{idProduct}=="4001", TEST=="power/control", ATTR{power/control}="auto" -ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="04b3", ATTR{idProduct}=="4002", TEST=="power/control", ATTR{power/control}="auto" -ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="04b3", ATTR{idProduct}=="4012", TEST=="power/control", ATTR{power/control}="auto" - -# Raritan Computer, Inc KVM. -ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="14dd", ATTR{idProduct}="0002", TEST=="power/control", ATTR{power/control}="auto" - -# USB HID devices that are internal to the machine should also be safe to autosuspend -ACTION=="add", SUBSYSTEM=="usb", ATTR{bInterfaceClass}=="03", ATTRS{removable}=="fixed", TEST=="../power/control", ATTR{../power/control}="auto" diff --git a/src/udev/rules/50-udev-default.rules b/src/udev/rules/50-udev-default.rules deleted file mode 100644 index 5ad787fc76..0000000000 --- a/src/udev/rules/50-udev-default.rules +++ /dev/null @@ -1,107 +0,0 @@ -# do not edit this file, it will be overwritten on update - -KERNEL=="pty[pqrstuvwxyzabcdef][0123456789abcdef]", GROUP="tty", MODE="0660" -KERNEL=="tty[pqrstuvwxyzabcdef][0123456789abcdef]", GROUP="tty", MODE="0660" -KERNEL=="ptmx", GROUP="tty", MODE="0666" -KERNEL=="tty", GROUP="tty", MODE="0666" -KERNEL=="tty[0-9]*", GROUP="tty", MODE="0620" -KERNEL=="vcs|vcs[0-9]*|vcsa|vcsa[0-9]*", GROUP="tty" - -# serial -KERNEL=="tty[A-Z]*[0-9]|pppox[0-9]*|ircomm[0-9]*|noz[0-9]*|rfcomm[0-9]*", GROUP="dialout" -KERNEL=="mwave", GROUP="dialout" -KERNEL=="hvc*|hvsi*", GROUP="dialout" - -# virtio serial / console ports -KERNEL=="vport*", ATTR{name}=="?*", SYMLINK+="virtio-ports/$attr{name}" - -# mem -KERNEL=="null|zero|full|random|urandom", MODE="0666" -KERNEL=="mem|kmem|port|nvram", GROUP="kmem", MODE="0640" - -# input -SUBSYSTEM=="input", ENV{ID_INPUT}=="", IMPORT{builtin}="input_id" -KERNEL=="mouse*|mice|event*", MODE="0640" -KERNEL=="ts[0-9]*|uinput", MODE="0640" -KERNEL=="js[0-9]*", MODE="0644" - -# video4linux -SUBSYSTEM=="video4linux", GROUP="video" -KERNEL=="vttuner*", GROUP="video" -KERNEL=="vtx*|vbi*", GROUP="video" -KERNEL=="winradio*", GROUP="video" - -# graphics -KERNEL=="agpgart", GROUP="video" -KERNEL=="pmu", GROUP="video" -KERNEL=="nvidia*|nvidiactl*", GROUP="video" -SUBSYSTEM=="graphics", GROUP="video" -SUBSYSTEM=="drm", GROUP="video" - -# sound -SUBSYSTEM=="sound", GROUP="audio", \ - OPTIONS+="static_node=snd/seq", OPTIONS+="static_node=snd/timer" - -# DVB (video) -SUBSYSTEM=="dvb", GROUP="video" - -# FireWire (firewire-core driver: IIDC devices, AV/C devices) -SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x00010*", GROUP="video" -SUBSYSTEM=="firewire", ATTR{units}=="*0x00b09d:0x00010*", GROUP="video" -SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x010001*", GROUP="video" -SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x014001*", GROUP="video" - -# 'libusb' device nodes -SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", MODE="0664" -SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", IMPORT{builtin}="usb_id" - -# printer -KERNEL=="parport[0-9]*", GROUP="lp" -SUBSYSTEM=="printer", KERNEL=="lp*", GROUP="lp" -SUBSYSTEM=="ppdev", GROUP="lp" -KERNEL=="lp[0-9]*", GROUP="lp" -KERNEL=="irlpt[0-9]*", GROUP="lp" -SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{ID_USB_INTERFACES}=="*:0701??:*", GROUP="lp" - -# block -SUBSYSTEM=="block", GROUP="disk" - -# floppy -SUBSYSTEM=="block", KERNEL=="fd[0-9]", GROUP="floppy" - -# cdrom -SUBSYSTEM=="block", KERNEL=="sr[0-9]*", GROUP="cdrom" -SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="4|5", GROUP="cdrom" -KERNEL=="pktcdvd[0-9]*", GROUP="cdrom" -KERNEL=="pktcdvd", GROUP="cdrom" - -# tape -KERNEL=="ht[0-9]*|nht[0-9]*", GROUP="tape" -KERNEL=="pt[0-9]*|npt[0-9]*|pht[0-9]*", GROUP="tape" -SUBSYSTEM=="scsi_generic|scsi_tape", SUBSYSTEMS=="scsi", ATTRS{type}=="1|8", GROUP="tape" - -# block-related -KERNEL=="sch[0-9]*", GROUP="disk" -SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="0", GROUP="disk" -KERNEL=="pg[0-9]*", GROUP="disk" -KERNEL=="qft[0-9]*|nqft[0-9]*|zqft[0-9]*|nzqft[0-9]*|rawqft[0-9]*|nrawqft[0-9]*", GROUP="disk" -KERNEL=="rawctl", GROUP="disk" -SUBSYSTEM=="raw", KERNEL=="raw[0-9]*", GROUP="disk" -SUBSYSTEM=="aoe", GROUP="disk", MODE="0220" -SUBSYSTEM=="aoe", KERNEL=="err", MODE="0440" - -# network -KERNEL=="tun", MODE="0666", OPTIONS+="static_node=net/tun" -KERNEL=="rfkill", MODE="0644" - -# CPU -KERNEL=="cpu[0-9]*", MODE="0444" - -KERNEL=="fuse", ACTION=="add", MODE="0666", OPTIONS+="static_node=fuse" - -SUBSYSTEM=="rtc", ATTR{hctosys}=="1", SYMLINK+="rtc" -KERNEL=="mmtimer", MODE="0644" -KERNEL=="rflash[0-9]*", MODE="0400" -KERNEL=="rrom[0-9]*", MODE="0400" - -SUBSYSTEM=="firmware", ACTION=="add", IMPORT{builtin}="firmware" diff --git a/src/udev/rules/60-persistent-alsa.rules b/src/udev/rules/60-persistent-alsa.rules deleted file mode 100644 index 8154e2dbb5..0000000000 --- a/src/udev/rules/60-persistent-alsa.rules +++ /dev/null @@ -1,14 +0,0 @@ -# do not edit this file, it will be overwritten on update - -ACTION=="remove", GOTO="persistent_alsa_end" -SUBSYSTEM!="sound", GOTO="persistent_alsa_end" -KERNEL!="controlC[0-9]*", GOTO="persistent_alsa_end" - -SUBSYSTEMS=="usb", ENV{ID_MODEL}=="", IMPORT{builtin}="usb_id" -ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="?*", SYMLINK+="snd/by-id/$env{ID_BUS}-$env{ID_SERIAL}-$env{ID_USB_INTERFACE_NUM}" -ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="", SYMLINK+="snd/by-id/$env{ID_BUS}-$env{ID_SERIAL}" - -IMPORT{builtin}="path_id" -ENV{ID_PATH}=="?*", SYMLINK+="snd/by-path/$env{ID_PATH}" - -LABEL="persistent_alsa_end" diff --git a/src/udev/rules/60-persistent-input.rules b/src/udev/rules/60-persistent-input.rules deleted file mode 100644 index fb798ddb05..0000000000 --- a/src/udev/rules/60-persistent-input.rules +++ /dev/null @@ -1,38 +0,0 @@ -# do not edit this file, it will be overwritten on update - -ACTION=="remove", GOTO="persistent_input_end" -SUBSYSTEM!="input", GOTO="persistent_input_end" -SUBSYSTEMS=="bluetooth", GOTO="persistent_input_end" - -SUBSYSTEMS=="usb", ENV{ID_BUS}=="", IMPORT{builtin}="usb_id" - -# determine class name for persistent symlinks -ENV{ID_INPUT_KEYBOARD}=="?*", ENV{.INPUT_CLASS}="kbd" -ENV{ID_INPUT_MOUSE}=="?*", ENV{.INPUT_CLASS}="mouse" -ENV{ID_INPUT_TOUCHPAD}=="?*", ENV{.INPUT_CLASS}="mouse" -ENV{ID_INPUT_TABLET}=="?*", ENV{.INPUT_CLASS}="mouse" -ENV{ID_INPUT_JOYSTICK}=="?*", ENV{.INPUT_CLASS}="joystick" -DRIVERS=="pcspkr", ENV{.INPUT_CLASS}="spkr" -ATTRS{name}=="*dvb*|*DVB*|* IR *", ENV{.INPUT_CLASS}="ir" - -# fill empty serial number -ENV{.INPUT_CLASS}=="?*", ENV{ID_SERIAL}=="", ENV{ID_SERIAL}="noserial" - -# by-id links -KERNEL=="mouse*|js*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-$env{.INPUT_CLASS}" -KERNEL=="mouse*|js*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", ATTRS{bInterfaceNumber}=="?*", ATTRS{bInterfaceNumber}!="00", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$attr{bInterfaceNumber}-$env{.INPUT_CLASS}" -KERNEL=="event*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-event-$env{.INPUT_CLASS}" -KERNEL=="event*", ENV{ID_BUS}=="?*", ENV{.INPUT_CLASS}=="?*", ATTRS{bInterfaceNumber}=="?*", ATTRS{bInterfaceNumber}!="00", SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$attr{bInterfaceNumber}-event-$env{.INPUT_CLASS}" -# allow empty class for USB devices, by appending the interface number -SUBSYSTEMS=="usb", ENV{ID_BUS}=="?*", KERNEL=="event*", ENV{.INPUT_CLASS}=="", ATTRS{bInterfaceNumber}=="?*", \ - SYMLINK+="input/by-id/$env{ID_BUS}-$env{ID_SERIAL}-event-if$attr{bInterfaceNumber}" - -# by-path -SUBSYSTEMS=="pci|usb|platform|acpi", IMPORT{builtin}="path_id" -ENV{ID_PATH}=="?*", KERNEL=="mouse*|js*", ENV{.INPUT_CLASS}=="?*", SYMLINK+="input/by-path/$env{ID_PATH}-$env{.INPUT_CLASS}" -ENV{ID_PATH}=="?*", KERNEL=="event*", ENV{.INPUT_CLASS}=="?*", SYMLINK+="input/by-path/$env{ID_PATH}-event-$env{.INPUT_CLASS}" -# allow empty class for platform and usb devices; platform supports only a single interface that way -SUBSYSTEMS=="usb|platform", ENV{ID_PATH}=="?*", KERNEL=="event*", ENV{.INPUT_CLASS}=="", \ - SYMLINK+="input/by-path/$env{ID_PATH}-event" - -LABEL="persistent_input_end" diff --git a/src/udev/rules/60-persistent-serial.rules b/src/udev/rules/60-persistent-serial.rules deleted file mode 100644 index 2948200c53..0000000000 --- a/src/udev/rules/60-persistent-serial.rules +++ /dev/null @@ -1,20 +0,0 @@ -# do not edit this file, it will be overwritten on update - -ACTION=="remove", GOTO="persistent_serial_end" -SUBSYSTEM!="tty", GOTO="persistent_serial_end" -KERNEL!="ttyUSB[0-9]*|ttyACM[0-9]*", GOTO="persistent_serial_end" - -SUBSYSTEMS=="usb-serial", ENV{.ID_PORT}="$attr{port_number}" - -IMPORT{builtin}="path_id" -ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="", SYMLINK+="serial/by-path/$env{ID_PATH}" -ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="?*", SYMLINK+="serial/by-path/$env{ID_PATH}-port$env{.ID_PORT}" - -IMPORT{builtin}="usb_id" -ENV{ID_SERIAL}=="", GOTO="persistent_serial_end" -SUBSYSTEMS=="usb", ENV{ID_USB_INTERFACE_NUM}="$attr{bInterfaceNumber}" -ENV{ID_USB_INTERFACE_NUM}=="", GOTO="persistent_serial_end" -ENV{.ID_PORT}=="", SYMLINK+="serial/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$env{ID_USB_INTERFACE_NUM}" -ENV{.ID_PORT}=="?*", SYMLINK+="serial/by-id/$env{ID_BUS}-$env{ID_SERIAL}-if$env{ID_USB_INTERFACE_NUM}-port$env{.ID_PORT}" - -LABEL="persistent_serial_end" diff --git a/src/udev/rules/60-persistent-storage-tape.rules b/src/udev/rules/60-persistent-storage-tape.rules deleted file mode 100644 index f2eabd92a8..0000000000 --- a/src/udev/rules/60-persistent-storage-tape.rules +++ /dev/null @@ -1,25 +0,0 @@ -# do not edit this file, it will be overwritten on update - -# persistent storage links: /dev/tape/{by-id,by-path} - -ACTION=="remove", GOTO="persistent_storage_tape_end" - -# type 8 devices are "Medium Changers" -SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="8", IMPORT{program}="scsi_id --sg-version=3 --export --whitelisted -d $devnode", \ - SYMLINK+="tape/by-id/scsi-$env{ID_SERIAL}" - -SUBSYSTEM!="scsi_tape", GOTO="persistent_storage_tape_end" - -KERNEL=="st*[0-9]|nst*[0-9]", ATTRS{ieee1394_id}=="?*", ENV{ID_SERIAL}="$attr{ieee1394_id}", ENV{ID_BUS}="ieee1394" -KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" -KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", KERNELS=="[0-9]*:*[0-9]", ENV{.BSG_DEV}="$root/bsg/$id" -KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --whitelisted --export --device=$env{.BSG_DEV}", ENV{ID_BUS}="scsi" -KERNEL=="st*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}" -KERNEL=="nst*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}-nst" - -# by-path (parent device path) -KERNEL=="st*[0-9]|nst*[0-9]", IMPORT{builtin}="path_id" -KERNEL=="st*[0-9]", ENV{ID_PATH}=="?*", SYMLINK+="tape/by-path/$env{ID_PATH}" -KERNEL=="nst*[0-9]", ENV{ID_PATH}=="?*", SYMLINK+="tape/by-path/$env{ID_PATH}-nst" - -LABEL="persistent_storage_tape_end" diff --git a/src/udev/rules/60-persistent-storage.rules b/src/udev/rules/60-persistent-storage.rules deleted file mode 100644 index b74821edd4..0000000000 --- a/src/udev/rules/60-persistent-storage.rules +++ /dev/null @@ -1,89 +0,0 @@ -# do not edit this file, it will be overwritten on update - -# persistent storage links: /dev/disk/{by-id,by-uuid,by-label,by-path} -# scheme based on "Linux persistent device names", 2004, Hannes Reinecke - -# forward scsi device event to corresponding block device -ACTION=="change", SUBSYSTEM=="scsi", ENV{DEVTYPE}=="scsi_device", TEST=="block", ATTR{block/*/uevent}="change" - -ACTION=="remove", GOTO="persistent_storage_end" - -# enable in-kernel media-presence polling -ACTION=="add", SUBSYSTEM=="module", KERNEL=="block", ATTR{parameters/events_dfl_poll_msecs}=="0", ATTR{parameters/events_dfl_poll_msecs}="2000" - -SUBSYSTEM!="block", GOTO="persistent_storage_end" - -# skip rules for inappropriate block devices -KERNEL=="fd*|mtd*|nbd*|gnbd*|btibm*|dm-*|md*", GOTO="persistent_storage_end" - -# ignore partitions that span the entire disk -TEST=="whole_disk", GOTO="persistent_storage_end" - -# for partitions import parent information -ENV{DEVTYPE}=="partition", IMPORT{parent}="ID_*" - -# virtio-blk -KERNEL=="vd*[!0-9]", ATTRS{serial}=="?*", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/virtio-$env{ID_SERIAL}" -KERNEL=="vd*[0-9]", ATTRS{serial}=="?*", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/virtio-$env{ID_SERIAL}-part%n" - -# ATA devices with their own "ata" kernel subsystem -KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="ata", IMPORT{program}="ata_id --export $devnode" -# ATA devices using the "scsi" subsystem -KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", IMPORT{program}="ata_id --export $devnode" -# ATA/ATAPI devices (SPC-3 or later) using the "scsi" subsystem -KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", ATTRS{type}=="5", ATTRS{scsi_level}=="[6-9]*", IMPORT{program}="ata_id --export $devnode" - -# Run ata_id on non-removable USB Mass Storage (SATA/PATA disks in enclosures) -KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", ATTR{removable}=="0", SUBSYSTEMS=="usb", IMPORT{program}="ata_id --export $devnode" -# Otherwise fall back to using usb_id for USB devices -KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" - -# scsi devices -KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --whitelisted -d $devnode", ENV{ID_BUS}="scsi" -KERNEL=="cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --whitelisted -d $devnode", ENV{ID_BUS}="cciss" -KERNEL=="sd*|sr*|cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}" -KERNEL=="sd*|cciss*", ENV{DEVTYPE}=="partition", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}-part%n" - -# firewire -KERNEL=="sd*[!0-9]|sr*", ATTRS{ieee1394_id}=="?*", SYMLINK+="disk/by-id/ieee1394-$attr{ieee1394_id}" -KERNEL=="sd*[0-9]", ATTRS{ieee1394_id}=="?*", SYMLINK+="disk/by-id/ieee1394-$attr{ieee1394_id}-part%n" - -KERNEL=="mmcblk[0-9]", SUBSYSTEMS=="mmc", ATTRS{name}=="?*", ATTRS{serial}=="?*", ENV{ID_NAME}="$attr{name}", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/mmc-$env{ID_NAME}_$env{ID_SERIAL}" -KERNEL=="mmcblk[0-9]p[0-9]", ENV{ID_NAME}=="?*", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/mmc-$env{ID_NAME}_$env{ID_SERIAL}-part%n" -KERNEL=="mspblk[0-9]", SUBSYSTEMS=="memstick", ATTRS{name}=="?*", ATTRS{serial}=="?*", ENV{ID_NAME}="$attr{name}", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/memstick-$env{ID_NAME}_$env{ID_SERIAL}" -KERNEL=="mspblk[0-9]p[0-9]", ENV{ID_NAME}=="?*", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/memstick-$env{ID_NAME}_$env{ID_SERIAL}-part%n" - -# by-path (parent device path) -ENV{DEVTYPE}=="disk", DEVPATH!="*/virtual/*", IMPORT{builtin}="path_id" -ENV{DEVTYPE}=="disk", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}" -ENV{DEVTYPE}=="partition", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}-part%n" - -# skip unpartitioned removable media devices from drivers which do not send "change" events -ENV{DEVTYPE}=="disk", KERNEL!="sd*|sr*", ATTR{removable}=="1", GOTO="persistent_storage_end" - -# probe filesystem metadata of optical drives which have a media inserted -KERNEL=="sr*", ENV{DISK_EJECT_REQUEST}!="?*", ENV{ID_CDROM_MEDIA_TRACK_COUNT_DATA}=="?*", ENV{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}=="?*", \ - IMPORT{builtin}="blkid --offset=$env{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}" -# single-session CDs do not have ID_CDROM_MEDIA_SESSION_LAST_OFFSET -KERNEL=="sr*", ENV{DISK_EJECT_REQUEST}!="?*", ENV{ID_CDROM_MEDIA_TRACK_COUNT_DATA}=="?*", ENV{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}=="", \ - IMPORT{builtin}="blkid --noraid" - -# probe filesystem metadata of disks -KERNEL!="sr*", IMPORT{builtin}="blkid" - -# watch metadata changes by tools closing the device after writing -KERNEL!="sr*", OPTIONS+="watch" - -# by-label/by-uuid links (filesystem metadata) -ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_UUID_ENC}=="?*", SYMLINK+="disk/by-uuid/$env{ID_FS_UUID_ENC}" -ENV{ID_FS_USAGE}=="filesystem|other", ENV{ID_FS_LABEL_ENC}=="?*", SYMLINK+="disk/by-label/$env{ID_FS_LABEL_ENC}" - -# by-id (World Wide Name) -ENV{DEVTYPE}=="disk", ENV{ID_WWN_WITH_EXTENSION}=="?*", SYMLINK+="disk/by-id/wwn-$env{ID_WWN_WITH_EXTENSION}" -ENV{DEVTYPE}=="partition", ENV{ID_WWN_WITH_EXTENSION}=="?*", SYMLINK+="disk/by-id/wwn-$env{ID_WWN_WITH_EXTENSION}-part%n" - -# by-partlabel/by-partuuid links (partition metadata) -ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_ENTRY_UUID}=="?*", SYMLINK+="disk/by-partuuid/$env{ID_PART_ENTRY_UUID}" -ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_ENTRY_NAME}=="?*", SYMLINK+="disk/by-partlabel/$env{ID_PART_ENTRY_NAME}" - -LABEL="persistent_storage_end" diff --git a/src/udev/rules/75-net-description.rules b/src/udev/rules/75-net-description.rules deleted file mode 100644 index ce57d48e86..0000000000 --- a/src/udev/rules/75-net-description.rules +++ /dev/null @@ -1,14 +0,0 @@ -# do not edit this file, it will be overwritten on update - -ACTION=="remove", GOTO="net_end" -SUBSYSTEM!="net", GOTO="net_end" - -SUBSYSTEMS=="usb", ENV{ID_MODEL}=="", IMPORT{builtin}="usb_id" -SUBSYSTEMS=="usb", IMPORT{builtin}="usb-db" -SUBSYSTEMS=="usb", ATTRS{idVendor}!="", ATTRS{idProduct}!="", ENV{ID_VENDOR_ID}="$attr{idVendor}", ENV{ID_MODEL_ID}="$attr{idProduct}" -SUBSYSTEMS=="usb", GOTO="net_end" - -SUBSYSTEMS=="pci", IMPORT{builtin}="pci-db" -SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}" - -LABEL="net_end" diff --git a/src/udev/rules/75-tty-description.rules b/src/udev/rules/75-tty-description.rules deleted file mode 100644 index 2e63e140cb..0000000000 --- a/src/udev/rules/75-tty-description.rules +++ /dev/null @@ -1,14 +0,0 @@ -# do not edit this file, it will be overwritten on update - -ACTION=="remove", GOTO="tty_end" -SUBSYSTEM!="tty", GOTO="tty_end" - -SUBSYSTEMS=="usb", ENV{ID_MODEL}=="", IMPORT{builtin}="usb_id" -SUBSYSTEMS=="usb", IMPORT{builtin}="usb-db" -SUBSYSTEMS=="usb", ATTRS{idVendor}!="", ATTRS{idProduct}!="", ENV{ID_VENDOR_ID}="$attr{idVendor}", ENV{ID_MODEL_ID}="$attr{idProduct}" -SUBSYSTEMS=="usb", GOTO="tty_end" - -SUBSYSTEMS=="pci", IMPORT{builtin}="pci-db" -SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}" - -LABEL="tty_end" diff --git a/src/udev/rules/78-sound-card.rules b/src/udev/rules/78-sound-card.rules deleted file mode 100644 index e564441893..0000000000 --- a/src/udev/rules/78-sound-card.rules +++ /dev/null @@ -1,89 +0,0 @@ -# do not edit this file, it will be overwritten on update - -SUBSYSTEM!="sound", GOTO="sound_end" - -ACTION=="add|change", KERNEL=="controlC*", ATTR{../uevent}="change" -ACTION!="change", GOTO="sound_end" - -# Ok, we probably need a little explanation here for what the two lines above -# are good for. -# -# The story goes like this: when ALSA registers a new sound card it emits a -# series of 'add' events to userspace, for the main card device and for all the -# child device nodes that belong to it. udev relays those to applications, -# however only maintains the order between father and child, but not between -# the siblings. The control device node creation can be used as synchronization -# point. All other devices that belong to a card are created in the kernel -# before it. However unfortunately due to the fact that siblings are forwarded -# out of order by udev this fact is lost to applications. -# -# OTOH before an application can open a device it needs to make sure that all -# its device nodes are completely created and set up. -# -# As a workaround for this issue we have added the udev rule above which will -# generate a 'change' event on the main card device from the 'add' event of the -# card's control device. Due to the ordering semantics of udev this event will -# only be relayed after all child devices have finished processing properly. -# When an application needs to listen for appearing devices it can hence look -# for 'change' events only, and ignore the actual 'add' events. -# -# When the application is initialized at the same time as a device is plugged -# in it may need to figure out if the 'change' event has already been triggered -# or not for a card. To find that out we store the flag environment variable -# SOUND_INITIALIZED on the device which simply tells us if the card 'change' -# event has already been processed. - -KERNEL!="card*", GOTO="sound_end" - -ENV{SOUND_INITIALIZED}="1" - -SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" -SUBSYSTEMS=="usb", IMPORT{builtin}="usb-db" -SUBSYSTEMS=="usb", GOTO="skip_pci" - -SUBSYSTEMS=="firewire", ATTRS{vendor_name}=="?*", ATTRS{model_name}=="?*", \ - ENV{ID_BUS}="firewire", ENV{ID_VENDOR}="$attr{vendor_name}", ENV{ID_MODEL}="$attr{model_name}" -SUBSYSTEMS=="firewire", ATTRS{guid}=="?*", ENV{ID_ID}="firewire-$attr{guid}" -SUBSYSTEMS=="firewire", GOTO="skip_pci" - - -SUBSYSTEMS=="pci", IMPORT{builtin}="pci-db" -SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}" - -LABEL="skip_pci" - -ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="?*", ENV{ID_ID}="$env{ID_BUS}-$env{ID_SERIAL}-$env{ID_USB_INTERFACE_NUM}-$attr{id}" -ENV{ID_SERIAL}=="?*", ENV{ID_USB_INTERFACE_NUM}=="", ENV{ID_ID}="$env{ID_BUS}-$env{ID_SERIAL}-$attr{id}" - -IMPORT{builtin}="path_id" - -# The values used here for $SOUND_FORM_FACTOR and $SOUND_CLASS should be kept -# in sync with those defined for PulseAudio's src/pulse/proplist.h -# PA_PROP_DEVICE_FORM_FACTOR, PA_PROP_DEVICE_CLASS properties. - -# If the first PCM device of this card has the pcm class 'modem', then the card is a modem -ATTR{pcmC%nD0p/pcm_class}=="modem", ENV{SOUND_CLASS}="modem", GOTO="sound_end" - -# Identify cards on the internal PCI bus as internal -SUBSYSTEMS=="pci", DEVPATH=="*/0000:00:??.?/sound/*", ENV{SOUND_FORM_FACTOR}="internal", GOTO="sound_end" - -# Devices that also support Image/Video interfaces are most likely webcams -SUBSYSTEMS=="usb", ENV{ID_USB_INTERFACES}=="*:0e????:*", ENV{SOUND_FORM_FACTOR}="webcam", GOTO="sound_end" - -# Matching on the model strings is a bit ugly, I admit -ENV{ID_MODEL}=="*[Ss]peaker*", ENV{SOUND_FORM_FACTOR}="speaker", GOTO="sound_end" -ENV{ID_MODEL_FROM_DATABASE}=="*[Ss]peaker*", ENV{SOUND_FORM_FACTOR}="speaker", GOTO="sound_end" - -ENV{ID_MODEL}=="*[Hh]eadphone*", ENV{SOUND_FORM_FACTOR}="headphone", GOTO="sound_end" -ENV{ID_MODEL_FROM_DATABASE}=="*[Hh]eadphone*", ENV{SOUND_FORM_FACTOR}="headphone", GOTO="sound_end" - -ENV{ID_MODEL}=="*[Hh]eadset*", ENV{SOUND_FORM_FACTOR}="headset", GOTO="sound_end" -ENV{ID_MODEL_FROM_DATABASE}=="*[Hh]eadset*", ENV{SOUND_FORM_FACTOR}="headset", GOTO="sound_end" - -ENV{ID_MODEL}=="*[Hh]andset*", ENV{SOUND_FORM_FACTOR}="handset", GOTO="sound_end" -ENV{ID_MODEL_FROM_DATABASE}=="*[Hh]andset*", ENV{SOUND_FORM_FACTOR}="handset", GOTO="sound_end" - -ENV{ID_MODEL}=="*[Mm]icrophone*", ENV{SOUND_FORM_FACTOR}="microphone", GOTO="sound_end" -ENV{ID_MODEL_FROM_DATABASE}=="*[Mm]icrophone*", ENV{SOUND_FORM_FACTOR}="microphone", GOTO="sound_end" - -LABEL="sound_end" diff --git a/src/udev/rules/80-drivers.rules b/src/udev/rules/80-drivers.rules deleted file mode 100644 index 38ebfeb0e6..0000000000 --- a/src/udev/rules/80-drivers.rules +++ /dev/null @@ -1,12 +0,0 @@ -# do not edit this file, it will be overwritten on update - -ACTION=="remove", GOTO="drivers_end" - -DRIVER!="?*", ENV{MODALIAS}=="?*", IMPORT{builtin}="kmod load $env{MODALIAS}" -SUBSYSTEM=="tifm", ENV{TIFM_CARD_TYPE}=="SD", IMPORT{builtin}="kmod load tifm_sd" -SUBSYSTEM=="tifm", ENV{TIFM_CARD_TYPE}=="MS", IMPORT{builtin}="kmod load tifm_ms" -SUBSYSTEM=="memstick", IMPORT{builtin}="kmod load ms_block mspro_block" -SUBSYSTEM=="i2o", IMPORT{builtin}="kmod load i2o_block" -SUBSYSTEM=="module", KERNEL=="parport_pc", IMPORT{builtin}="kmod load ppdev" - -LABEL="drivers_end" diff --git a/src/udev/rules/95-udev-late.rules b/src/udev/rules/95-udev-late.rules deleted file mode 100644 index eca0faa5c5..0000000000 --- a/src/udev/rules/95-udev-late.rules +++ /dev/null @@ -1,4 +0,0 @@ -# do not edit this file, it will be overwritten on update - -# run a command on remove events -ACTION=="remove", ENV{REMOVE_CMD}!="", RUN+="$env{REMOVE_CMD}" diff --git a/src/udev/scsi_id/.gitignore b/src/udev/scsi_id/.gitignore new file mode 100644 index 0000000000..6aebddd809 --- /dev/null +++ b/src/udev/scsi_id/.gitignore @@ -0,0 +1 @@ +scsi_id_version.h diff --git a/src/udev/scsi_id/README b/src/udev/scsi_id/README new file mode 100644 index 0000000000..9cfe73991c --- /dev/null +++ b/src/udev/scsi_id/README @@ -0,0 +1,4 @@ +scsi_id - generate a SCSI unique identifier for a given SCSI device + +Please send questions, comments or patches to or +. diff --git a/src/udev/scsi_id/scsi.h b/src/udev/scsi_id/scsi.h new file mode 100644 index 0000000000..c423cac574 --- /dev/null +++ b/src/udev/scsi_id/scsi.h @@ -0,0 +1,97 @@ +/* + * scsi.h + * + * General scsi and linux scsi specific defines and structs. + * + * Copyright (C) IBM Corp. 2003 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation version 2 of the License. + */ + +#include + +struct scsi_ioctl_command { + unsigned int inlen; /* excluding scsi command length */ + unsigned int outlen; + unsigned char data[1]; + /* on input, scsi command starts here then opt. data */ +}; + +/* + * Default 5 second timeout + */ +#define DEF_TIMEOUT 5000 + +#define SENSE_BUFF_LEN 32 + +/* + * The request buffer size passed to the SCSI INQUIRY commands, use 254, + * as this is a nice value for some devices, especially some of the usb + * mass storage devices. + */ +#define SCSI_INQ_BUFF_LEN 254 + +/* + * SCSI INQUIRY vendor and model (really product) lengths. + */ +#define VENDOR_LENGTH 8 +#define MODEL_LENGTH 16 + +#define INQUIRY_CMD 0x12 +#define INQUIRY_CMDLEN 6 + +/* + * INQUIRY VPD page 0x83 identifier descriptor related values. Reference the + * SCSI Primary Commands specification for details. + */ + +/* + * id type values of id descriptors. These are assumed to fit in 4 bits. + */ +#define SCSI_ID_VENDOR_SPECIFIC 0 +#define SCSI_ID_T10_VENDOR 1 +#define SCSI_ID_EUI_64 2 +#define SCSI_ID_NAA 3 +#define SCSI_ID_RELPORT 4 +#define SCSI_ID_TGTGROUP 5 +#define SCSI_ID_LUNGROUP 6 +#define SCSI_ID_MD5 7 +#define SCSI_ID_NAME 8 + +/* + * Supported NAA values. These fit in 4 bits, so the "don't care" value + * cannot conflict with real values. + */ +#define SCSI_ID_NAA_DONT_CARE 0xff +#define SCSI_ID_NAA_IEEE_REG 5 +#define SCSI_ID_NAA_IEEE_REG_EXTENDED 6 + +/* + * Supported Code Set values. + */ +#define SCSI_ID_BINARY 1 +#define SCSI_ID_ASCII 2 + +struct scsi_id_search_values { + u_char id_type; + u_char naa_type; + u_char code_set; +}; + +/* + * Following are the "true" SCSI status codes. Linux has traditionally + * used a 1 bit right and masked version of these. So now CHECK_CONDITION + * and friends (in ) are deprecated. + */ +#define SCSI_CHECK_CONDITION 0x2 +#define SCSI_CONDITION_MET 0x4 +#define SCSI_BUSY 0x8 +#define SCSI_IMMEDIATE 0x10 +#define SCSI_IMMEDIATE_CONDITION_MET 0x14 +#define SCSI_RESERVATION_CONFLICT 0x18 +#define SCSI_COMMAND_TERMINATED 0x22 +#define SCSI_TASK_SET_FULL 0x28 +#define SCSI_ACA_ACTIVE 0x30 +#define SCSI_TASK_ABORTED 0x40 diff --git a/src/udev/scsi_id/scsi_id.c b/src/udev/scsi_id/scsi_id.c new file mode 100644 index 0000000000..9bb0d7f538 --- /dev/null +++ b/src/udev/scsi_id/scsi_id.c @@ -0,0 +1,657 @@ +/* + * Copyright (C) IBM Corp. 2003 + * Copyright (C) SUSE Linux Products GmbH, 2006 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libudev.h" +#include "libudev-private.h" +#include "scsi_id.h" + +static const struct option options[] = { + { "device", required_argument, NULL, 'd' }, + { "config", required_argument, NULL, 'f' }, + { "page", required_argument, NULL, 'p' }, + { "blacklisted", no_argument, NULL, 'b' }, + { "whitelisted", no_argument, NULL, 'g' }, + { "replace-whitespace", no_argument, NULL, 'u' }, + { "sg-version", required_argument, NULL, 's' }, + { "verbose", no_argument, NULL, 'v' }, + { "version", no_argument, NULL, 'V' }, + { "export", no_argument, NULL, 'x' }, + { "help", no_argument, NULL, 'h' }, + {} +}; + +static const char short_options[] = "d:f:ghip:uvVx"; +static const char dev_short_options[] = "bgp:"; + +static int all_good; +static int dev_specified; +static char config_file[MAX_PATH_LEN] = SYSCONFDIR "/scsi_id.config"; +static enum page_code default_page_code; +static int sg_version = 4; +static int use_stderr; +static int debug; +static int reformat_serial; +static int export; +static char vendor_str[64]; +static char model_str[64]; +static char vendor_enc_str[256]; +static char model_enc_str[256]; +static char revision_str[16]; +static char type_str[16]; + +static void log_fn(struct udev *udev, int priority, + const char *file, int line, const char *fn, + const char *format, va_list args) +{ + vsyslog(priority, format, args); +} + +static void set_type(const char *from, char *to, size_t len) +{ + int type_num; + char *eptr; + char *type = "generic"; + + type_num = strtoul(from, &eptr, 0); + if (eptr != from) { + switch (type_num) { + case 0: + type = "disk"; + break; + case 1: + type = "tape"; + break; + case 4: + type = "optical"; + break; + case 5: + type = "cd"; + break; + case 7: + type = "optical"; + break; + case 0xe: + type = "disk"; + break; + case 0xf: + type = "optical"; + break; + default: + break; + } + } + util_strscpy(to, len, type); +} + +/* + * get_value: + * + * buf points to an '=' followed by a quoted string ("foo") or a string ending + * with a space or ','. + * + * Return a pointer to the NUL terminated string, returns NULL if no + * matches. + */ +static char *get_value(char **buffer) +{ + static char *quote_string = "\"\n"; + static char *comma_string = ",\n"; + char *val; + char *end; + + if (**buffer == '"') { + /* + * skip leading quote, terminate when quote seen + */ + (*buffer)++; + end = quote_string; + } else { + end = comma_string; + } + val = strsep(buffer, end); + if (val && end == quote_string) + /* + * skip trailing quote + */ + (*buffer)++; + + while (isspace(**buffer)) + (*buffer)++; + + return val; +} + +static int argc_count(char *opts) +{ + int i = 0; + while (*opts != '\0') + if (*opts++ == ' ') + i++; + return i; +} + +/* + * get_file_options: + * + * If vendor == NULL, find a line in the config file with only "OPTIONS="; + * if vendor and model are set find the first OPTIONS line in the config + * file that matches. Set argc and argv to match the OPTIONS string. + * + * vendor and model can end in '\n'. + */ +static int get_file_options(struct udev *udev, + const char *vendor, const char *model, + int *argc, char ***newargv) +{ + char *buffer; + FILE *fd; + char *buf; + char *str1; + char *vendor_in, *model_in, *options_in; /* read in from file */ + int lineno; + int c; + int retval = 0; + + dbg(udev, "vendor='%s'; model='%s'\n", vendor, model); + fd = fopen(config_file, "r"); + if (fd == NULL) { + dbg(udev, "can't open %s\n", config_file); + if (errno == ENOENT) { + return 1; + } else { + err(udev, "can't open %s: %s\n", config_file, strerror(errno)); + return -1; + } + } + + /* + * Allocate a buffer rather than put it on the stack so we can + * keep it around to parse any options (any allocated newargv + * points into this buffer for its strings). + */ + buffer = malloc(MAX_BUFFER_LEN); + if (!buffer) { + fclose(fd); + err(udev, "can't allocate memory\n"); + return -1; + } + + *newargv = NULL; + lineno = 0; + while (1) { + vendor_in = model_in = options_in = NULL; + + buf = fgets(buffer, MAX_BUFFER_LEN, fd); + if (buf == NULL) + break; + lineno++; + if (buf[strlen(buffer) - 1] != '\n') { + err(udev, "Config file line %d too long\n", lineno); + break; + } + + while (isspace(*buf)) + buf++; + + /* blank or all whitespace line */ + if (*buf == '\0') + continue; + + /* comment line */ + if (*buf == '#') + continue; + + dbg(udev, "lineno %d: '%s'\n", lineno, buf); + str1 = strsep(&buf, "="); + if (str1 && strcasecmp(str1, "VENDOR") == 0) { + str1 = get_value(&buf); + if (!str1) { + retval = -1; + break; + } + vendor_in = str1; + + str1 = strsep(&buf, "="); + if (str1 && strcasecmp(str1, "MODEL") == 0) { + str1 = get_value(&buf); + if (!str1) { + retval = -1; + break; + } + model_in = str1; + str1 = strsep(&buf, "="); + } + } + + if (str1 && strcasecmp(str1, "OPTIONS") == 0) { + str1 = get_value(&buf); + if (!str1) { + retval = -1; + break; + } + options_in = str1; + } + dbg(udev, "config file line %d:\n" + " vendor '%s'; model '%s'; options '%s'\n", + lineno, vendor_in, model_in, options_in); + /* + * Only allow: [vendor=foo[,model=bar]]options=stuff + */ + if (!options_in || (!vendor_in && model_in)) { + err(udev, "Error parsing config file line %d '%s'\n", lineno, buffer); + retval = -1; + break; + } + if (vendor == NULL) { + if (vendor_in == NULL) { + dbg(udev, "matched global option\n"); + break; + } + } else if ((vendor_in && strncmp(vendor, vendor_in, + strlen(vendor_in)) == 0) && + (!model_in || (strncmp(model, model_in, + strlen(model_in)) == 0))) { + /* + * Matched vendor and optionally model. + * + * Note: a short vendor_in or model_in can + * give a partial match (that is FOO + * matches FOOBAR). + */ + dbg(udev, "matched vendor/model\n"); + break; + } else { + dbg(udev, "no match\n"); + } + } + + if (retval == 0) { + if (vendor_in != NULL || model_in != NULL || + options_in != NULL) { + /* + * Something matched. Allocate newargv, and store + * values found in options_in. + */ + strcpy(buffer, options_in); + c = argc_count(buffer) + 2; + *newargv = calloc(c, sizeof(**newargv)); + if (!*newargv) { + err(udev, "can't allocate memory\n"); + retval = -1; + } else { + *argc = c; + c = 0; + /* + * argv[0] at 0 is skipped by getopt, but + * store the buffer address there for + * later freeing + */ + (*newargv)[c] = buffer; + for (c = 1; c < *argc; c++) + (*newargv)[c] = strsep(&buffer, " \t"); + } + } else { + /* No matches */ + retval = 1; + } + } + if (retval != 0) + free(buffer); + fclose(fd); + return retval; +} + +static int set_options(struct udev *udev, + int argc, char **argv, const char *short_opts, + char *maj_min_dev) +{ + int option; + + /* + * optind is a global extern used by getopt. Since we can call + * set_options twice (once for command line, and once for config + * file) we have to reset this back to 1. + */ + optind = 1; + while (1) { + option = getopt_long(argc, argv, short_opts, options, NULL); + if (option == -1) + break; + + if (optarg) + dbg(udev, "option '%c' arg '%s'\n", option, optarg); + else + dbg(udev, "option '%c'\n", option); + + switch (option) { + case 'b': + all_good = 0; + break; + + case 'd': + dev_specified = 1; + util_strscpy(maj_min_dev, MAX_PATH_LEN, optarg); + break; + + case 'e': + use_stderr = 1; + break; + + case 'f': + util_strscpy(config_file, MAX_PATH_LEN, optarg); + break; + + case 'g': + all_good = 1; + break; + + case 'h': + printf("Usage: scsi_id OPTIONS \n" + " --device= device node for SG_IO commands\n" + " --config= location of config file\n" + " --page=0x80|0x83|pre-spc3-83 SCSI page (0x80, 0x83, pre-spc3-83)\n" + " --sg-version=3|4 use SGv3 or SGv4\n" + " --blacklisted threat device as blacklisted\n" + " --whitelisted threat device as whitelisted\n" + " --replace-whitespace replace all whitespaces by underscores\n" + " --verbose verbose logging\n" + " --version print version\n" + " --export print values as environment keys\n" + " --help print this help text\n\n"); + exit(0); + + case 'p': + if (strcmp(optarg, "0x80") == 0) { + default_page_code = PAGE_80; + } else if (strcmp(optarg, "0x83") == 0) { + default_page_code = PAGE_83; + } else if (strcmp(optarg, "pre-spc3-83") == 0) { + default_page_code = PAGE_83_PRE_SPC3; + } else { + err(udev, "Unknown page code '%s'\n", optarg); + return -1; + } + break; + + case 's': + sg_version = atoi(optarg); + if (sg_version < 3 || sg_version > 4) { + err(udev, "Unknown SG version '%s'\n", optarg); + return -1; + } + break; + + case 'u': + reformat_serial = 1; + break; + + case 'x': + export = 1; + break; + + case 'v': + debug++; + break; + + case 'V': + printf("%s\n", VERSION); + exit(0); + break; + + default: + exit(1); + } + } + if (optind < argc && !dev_specified) { + dev_specified = 1; + util_strscpy(maj_min_dev, MAX_PATH_LEN, argv[optind]); + } + return 0; +} + +static int per_dev_options(struct udev *udev, + struct scsi_id_device *dev_scsi, int *good_bad, int *page_code) +{ + int retval; + int newargc; + char **newargv = NULL; + int option; + + *good_bad = all_good; + *page_code = default_page_code; + + retval = get_file_options(udev, vendor_str, model_str, &newargc, &newargv); + + optind = 1; /* reset this global extern */ + while (retval == 0) { + option = getopt_long(newargc, newargv, dev_short_options, options, NULL); + if (option == -1) + break; + + if (optarg) + dbg(udev, "option '%c' arg '%s'\n", option, optarg); + else + dbg(udev, "option '%c'\n", option); + + switch (option) { + case 'b': + *good_bad = 0; + break; + + case 'g': + *good_bad = 1; + break; + + case 'p': + if (strcmp(optarg, "0x80") == 0) { + *page_code = PAGE_80; + } else if (strcmp(optarg, "0x83") == 0) { + *page_code = PAGE_83; + } else if (strcmp(optarg, "pre-spc3-83") == 0) { + *page_code = PAGE_83_PRE_SPC3; + } else { + err(udev, "Unknown page code '%s'\n", optarg); + retval = -1; + } + break; + + default: + err(udev, "Unknown or bad option '%c' (0x%x)\n", option, option); + retval = -1; + break; + } + } + + if (newargv) { + free(newargv[0]); + free(newargv); + } + return retval; +} + +static int set_inq_values(struct udev *udev, struct scsi_id_device *dev_scsi, const char *path) +{ + int retval; + + dev_scsi->use_sg = sg_version; + + retval = scsi_std_inquiry(udev, dev_scsi, path); + if (retval) + return retval; + + udev_util_encode_string(dev_scsi->vendor, vendor_enc_str, sizeof(vendor_enc_str)); + udev_util_encode_string(dev_scsi->model, model_enc_str, sizeof(model_enc_str)); + + util_replace_whitespace(dev_scsi->vendor, vendor_str, sizeof(vendor_str)); + util_replace_chars(vendor_str, NULL); + util_replace_whitespace(dev_scsi->model, model_str, sizeof(model_str)); + util_replace_chars(model_str, NULL); + set_type(dev_scsi->type, type_str, sizeof(type_str)); + util_replace_whitespace(dev_scsi->revision, revision_str, sizeof(revision_str)); + util_replace_chars(revision_str, NULL); + return 0; +} + +/* + * scsi_id: try to get an id, if one is found, printf it to stdout. + * returns a value passed to exit() - 0 if printed an id, else 1. + */ +static int scsi_id(struct udev *udev, char *maj_min_dev) +{ + struct scsi_id_device dev_scsi; + int good_dev; + int page_code; + int retval = 0; + + memset(&dev_scsi, 0x00, sizeof(struct scsi_id_device)); + + if (set_inq_values(udev, &dev_scsi, maj_min_dev) < 0) { + retval = 1; + goto out; + } + + /* get per device (vendor + model) options from the config file */ + per_dev_options(udev, &dev_scsi, &good_dev, &page_code); + dbg(udev, "per dev options: good %d; page code 0x%x\n", good_dev, page_code); + if (!good_dev) { + retval = 1; + goto out; + } + + /* read serial number from mode pages (no values for optical drives) */ + scsi_get_serial(udev, &dev_scsi, maj_min_dev, page_code, MAX_SERIAL_LEN); + + if (export) { + char serial_str[MAX_SERIAL_LEN]; + + printf("ID_SCSI=1\n"); + printf("ID_VENDOR=%s\n", vendor_str); + printf("ID_VENDOR_ENC=%s\n", vendor_enc_str); + printf("ID_MODEL=%s\n", model_str); + printf("ID_MODEL_ENC=%s\n", model_enc_str); + printf("ID_REVISION=%s\n", revision_str); + printf("ID_TYPE=%s\n", type_str); + if (dev_scsi.serial[0] != '\0') { + util_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str)); + util_replace_chars(serial_str, NULL); + printf("ID_SERIAL=%s\n", serial_str); + util_replace_whitespace(dev_scsi.serial_short, serial_str, sizeof(serial_str)); + util_replace_chars(serial_str, NULL); + printf("ID_SERIAL_SHORT=%s\n", serial_str); + } + if (dev_scsi.wwn[0] != '\0') { + printf("ID_WWN=0x%s\n", dev_scsi.wwn); + if (dev_scsi.wwn_vendor_extension[0] != '\0') { + printf("ID_WWN_VENDOR_EXTENSION=0x%s\n", dev_scsi.wwn_vendor_extension); + printf("ID_WWN_WITH_EXTENSION=0x%s%s\n", dev_scsi.wwn, dev_scsi.wwn_vendor_extension); + } else { + printf("ID_WWN_WITH_EXTENSION=0x%s\n", dev_scsi.wwn); + } + } + if (dev_scsi.tgpt_group[0] != '\0') { + printf("ID_TARGET_PORT=%s\n", dev_scsi.tgpt_group); + } + if (dev_scsi.unit_serial_number[0] != '\0') { + printf("ID_SCSI_SERIAL=%s\n", dev_scsi.unit_serial_number); + } + goto out; + } + + if (dev_scsi.serial[0] == '\0') { + retval = 1; + goto out; + } + + if (reformat_serial) { + char serial_str[MAX_SERIAL_LEN]; + + util_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str)); + util_replace_chars(serial_str, NULL); + printf("%s\n", serial_str); + goto out; + } + + printf("%s\n", dev_scsi.serial); +out: + return retval; +} + +int main(int argc, char **argv) +{ + struct udev *udev; + int retval = 0; + char maj_min_dev[MAX_PATH_LEN]; + int newargc; + char **newargv; + + udev = udev_new(); + if (udev == NULL) + goto exit; + + udev_log_init("scsi_id"); + udev_set_log_fn(udev, log_fn); + + /* + * Get config file options. + */ + newargv = NULL; + retval = get_file_options(udev, NULL, NULL, &newargc, &newargv); + if (retval < 0) { + retval = 1; + goto exit; + } + if (newargv && (retval == 0)) { + if (set_options(udev, newargc, newargv, short_options, maj_min_dev) < 0) { + retval = 2; + goto exit; + } + free(newargv); + } + + /* + * Get command line options (overriding any config file settings). + */ + if (set_options(udev, argc, argv, short_options, maj_min_dev) < 0) + exit(1); + + if (!dev_specified) { + err(udev, "no device specified\n"); + retval = 1; + goto exit; + } + + retval = scsi_id(udev, maj_min_dev); + +exit: + udev_unref(udev); + udev_log_close(); + return retval; +} diff --git a/src/udev/scsi_id/scsi_id.h b/src/udev/scsi_id/scsi_id.h new file mode 100644 index 0000000000..828a98305f --- /dev/null +++ b/src/udev/scsi_id/scsi_id.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) IBM Corp. 2003 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define MAX_PATH_LEN 512 + +/* + * MAX_ATTR_LEN: maximum length of the result of reading a sysfs + * attribute. + */ +#define MAX_ATTR_LEN 256 + +/* + * MAX_SERIAL_LEN: the maximum length of the serial number, including + * added prefixes such as vendor and product (model) strings. + */ +#define MAX_SERIAL_LEN 256 + +/* + * MAX_BUFFER_LEN: maximum buffer size and line length used while reading + * the config file. + */ +#define MAX_BUFFER_LEN 256 + +struct scsi_id_device { + char vendor[9]; + char model[17]; + char revision[5]; + char type[33]; + char kernel[64]; + char serial[MAX_SERIAL_LEN]; + char serial_short[MAX_SERIAL_LEN]; + int use_sg; + + /* Always from page 0x80 e.g. 'B3G1P8500RWT' - may not be unique */ + char unit_serial_number[MAX_SERIAL_LEN]; + + /* NULs if not set - otherwise hex encoding using lower-case e.g. '50014ee0016eb572' */ + char wwn[17]; + + /* NULs if not set - otherwise hex encoding using lower-case e.g. '0xe00000d80000' */ + char wwn_vendor_extension[17]; + + /* NULs if not set - otherwise decimal number */ + char tgpt_group[8]; +}; + +extern int scsi_std_inquiry(struct udev *udev, struct scsi_id_device *dev_scsi, const char *devname); +extern int scsi_get_serial (struct udev *udev, struct scsi_id_device *dev_scsi, const char *devname, + int page_code, int len); + +/* + * Page code values. + */ +enum page_code { + PAGE_83_PRE_SPC3 = -0x83, + PAGE_UNSPECIFIED = 0x00, + PAGE_80 = 0x80, + PAGE_83 = 0x83, +}; diff --git a/src/udev/scsi_id/scsi_serial.c b/src/udev/scsi_id/scsi_serial.c new file mode 100644 index 0000000000..f1d63f40cc --- /dev/null +++ b/src/udev/scsi_id/scsi_serial.c @@ -0,0 +1,990 @@ +/* + * Copyright (C) IBM Corp. 2003 + * + * Author: Patrick Mansfield + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libudev.h" +#include "libudev-private.h" +#include "scsi.h" +#include "scsi_id.h" + +/* + * A priority based list of id, naa, and binary/ascii for the identifier + * descriptor in VPD page 0x83. + * + * Brute force search for a match starting with the first value in the + * following id_search_list. This is not a performance issue, since there + * is normally one or some small number of descriptors. + */ +static const struct scsi_id_search_values id_search_list[] = { + { SCSI_ID_TGTGROUP, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, + { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG_EXTENDED, SCSI_ID_BINARY }, + { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG_EXTENDED, SCSI_ID_ASCII }, + { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG, SCSI_ID_BINARY }, + { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG, SCSI_ID_ASCII }, + /* + * Devices already exist using NAA values that are now marked + * reserved. These should not conflict with other values, or it is + * a bug in the device. As long as we find the IEEE extended one + * first, we really don't care what other ones are used. Using + * don't care here means that a device that returns multiple + * non-IEEE descriptors in a random order will get different + * names. + */ + { SCSI_ID_NAA, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, + { SCSI_ID_NAA, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII }, + { SCSI_ID_EUI_64, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, + { SCSI_ID_EUI_64, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII }, + { SCSI_ID_T10_VENDOR, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, + { SCSI_ID_T10_VENDOR, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII }, + { SCSI_ID_VENDOR_SPECIFIC, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, + { SCSI_ID_VENDOR_SPECIFIC, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII }, +}; + +static const char hex_str[]="0123456789abcdef"; + +/* + * Values returned in the result/status, only the ones used by the code + * are used here. + */ + +#define DID_NO_CONNECT 0x01 /* Unable to connect before timeout */ +#define DID_BUS_BUSY 0x02 /* Bus remain busy until timeout */ +#define DID_TIME_OUT 0x03 /* Timed out for some other reason */ +#define DRIVER_TIMEOUT 0x06 +#define DRIVER_SENSE 0x08 /* Sense_buffer has been set */ + +/* The following "category" function returns one of the following */ +#define SG_ERR_CAT_CLEAN 0 /* No errors or other information */ +#define SG_ERR_CAT_MEDIA_CHANGED 1 /* interpreted from sense buffer */ +#define SG_ERR_CAT_RESET 2 /* interpreted from sense buffer */ +#define SG_ERR_CAT_TIMEOUT 3 +#define SG_ERR_CAT_RECOVERED 4 /* Successful command after recovered err */ +#define SG_ERR_CAT_NOTSUPPORTED 5 /* Illegal / unsupported command */ +#define SG_ERR_CAT_SENSE 98 /* Something else in the sense buffer */ +#define SG_ERR_CAT_OTHER 99 /* Some other error/warning */ + +static int do_scsi_page80_inquiry(struct udev *udev, + struct scsi_id_device *dev_scsi, int fd, + char *serial, char *serial_short, int max_len); + +static int sg_err_category_new(struct udev *udev, + int scsi_status, int msg_status, int + host_status, int driver_status, const + unsigned char *sense_buffer, int sb_len) +{ + scsi_status &= 0x7e; + + /* + * XXX change to return only two values - failed or OK. + */ + + if (!scsi_status && !host_status && !driver_status) + return SG_ERR_CAT_CLEAN; + + if ((scsi_status == SCSI_CHECK_CONDITION) || + (scsi_status == SCSI_COMMAND_TERMINATED) || + ((driver_status & 0xf) == DRIVER_SENSE)) { + if (sense_buffer && (sb_len > 2)) { + int sense_key; + unsigned char asc; + + if (sense_buffer[0] & 0x2) { + sense_key = sense_buffer[1] & 0xf; + asc = sense_buffer[2]; + } else { + sense_key = sense_buffer[2] & 0xf; + asc = (sb_len > 12) ? sense_buffer[12] : 0; + } + + if (sense_key == RECOVERED_ERROR) + return SG_ERR_CAT_RECOVERED; + else if (sense_key == UNIT_ATTENTION) { + if (0x28 == asc) + return SG_ERR_CAT_MEDIA_CHANGED; + if (0x29 == asc) + return SG_ERR_CAT_RESET; + } else if (sense_key == ILLEGAL_REQUEST) { + return SG_ERR_CAT_NOTSUPPORTED; + } + } + return SG_ERR_CAT_SENSE; + } + if (host_status) { + if ((host_status == DID_NO_CONNECT) || + (host_status == DID_BUS_BUSY) || + (host_status == DID_TIME_OUT)) + return SG_ERR_CAT_TIMEOUT; + } + if (driver_status) { + if (driver_status == DRIVER_TIMEOUT) + return SG_ERR_CAT_TIMEOUT; + } + return SG_ERR_CAT_OTHER; +} + +static int sg_err_category3(struct udev *udev, struct sg_io_hdr *hp) +{ + return sg_err_category_new(udev, + hp->status, hp->msg_status, + hp->host_status, hp->driver_status, + hp->sbp, hp->sb_len_wr); +} + +static int sg_err_category4(struct udev *udev, struct sg_io_v4 *hp) +{ + return sg_err_category_new(udev, hp->device_status, 0, + hp->transport_status, hp->driver_status, + (unsigned char *)(uintptr_t)hp->response, + hp->response_len); +} + +static int scsi_dump_sense(struct udev *udev, + struct scsi_id_device *dev_scsi, + unsigned char *sense_buffer, int sb_len) +{ + int s; + int code; + int sense_class; + int sense_key; + int asc, ascq; +#ifdef DUMP_SENSE + char out_buffer[256]; + int i, j; +#endif + + /* + * Figure out and print the sense key, asc and ascq. + * + * If you want to suppress these for a particular drive model, add + * a black list entry in the scsi_id config file. + * + * XXX We probably need to: lookup the sense/asc/ascq in a retry + * table, and if found return 1 (after dumping the sense, asc, and + * ascq). So, if/when we get something like a power on/reset, + * we'll retry the command. + */ + + dbg(udev, "got check condition\n"); + + if (sb_len < 1) { + info(udev, "%s: sense buffer empty\n", dev_scsi->kernel); + return -1; + } + + sense_class = (sense_buffer[0] >> 4) & 0x07; + code = sense_buffer[0] & 0xf; + + if (sense_class == 7) { + /* + * extended sense data. + */ + s = sense_buffer[7] + 8; + if (sb_len < s) { + info(udev, "%s: sense buffer too small %d bytes, %d bytes too short\n", + dev_scsi->kernel, sb_len, s - sb_len); + return -1; + } + if ((code == 0x0) || (code == 0x1)) { + sense_key = sense_buffer[2] & 0xf; + if (s < 14) { + /* + * Possible? + */ + info(udev, "%s: sense result too" " small %d bytes\n", + dev_scsi->kernel, s); + return -1; + } + asc = sense_buffer[12]; + ascq = sense_buffer[13]; + } else if ((code == 0x2) || (code == 0x3)) { + sense_key = sense_buffer[1] & 0xf; + asc = sense_buffer[2]; + ascq = sense_buffer[3]; + } else { + info(udev, "%s: invalid sense code 0x%x\n", + dev_scsi->kernel, code); + return -1; + } + info(udev, "%s: sense key 0x%x ASC 0x%x ASCQ 0x%x\n", + dev_scsi->kernel, sense_key, asc, ascq); + } else { + if (sb_len < 4) { + info(udev, "%s: sense buffer too small %d bytes, %d bytes too short\n", + dev_scsi->kernel, sb_len, 4 - sb_len); + return -1; + } + + if (sense_buffer[0] < 15) + info(udev, "%s: old sense key: 0x%x\n", dev_scsi->kernel, sense_buffer[0] & 0x0f); + else + info(udev, "%s: sense = %2x %2x\n", + dev_scsi->kernel, sense_buffer[0], sense_buffer[2]); + info(udev, "%s: non-extended sense class %d code 0x%0x\n", + dev_scsi->kernel, sense_class, code); + + } + +#ifdef DUMP_SENSE + for (i = 0, j = 0; (i < s) && (j < 254); i++) { + dbg(udev, "i %d, j %d\n", i, j); + out_buffer[j++] = hex_str[(sense_buffer[i] & 0xf0) >> 4]; + out_buffer[j++] = hex_str[sense_buffer[i] & 0x0f]; + out_buffer[j++] = ' '; + } + out_buffer[j] = '\0'; + info(udev, "%s: sense dump:\n", dev_scsi->kernel); + info(udev, "%s: %s\n", dev_scsi->kernel, out_buffer); + +#endif + return -1; +} + +static int scsi_dump(struct udev *udev, + struct scsi_id_device *dev_scsi, struct sg_io_hdr *io) +{ + if (!io->status && !io->host_status && !io->msg_status && + !io->driver_status) { + /* + * Impossible, should not be called. + */ + info(udev, "%s: called with no error\n", __FUNCTION__); + return -1; + } + + info(udev, "%s: sg_io failed status 0x%x 0x%x 0x%x 0x%x\n", + dev_scsi->kernel, io->driver_status, io->host_status, io->msg_status, io->status); + if (io->status == SCSI_CHECK_CONDITION) + return scsi_dump_sense(udev, dev_scsi, io->sbp, io->sb_len_wr); + else + return -1; +} + +static int scsi_dump_v4(struct udev *udev, + struct scsi_id_device *dev_scsi, struct sg_io_v4 *io) +{ + if (!io->device_status && !io->transport_status && + !io->driver_status) { + /* + * Impossible, should not be called. + */ + info(udev, "%s: called with no error\n", __FUNCTION__); + return -1; + } + + info(udev, "%s: sg_io failed status 0x%x 0x%x 0x%x\n", + dev_scsi->kernel, io->driver_status, io->transport_status, + io->device_status); + if (io->device_status == SCSI_CHECK_CONDITION) + return scsi_dump_sense(udev, dev_scsi, (unsigned char *)(uintptr_t)io->response, + io->response_len); + else + return -1; +} + +static int scsi_inquiry(struct udev *udev, + struct scsi_id_device *dev_scsi, int fd, + unsigned char evpd, unsigned char page, + unsigned char *buf, unsigned int buflen) +{ + unsigned char inq_cmd[INQUIRY_CMDLEN] = + { INQUIRY_CMD, evpd, page, 0, buflen, 0 }; + unsigned char sense[SENSE_BUFF_LEN]; + void *io_buf; + struct sg_io_v4 io_v4; + struct sg_io_hdr io_hdr; + int retry = 3; /* rather random */ + int retval; + + if (buflen > SCSI_INQ_BUFF_LEN) { + info(udev, "buflen %d too long\n", buflen); + return -1; + } + +resend: + dbg(udev, "%s evpd %d, page 0x%x\n", dev_scsi->kernel, evpd, page); + + if (dev_scsi->use_sg == 4) { + memset(&io_v4, 0, sizeof(struct sg_io_v4)); + io_v4.guard = 'Q'; + io_v4.protocol = BSG_PROTOCOL_SCSI; + io_v4.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD; + io_v4.request_len = sizeof(inq_cmd); + io_v4.request = (uintptr_t)inq_cmd; + io_v4.max_response_len = sizeof(sense); + io_v4.response = (uintptr_t)sense; + io_v4.din_xfer_len = buflen; + io_v4.din_xferp = (uintptr_t)buf; + io_buf = (void *)&io_v4; + } else { + memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = sizeof(inq_cmd); + io_hdr.mx_sb_len = sizeof(sense); + io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; + io_hdr.dxfer_len = buflen; + io_hdr.dxferp = buf; + io_hdr.cmdp = inq_cmd; + io_hdr.sbp = sense; + io_hdr.timeout = DEF_TIMEOUT; + io_buf = (void *)&io_hdr; + } + + retval = ioctl(fd, SG_IO, io_buf); + if (retval < 0) { + if ((errno == EINVAL || errno == ENOSYS) && dev_scsi->use_sg == 4) { + dev_scsi->use_sg = 3; + goto resend; + } + info(udev, "%s: ioctl failed: %s\n", dev_scsi->kernel, strerror(errno)); + goto error; + } + + if (dev_scsi->use_sg == 4) + retval = sg_err_category4(udev, io_buf); + else + retval = sg_err_category3(udev, io_buf); + + switch (retval) { + case SG_ERR_CAT_NOTSUPPORTED: + buf[1] = 0; + /* Fallthrough */ + case SG_ERR_CAT_CLEAN: + case SG_ERR_CAT_RECOVERED: + retval = 0; + break; + + default: + if (dev_scsi->use_sg == 4) + retval = scsi_dump_v4(udev, dev_scsi, io_buf); + else + retval = scsi_dump(udev, dev_scsi, io_buf); + } + + if (!retval) { + retval = buflen; + } else if (retval > 0) { + if (--retry > 0) { + dbg(udev, "%s: Retrying ...\n", dev_scsi->kernel); + goto resend; + } + retval = -1; + } + +error: + if (retval < 0) + info(udev, "%s: Unable to get INQUIRY vpd %d page 0x%x.\n", + dev_scsi->kernel, evpd, page); + + return retval; +} + +/* Get list of supported EVPD pages */ +static int do_scsi_page0_inquiry(struct udev *udev, + struct scsi_id_device *dev_scsi, int fd, + unsigned char *buffer, unsigned int len) +{ + int retval; + + memset(buffer, 0, len); + retval = scsi_inquiry(udev, dev_scsi, fd, 1, 0x0, buffer, len); + if (retval < 0) + return 1; + + if (buffer[1] != 0) { + info(udev, "%s: page 0 not available.\n", dev_scsi->kernel); + return 1; + } + if (buffer[3] > len) { + info(udev, "%s: page 0 buffer too long %d\n", dev_scsi->kernel, buffer[3]); + return 1; + } + + /* + * Following check is based on code once included in the 2.5.x + * kernel. + * + * Some ill behaved devices return the standard inquiry here + * rather than the evpd data, snoop the data to verify. + */ + if (buffer[3] > MODEL_LENGTH) { + /* + * If the vendor id appears in the page assume the page is + * invalid. + */ + if (!strncmp((char *)&buffer[VENDOR_LENGTH], dev_scsi->vendor, VENDOR_LENGTH)) { + info(udev, "%s: invalid page0 data\n", dev_scsi->kernel); + return 1; + } + } + return 0; +} + +/* + * The caller checks that serial is long enough to include the vendor + + * model. + */ +static int prepend_vendor_model(struct udev *udev, + struct scsi_id_device *dev_scsi, char *serial) +{ + int ind; + + strncpy(serial, dev_scsi->vendor, VENDOR_LENGTH); + strncat(serial, dev_scsi->model, MODEL_LENGTH); + ind = strlen(serial); + + /* + * This is not a complete check, since we are using strncat/cpy + * above, ind will never be too large. + */ + if (ind != (VENDOR_LENGTH + MODEL_LENGTH)) { + info(udev, "%s: expected length %d, got length %d\n", + dev_scsi->kernel, (VENDOR_LENGTH + MODEL_LENGTH), ind); + return -1; + } + return ind; +} + +/** + * check_fill_0x83_id - check the page 0x83 id, if OK allocate and fill + * serial number. + **/ +static int check_fill_0x83_id(struct udev *udev, + struct scsi_id_device *dev_scsi, + unsigned char *page_83, + const struct scsi_id_search_values + *id_search, char *serial, char *serial_short, + int max_len, char *wwn, + char *wwn_vendor_extension, char *tgpt_group) +{ + int i, j, s, len; + + /* + * ASSOCIATION must be with the device (value 0) + * or with the target port for SCSI_ID_TGTPORT + */ + if ((page_83[1] & 0x30) == 0x10) { + if (id_search->id_type != SCSI_ID_TGTGROUP) + return 1; + } else if ((page_83[1] & 0x30) != 0) { + return 1; + } + + if ((page_83[1] & 0x0f) != id_search->id_type) + return 1; + + /* + * Possibly check NAA sub-type. + */ + if ((id_search->naa_type != SCSI_ID_NAA_DONT_CARE) && + (id_search->naa_type != (page_83[4] & 0xf0) >> 4)) + return 1; + + /* + * Check for matching code set - ASCII or BINARY. + */ + if ((page_83[0] & 0x0f) != id_search->code_set) + return 1; + + /* + * page_83[3]: identifier length + */ + len = page_83[3]; + if ((page_83[0] & 0x0f) != SCSI_ID_ASCII) + /* + * If not ASCII, use two bytes for each binary value. + */ + len *= 2; + + /* + * Add one byte for the NUL termination, and one for the id_type. + */ + len += 2; + if (id_search->id_type == SCSI_ID_VENDOR_SPECIFIC) + len += VENDOR_LENGTH + MODEL_LENGTH; + + if (max_len < len) { + info(udev, "%s: length %d too short - need %d\n", + dev_scsi->kernel, max_len, len); + return 1; + } + + if (id_search->id_type == SCSI_ID_TGTGROUP && tgpt_group != NULL) { + unsigned int group; + + group = ((unsigned int)page_83[6] << 8) | page_83[7]; + sprintf(tgpt_group,"%x", group); + return 1; + } + + serial[0] = hex_str[id_search->id_type]; + + /* + * For SCSI_ID_VENDOR_SPECIFIC prepend the vendor and model before + * the id since it is not unique across all vendors and models, + * this differs from SCSI_ID_T10_VENDOR, where the vendor is + * included in the identifier. + */ + if (id_search->id_type == SCSI_ID_VENDOR_SPECIFIC) + if (prepend_vendor_model(udev, dev_scsi, &serial[1]) < 0) { + dbg(udev, "prepend failed\n"); + return 1; + } + + i = 4; /* offset to the start of the identifier */ + s = j = strlen(serial); + if ((page_83[0] & 0x0f) == SCSI_ID_ASCII) { + /* + * ASCII descriptor. + */ + while (i < (4 + page_83[3])) + serial[j++] = page_83[i++]; + } else { + /* + * Binary descriptor, convert to ASCII, using two bytes of + * ASCII for each byte in the page_83. + */ + while (i < (4 + page_83[3])) { + serial[j++] = hex_str[(page_83[i] & 0xf0) >> 4]; + serial[j++] = hex_str[page_83[i] & 0x0f]; + i++; + } + } + + strcpy(serial_short, &serial[s]); + + if (id_search->id_type == SCSI_ID_NAA && wwn != NULL) { + strncpy(wwn, &serial[s], 16); + if (wwn_vendor_extension != NULL) { + strncpy(wwn_vendor_extension, &serial[s + 16], 16); + } + } + + return 0; +} + +/* Extract the raw binary from VPD 0x83 pre-SPC devices */ +static int check_fill_0x83_prespc3(struct udev *udev, + struct scsi_id_device *dev_scsi, + unsigned char *page_83, + const struct scsi_id_search_values + *id_search, char *serial, char *serial_short, int max_len) +{ + int i, j; + + dbg(udev, "using pre-spc3-83 for %s\n", dev_scsi->kernel); + serial[0] = hex_str[id_search->id_type]; + /* serial has been memset to zero before */ + j = strlen(serial); /* j = 1; */ + + for (i = 0; (i < page_83[3]) && (j < max_len-3); ++i) { + serial[j++] = hex_str[(page_83[4+i] & 0xf0) >> 4]; + serial[j++] = hex_str[ page_83[4+i] & 0x0f]; + } + serial[max_len-1] = 0; + strncpy(serial_short, serial, max_len-1); + return 0; +} + + +/* Get device identification VPD page */ +static int do_scsi_page83_inquiry(struct udev *udev, + struct scsi_id_device *dev_scsi, int fd, + char *serial, char *serial_short, int len, + char *unit_serial_number, char *wwn, + char *wwn_vendor_extension, char *tgpt_group) +{ + int retval; + unsigned int id_ind, j; + unsigned char page_83[SCSI_INQ_BUFF_LEN]; + + /* also pick up the page 80 serial number */ + do_scsi_page80_inquiry(udev, dev_scsi, fd, NULL, unit_serial_number, MAX_SERIAL_LEN); + + memset(page_83, 0, SCSI_INQ_BUFF_LEN); + retval = scsi_inquiry(udev, dev_scsi, fd, 1, PAGE_83, page_83, + SCSI_INQ_BUFF_LEN); + if (retval < 0) + return 1; + + if (page_83[1] != PAGE_83) { + info(udev, "%s: Invalid page 0x83\n", dev_scsi->kernel); + return 1; + } + + /* + * XXX Some devices (IBM 3542) return all spaces for an identifier if + * the LUN is not actually configured. This leads to identifiers of + * the form: "1 ". + */ + + /* + * Model 4, 5, and (some) model 6 EMC Symmetrix devices return + * a page 83 reply according to SCSI-2 format instead of SPC-2/3. + * + * The SCSI-2 page 83 format returns an IEEE WWN in binary + * encoded hexi-decimal in the 16 bytes following the initial + * 4-byte page 83 reply header. + * + * Both the SPC-2 and SPC-3 formats return an IEEE WWN as part + * of an Identification descriptor. The 3rd byte of the first + * Identification descriptor is a reserved (BSZ) byte field. + * + * Reference the 7th byte of the page 83 reply to determine + * whether the reply is compliant with SCSI-2 or SPC-2/3 + * specifications. A zero value in the 7th byte indicates + * an SPC-2/3 conformant reply, (i.e., the reserved field of the + * first Identification descriptor). This byte will be non-zero + * for a SCSI-2 conformant page 83 reply from these EMC + * Symmetrix models since the 7th byte of the reply corresponds + * to the 4th and 5th nibbles of the 6-byte OUI for EMC, that is, + * 0x006048. + */ + + if (page_83[6] != 0) + return check_fill_0x83_prespc3(udev, + dev_scsi, page_83, id_search_list, + serial, serial_short, len); + + /* + * Search for a match in the prioritized id_search_list - since WWN ids + * come first we can pick up the WWN in check_fill_0x83_id(). + */ + for (id_ind = 0; + id_ind < sizeof(id_search_list)/sizeof(id_search_list[0]); + id_ind++) { + /* + * Examine each descriptor returned. There is normally only + * one or a small number of descriptors. + */ + for (j = 4; j <= (unsigned int)page_83[3] + 3; j += page_83[j + 3] + 4) { + retval = check_fill_0x83_id(udev, + dev_scsi, &page_83[j], + &id_search_list[id_ind], + serial, serial_short, len, + wwn, wwn_vendor_extension, + tgpt_group); + dbg(udev, "%s id desc %d/%d/%d\n", dev_scsi->kernel, + id_search_list[id_ind].id_type, + id_search_list[id_ind].naa_type, + id_search_list[id_ind].code_set); + if (!retval) { + dbg(udev, " used\n"); + return retval; + } else if (retval < 0) { + dbg(udev, " failed\n"); + return retval; + } else { + dbg(udev, " not used\n"); + } + } + } + return 1; +} + +/* + * Get device identification VPD page for older SCSI-2 device which is not + * compliant with either SPC-2 or SPC-3 format. + * + * Return the hard coded error code value 2 if the page 83 reply is not + * conformant to the SCSI-2 format. + */ +static int do_scsi_page83_prespc3_inquiry(struct udev *udev, + struct scsi_id_device *dev_scsi, int fd, + char *serial, char *serial_short, int len) +{ + int retval; + int i, j; + unsigned char page_83[SCSI_INQ_BUFF_LEN]; + + memset(page_83, 0, SCSI_INQ_BUFF_LEN); + retval = scsi_inquiry(udev, dev_scsi, fd, 1, PAGE_83, page_83, SCSI_INQ_BUFF_LEN); + if (retval < 0) + return 1; + + if (page_83[1] != PAGE_83) { + info(udev, "%s: Invalid page 0x83\n", dev_scsi->kernel); + return 1; + } + /* + * Model 4, 5, and (some) model 6 EMC Symmetrix devices return + * a page 83 reply according to SCSI-2 format instead of SPC-2/3. + * + * The SCSI-2 page 83 format returns an IEEE WWN in binary + * encoded hexi-decimal in the 16 bytes following the initial + * 4-byte page 83 reply header. + * + * Both the SPC-2 and SPC-3 formats return an IEEE WWN as part + * of an Identification descriptor. The 3rd byte of the first + * Identification descriptor is a reserved (BSZ) byte field. + * + * Reference the 7th byte of the page 83 reply to determine + * whether the reply is compliant with SCSI-2 or SPC-2/3 + * specifications. A zero value in the 7th byte indicates + * an SPC-2/3 conformant reply, (i.e., the reserved field of the + * first Identification descriptor). This byte will be non-zero + * for a SCSI-2 conformant page 83 reply from these EMC + * Symmetrix models since the 7th byte of the reply corresponds + * to the 4th and 5th nibbles of the 6-byte OUI for EMC, that is, + * 0x006048. + */ + if (page_83[6] == 0) + return 2; + + serial[0] = hex_str[id_search_list[0].id_type]; + /* + * The first four bytes contain data, not a descriptor. + */ + i = 4; + j = strlen(serial); + /* + * Binary descriptor, convert to ASCII, + * using two bytes of ASCII for each byte + * in the page_83. + */ + while (i < (page_83[3]+4)) { + serial[j++] = hex_str[(page_83[i] & 0xf0) >> 4]; + serial[j++] = hex_str[page_83[i] & 0x0f]; + i++; + } + dbg(udev, "using pre-spc3-83 for %s\n", dev_scsi->kernel); + return 0; +} + +/* Get unit serial number VPD page */ +static int do_scsi_page80_inquiry(struct udev *udev, + struct scsi_id_device *dev_scsi, int fd, + char *serial, char *serial_short, int max_len) +{ + int retval; + int ser_ind; + int i; + int len; + unsigned char buf[SCSI_INQ_BUFF_LEN]; + + memset(buf, 0, SCSI_INQ_BUFF_LEN); + retval = scsi_inquiry(udev, dev_scsi, fd, 1, PAGE_80, buf, SCSI_INQ_BUFF_LEN); + if (retval < 0) + return retval; + + if (buf[1] != PAGE_80) { + info(udev, "%s: Invalid page 0x80\n", dev_scsi->kernel); + return 1; + } + + len = 1 + VENDOR_LENGTH + MODEL_LENGTH + buf[3]; + if (max_len < len) { + info(udev, "%s: length %d too short - need %d\n", + dev_scsi->kernel, max_len, len); + return 1; + } + /* + * Prepend 'S' to avoid unlikely collision with page 0x83 vendor + * specific type where we prepend '0' + vendor + model. + */ + len = buf[3]; + if (serial != NULL) { + serial[0] = 'S'; + ser_ind = prepend_vendor_model(udev, dev_scsi, &serial[1]); + if (ser_ind < 0) + return 1; + for (i = 4; i < len + 4; i++, ser_ind++) + serial[ser_ind] = buf[i]; + } + if (serial_short != NULL) { + memcpy(serial_short, &buf[4], len); + serial_short[len] = '\0'; + } + return 0; +} + +int scsi_std_inquiry(struct udev *udev, + struct scsi_id_device *dev_scsi, const char *devname) +{ + int fd; + unsigned char buf[SCSI_INQ_BUFF_LEN]; + struct stat statbuf; + int err = 0; + + dbg(udev, "opening %s\n", devname); + fd = open(devname, O_RDONLY | O_NONBLOCK); + if (fd < 0) { + info(udev, "scsi_id: cannot open %s: %s\n", + devname, strerror(errno)); + return 1; + } + + if (fstat(fd, &statbuf) < 0) { + info(udev, "scsi_id: cannot stat %s: %s\n", + devname, strerror(errno)); + err = 2; + goto out; + } + sprintf(dev_scsi->kernel,"%d:%d", major(statbuf.st_rdev), + minor(statbuf.st_rdev)); + + memset(buf, 0, SCSI_INQ_BUFF_LEN); + err = scsi_inquiry(udev, dev_scsi, fd, 0, 0, buf, SCSI_INQ_BUFF_LEN); + if (err < 0) + goto out; + + err = 0; + memcpy(dev_scsi->vendor, buf + 8, 8); + dev_scsi->vendor[8] = '\0'; + memcpy(dev_scsi->model, buf + 16, 16); + dev_scsi->model[16] = '\0'; + memcpy(dev_scsi->revision, buf + 32, 4); + dev_scsi->revision[4] = '\0'; + sprintf(dev_scsi->type,"%x", buf[0] & 0x1f); + +out: + close(fd); + return err; +} + +int scsi_get_serial(struct udev *udev, + struct scsi_id_device *dev_scsi, const char *devname, + int page_code, int len) +{ + unsigned char page0[SCSI_INQ_BUFF_LEN]; + int fd = -1; + int cnt; + int ind; + int retval; + + memset(dev_scsi->serial, 0, len); + dbg(udev, "opening %s\n", devname); + srand((unsigned int)getpid()); + for (cnt = 20; cnt > 0; cnt--) { + struct timespec duration; + + fd = open(devname, O_RDONLY | O_NONBLOCK); + if (fd >= 0 || errno != EBUSY) + break; + duration.tv_sec = 0; + duration.tv_nsec = (200 * 1000 * 1000) + (rand() % 100 * 1000 * 1000); + nanosleep(&duration, NULL); + } + if (fd < 0) + return 1; + + if (page_code == PAGE_80) { + if (do_scsi_page80_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len)) { + retval = 1; + goto completed; + } else { + retval = 0; + goto completed; + } + } else if (page_code == PAGE_83) { + if (do_scsi_page83_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len, dev_scsi->unit_serial_number, dev_scsi->wwn, dev_scsi->wwn_vendor_extension, dev_scsi->tgpt_group)) { + retval = 1; + goto completed; + } else { + retval = 0; + goto completed; + } + } else if (page_code == PAGE_83_PRE_SPC3) { + retval = do_scsi_page83_prespc3_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len); + if (retval) { + /* + * Fallback to servicing a SPC-2/3 compliant page 83 + * inquiry if the page 83 reply format does not + * conform to pre-SPC3 expectations. + */ + if (retval == 2) { + if (do_scsi_page83_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len, dev_scsi->unit_serial_number, dev_scsi->wwn, dev_scsi->wwn_vendor_extension, dev_scsi->tgpt_group)) { + retval = 1; + goto completed; + } else { + retval = 0; + goto completed; + } + } + else { + retval = 1; + goto completed; + } + } else { + retval = 0; + goto completed; + } + } else if (page_code != 0x00) { + info(udev, "%s: unsupported page code 0x%d\n", dev_scsi->kernel, page_code); + return 1; + } + + /* + * Get page 0, the page of the pages. By default, try from best to + * worst of supported pages: 0x83 then 0x80. + */ + if (do_scsi_page0_inquiry(udev, dev_scsi, fd, page0, SCSI_INQ_BUFF_LEN)) { + /* + * Don't try anything else. Black list if a specific page + * should be used for this vendor+model, or maybe have an + * optional fall-back to page 0x80 or page 0x83. + */ + retval = 1; + goto completed; + } + + dbg(udev, "%s: Checking page0\n", dev_scsi->kernel); + + for (ind = 4; ind <= page0[3] + 3; ind++) + if (page0[ind] == PAGE_83) + if (!do_scsi_page83_inquiry(udev, dev_scsi, fd, + dev_scsi->serial, dev_scsi->serial_short, len, dev_scsi->unit_serial_number, dev_scsi->wwn, dev_scsi->wwn_vendor_extension, dev_scsi->tgpt_group)) { + /* + * Success + */ + retval = 0; + goto completed; + } + + for (ind = 4; ind <= page0[3] + 3; ind++) + if (page0[ind] == PAGE_80) + if (!do_scsi_page80_inquiry(udev, dev_scsi, fd, + dev_scsi->serial, dev_scsi->serial_short, len)) { + /* + * Success + */ + retval = 0; + goto completed; + } + retval = 1; + +completed: + close(fd); + return retval; +} diff --git a/src/udev/src/.gitignore b/src/udev/src/.gitignore deleted file mode 100644 index beb8604bc6..0000000000 --- a/src/udev/src/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -*.[78] -*.html -udev.pc -libudev.pc -udev*.service diff --git a/src/udev/src/COPYING b/src/udev/src/COPYING deleted file mode 100644 index d2e31278b0..0000000000 --- a/src/udev/src/COPYING +++ /dev/null @@ -1,502 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/src/udev/src/accelerometer/61-accelerometer.rules b/src/udev/src/accelerometer/61-accelerometer.rules deleted file mode 100644 index a6a2bfd088..0000000000 --- a/src/udev/src/accelerometer/61-accelerometer.rules +++ /dev/null @@ -1,3 +0,0 @@ -# do not edit this file, it will be overwritten on update - -SUBSYSTEM=="input", ACTION!="remove", ENV{ID_INPUT_ACCELEROMETER}=="1", IMPORT{program}="accelerometer %p" diff --git a/src/udev/src/accelerometer/accelerometer.c b/src/udev/src/accelerometer/accelerometer.c deleted file mode 100644 index bc9715b264..0000000000 --- a/src/udev/src/accelerometer/accelerometer.c +++ /dev/null @@ -1,357 +0,0 @@ -/* - * accelerometer - exports device orientation through property - * - * When an "change" event is received on an accelerometer, - * open its device node, and from the value, as well as the previous - * value of the property, calculate the device's new orientation, - * and export it as ID_INPUT_ACCELEROMETER_ORIENTATION. - * - * Possible values are: - * undefined - * * normal - * * bottom-up - * * left-up - * * right-up - * - * The property will be persistent across sessions, and the new - * orientations can be deducted from the previous one (it allows - * for a threshold for switching between opposite ends of the - * orientation). - * - * Copyright (C) 2011 Red Hat, Inc. - * Author: - * Bastien Nocera - * - * orientation_calc() from the sensorfw package - * Copyright (C) 2009-2010 Nokia Corporation - * Authors: - * Üstün Ergenoglu - * Timo Rongas - * Lihan Guo - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with keymap; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libudev.h" -#include "libudev-private.h" - -/* we must use this kernel-compatible implementation */ -#define BITS_PER_LONG (sizeof(unsigned long) * 8) -#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) -#define OFF(x) ((x)%BITS_PER_LONG) -#define BIT(x) (1UL<> OFF(bit)) & 1) - -static int debug = 0; - -static void log_fn(struct udev *udev, int priority, - const char *file, int line, const char *fn, - const char *format, va_list args) -{ - if (debug) { - fprintf(stderr, "%s: ", fn); - vfprintf(stderr, format, args); - } else { - vsyslog(priority, format, args); - } -} - -typedef enum { - ORIENTATION_UNDEFINED, - ORIENTATION_NORMAL, - ORIENTATION_BOTTOM_UP, - ORIENTATION_LEFT_UP, - ORIENTATION_RIGHT_UP -} OrientationUp; - -static const char *orientations[] = { - "undefined", - "normal", - "bottom-up", - "left-up", - "right-up", - NULL -}; - -#define ORIENTATION_UP_UP ORIENTATION_NORMAL - -#define DEFAULT_THRESHOLD 250 -#define RADIANS_TO_DEGREES 180.0/M_PI -#define SAME_AXIS_LIMIT 5 - -#define THRESHOLD_LANDSCAPE 25 -#define THRESHOLD_PORTRAIT 20 - -static const char * -orientation_to_string (OrientationUp o) -{ - return orientations[o]; -} - -static OrientationUp -string_to_orientation (const char *orientation) -{ - int i; - - if (orientation == NULL) - return ORIENTATION_UNDEFINED; - for (i = 0; orientations[i] != NULL; i++) { - if (strcmp (orientation, orientations[i]) == 0) - return i; - } - return ORIENTATION_UNDEFINED; -} - -static OrientationUp -orientation_calc (OrientationUp prev, - int x, int y, int z) -{ - int rotation; - OrientationUp ret = prev; - - /* Portrait check */ - rotation = round(atan((double) x / sqrt(y * y + z * z)) * RADIANS_TO_DEGREES); - - if (abs(rotation) > THRESHOLD_PORTRAIT) { - ret = (rotation < 0) ? ORIENTATION_LEFT_UP : ORIENTATION_RIGHT_UP; - - /* Some threshold to switching between portrait modes */ - if (prev == ORIENTATION_LEFT_UP || prev == ORIENTATION_RIGHT_UP) { - if (abs(rotation) < SAME_AXIS_LIMIT) { - ret = prev; - } - } - - } else { - /* Landscape check */ - rotation = round(atan((double) y / sqrt(x * x + z * z)) * RADIANS_TO_DEGREES); - - if (abs(rotation) > THRESHOLD_LANDSCAPE) { - ret = (rotation < 0) ? ORIENTATION_BOTTOM_UP : ORIENTATION_NORMAL; - - /* Some threshold to switching between landscape modes */ - if (prev == ORIENTATION_BOTTOM_UP || prev == ORIENTATION_NORMAL) { - if (abs(rotation) < SAME_AXIS_LIMIT) { - ret = prev; - } - } - } - } - - return ret; -} - -static OrientationUp -get_prev_orientation(struct udev_device *dev) -{ - const char *value; - - value = udev_device_get_property_value(dev, "ID_INPUT_ACCELEROMETER_ORIENTATION"); - if (value == NULL) - return ORIENTATION_UNDEFINED; - return string_to_orientation(value); -} - -#define SET_AXIS(axis, code_) if (ev[i].code == code_) { if (got_##axis == 0) { axis = ev[i].value; got_##axis = 1; } } - -/* accelerometers */ -static void test_orientation(struct udev *udev, - struct udev_device *dev, - const char *devpath) -{ - OrientationUp old, new; - int fd, r; - struct input_event ev[64]; - int got_syn = 0; - int got_x, got_y, got_z; - int x = 0, y = 0, z = 0; - char text[64]; - - old = get_prev_orientation(dev); - - if ((fd = open(devpath, O_RDONLY)) < 0) - return; - - got_x = got_y = got_z = 0; - - while (1) { - int i; - - r = read(fd, ev, sizeof(struct input_event) * 64); - - if (r < (int) sizeof(struct input_event)) - return; - - for (i = 0; i < r / (int) sizeof(struct input_event); i++) { - if (got_syn == 1) { - if (ev[i].type == EV_ABS) { - SET_AXIS(x, ABS_X); - SET_AXIS(y, ABS_Y); - SET_AXIS(z, ABS_Z); - } - } - if (ev[i].type == EV_SYN && ev[i].code == SYN_REPORT) { - got_syn = 1; - } - if (got_x && got_y && got_z) - goto read_dev; - } - } - -read_dev: - close(fd); - - if (!got_x || !got_y || !got_z) - return; - - new = orientation_calc(old, x, y, z); - snprintf(text, sizeof(text), "ID_INPUT_ACCELEROMETER_ORIENTATION=%s", orientation_to_string(new)); - puts(text); -} - -static void help(void) -{ - printf("Usage: accelerometer [options] \n" - " --debug debug to stderr\n" - " --help print this help text\n\n"); -} - -int main (int argc, char** argv) -{ - struct udev *udev; - struct udev_device *dev; - - static const struct option options[] = { - { "debug", no_argument, NULL, 'd' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - - char devpath[PATH_MAX]; - char *devnode; - const char *id_path; - struct udev_enumerate *enumerate; - struct udev_list_entry *list_entry; - - udev = udev_new(); - if (udev == NULL) - return 1; - - udev_log_init("input_id"); - udev_set_log_fn(udev, log_fn); - - /* CLI argument parsing */ - while (1) { - int option; - - option = getopt_long(argc, argv, "dxh", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'd': - debug = 1; - if (udev_get_log_priority(udev) < LOG_INFO) - udev_set_log_priority(udev, LOG_INFO); - break; - case 'h': - help(); - exit(0); - default: - exit(1); - } - } - - if (argv[optind] == NULL) { - help(); - exit(1); - } - - /* get the device */ - snprintf(devpath, sizeof(devpath), "%s/%s", udev_get_sys_path(udev), argv[optind]); - dev = udev_device_new_from_syspath(udev, devpath); - if (dev == NULL) { - fprintf(stderr, "unable to access '%s'\n", devpath); - return 1; - } - - id_path = udev_device_get_property_value(dev, "ID_PATH"); - if (id_path == NULL) { - fprintf (stderr, "unable to get property ID_PATH for '%s'", devpath); - return 0; - } - - /* Get the children devices and find the devnode - * FIXME: use udev_enumerate_add_match_children() instead - * when it's available */ - devnode = NULL; - enumerate = udev_enumerate_new(udev); - udev_enumerate_add_match_property(enumerate, "ID_PATH", id_path); - udev_enumerate_add_match_subsystem(enumerate, "input"); - udev_enumerate_scan_devices(enumerate); - udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(enumerate)) { - struct udev_device *device; - const char *node; - - device = udev_device_new_from_syspath(udev_enumerate_get_udev(enumerate), - udev_list_entry_get_name(list_entry)); - if (device == NULL) - continue; - /* Already found it */ - if (devnode != NULL) { - udev_device_unref(device); - continue; - } - - node = udev_device_get_devnode(device); - if (node == NULL) { - udev_device_unref(device); - continue; - } - /* Use the event sub-device */ - if (strstr(node, "/event") == NULL) { - udev_device_unref(device); - continue; - } - - devnode = strdup(node); - udev_device_unref(device); - } - - if (devnode == NULL) { - fprintf(stderr, "unable to get device node for '%s'\n", devpath); - return 0; - } - - info(udev, "Opening accelerometer device %s\n", devnode); - test_orientation(udev, dev, devnode); - free(devnode); - - return 0; -} diff --git a/src/udev/src/ata_id/ata_id.c b/src/udev/src/ata_id/ata_id.c deleted file mode 100644 index 846a73b547..0000000000 --- a/src/udev/src/ata_id/ata_id.c +++ /dev/null @@ -1,721 +0,0 @@ -/* - * ata_id - reads product/serial number from ATA drives - * - * Copyright (C) 2005-2008 Kay Sievers - * Copyright (C) 2009 Lennart Poettering - * Copyright (C) 2009-2010 David Zeuthen - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libudev.h" -#include "libudev-private.h" - -#define COMMAND_TIMEOUT_MSEC (30 * 1000) - -static int disk_scsi_inquiry_command(int fd, - void *buf, - size_t buf_len) -{ - struct sg_io_v4 io_v4; - uint8_t cdb[6]; - uint8_t sense[32]; - int ret; - - /* - * INQUIRY, see SPC-4 section 6.4 - */ - memset(cdb, 0, sizeof(cdb)); - cdb[0] = 0x12; /* OPERATION CODE: INQUIRY */ - cdb[3] = (buf_len >> 8); /* ALLOCATION LENGTH */ - cdb[4] = (buf_len & 0xff); - - memset(sense, 0, sizeof(sense)); - - memset(&io_v4, 0, sizeof(struct sg_io_v4)); - io_v4.guard = 'Q'; - io_v4.protocol = BSG_PROTOCOL_SCSI; - io_v4.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD; - io_v4.request_len = sizeof (cdb); - io_v4.request = (uintptr_t) cdb; - io_v4.max_response_len = sizeof (sense); - io_v4.response = (uintptr_t) sense; - io_v4.din_xfer_len = buf_len; - io_v4.din_xferp = (uintptr_t) buf; - io_v4.timeout = COMMAND_TIMEOUT_MSEC; - - ret = ioctl(fd, SG_IO, &io_v4); - if (ret != 0) { - /* could be that the driver doesn't do version 4, try version 3 */ - if (errno == EINVAL) { - struct sg_io_hdr io_hdr; - - memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); - io_hdr.interface_id = 'S'; - io_hdr.cmdp = (unsigned char*) cdb; - io_hdr.cmd_len = sizeof (cdb); - io_hdr.dxferp = buf; - io_hdr.dxfer_len = buf_len; - io_hdr.sbp = sense; - io_hdr.mx_sb_len = sizeof (sense); - io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; - io_hdr.timeout = COMMAND_TIMEOUT_MSEC; - - ret = ioctl(fd, SG_IO, &io_hdr); - if (ret != 0) - goto out; - - /* even if the ioctl succeeds, we need to check the return value */ - if (!(io_hdr.status == 0 && - io_hdr.host_status == 0 && - io_hdr.driver_status == 0)) { - errno = EIO; - ret = -1; - goto out; - } - } else { - goto out; - } - } - - /* even if the ioctl succeeds, we need to check the return value */ - if (!(io_v4.device_status == 0 && - io_v4.transport_status == 0 && - io_v4.driver_status == 0)) { - errno = EIO; - ret = -1; - goto out; - } - - out: - return ret; -} - -static int disk_identify_command(int fd, - void *buf, - size_t buf_len) -{ - struct sg_io_v4 io_v4; - uint8_t cdb[12]; - uint8_t sense[32]; - uint8_t *desc = sense+8; - int ret; - - /* - * ATA Pass-Through 12 byte command, as described in - * - * T10 04-262r8 ATA Command Pass-Through - * - * from http://www.t10.org/ftp/t10/document.04/04-262r8.pdf - */ - memset(cdb, 0, sizeof(cdb)); - cdb[0] = 0xa1; /* OPERATION CODE: 12 byte pass through */ - cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */ - cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */ - cdb[3] = 0; /* FEATURES */ - cdb[4] = 1; /* SECTORS */ - cdb[5] = 0; /* LBA LOW */ - cdb[6] = 0; /* LBA MID */ - cdb[7] = 0; /* LBA HIGH */ - cdb[8] = 0 & 0x4F; /* SELECT */ - cdb[9] = 0xEC; /* Command: ATA IDENTIFY DEVICE */; - memset(sense, 0, sizeof(sense)); - - memset(&io_v4, 0, sizeof(struct sg_io_v4)); - io_v4.guard = 'Q'; - io_v4.protocol = BSG_PROTOCOL_SCSI; - io_v4.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD; - io_v4.request_len = sizeof (cdb); - io_v4.request = (uintptr_t) cdb; - io_v4.max_response_len = sizeof (sense); - io_v4.response = (uintptr_t) sense; - io_v4.din_xfer_len = buf_len; - io_v4.din_xferp = (uintptr_t) buf; - io_v4.timeout = COMMAND_TIMEOUT_MSEC; - - ret = ioctl(fd, SG_IO, &io_v4); - if (ret != 0) { - /* could be that the driver doesn't do version 4, try version 3 */ - if (errno == EINVAL) { - struct sg_io_hdr io_hdr; - - memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); - io_hdr.interface_id = 'S'; - io_hdr.cmdp = (unsigned char*) cdb; - io_hdr.cmd_len = sizeof (cdb); - io_hdr.dxferp = buf; - io_hdr.dxfer_len = buf_len; - io_hdr.sbp = sense; - io_hdr.mx_sb_len = sizeof (sense); - io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; - io_hdr.timeout = COMMAND_TIMEOUT_MSEC; - - ret = ioctl(fd, SG_IO, &io_hdr); - if (ret != 0) - goto out; - } else { - goto out; - } - } - - if (!(sense[0] == 0x72 && desc[0] == 0x9 && desc[1] == 0x0c)) { - errno = EIO; - ret = -1; - goto out; - } - - out: - return ret; -} - -static int disk_identify_packet_device_command(int fd, - void *buf, - size_t buf_len) -{ - struct sg_io_v4 io_v4; - uint8_t cdb[16]; - uint8_t sense[32]; - uint8_t *desc = sense+8; - int ret; - - /* - * ATA Pass-Through 16 byte command, as described in - * - * T10 04-262r8 ATA Command Pass-Through - * - * from http://www.t10.org/ftp/t10/document.04/04-262r8.pdf - */ - memset(cdb, 0, sizeof(cdb)); - cdb[0] = 0x85; /* OPERATION CODE: 16 byte pass through */ - cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */ - cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */ - cdb[3] = 0; /* FEATURES */ - cdb[4] = 0; /* FEATURES */ - cdb[5] = 0; /* SECTORS */ - cdb[6] = 1; /* SECTORS */ - cdb[7] = 0; /* LBA LOW */ - cdb[8] = 0; /* LBA LOW */ - cdb[9] = 0; /* LBA MID */ - cdb[10] = 0; /* LBA MID */ - cdb[11] = 0; /* LBA HIGH */ - cdb[12] = 0; /* LBA HIGH */ - cdb[13] = 0; /* DEVICE */ - cdb[14] = 0xA1; /* Command: ATA IDENTIFY PACKET DEVICE */; - cdb[15] = 0; /* CONTROL */ - memset(sense, 0, sizeof(sense)); - - memset(&io_v4, 0, sizeof(struct sg_io_v4)); - io_v4.guard = 'Q'; - io_v4.protocol = BSG_PROTOCOL_SCSI; - io_v4.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD; - io_v4.request_len = sizeof (cdb); - io_v4.request = (uintptr_t) cdb; - io_v4.max_response_len = sizeof (sense); - io_v4.response = (uintptr_t) sense; - io_v4.din_xfer_len = buf_len; - io_v4.din_xferp = (uintptr_t) buf; - io_v4.timeout = COMMAND_TIMEOUT_MSEC; - - ret = ioctl(fd, SG_IO, &io_v4); - if (ret != 0) { - /* could be that the driver doesn't do version 4, try version 3 */ - if (errno == EINVAL) { - struct sg_io_hdr io_hdr; - - memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); - io_hdr.interface_id = 'S'; - io_hdr.cmdp = (unsigned char*) cdb; - io_hdr.cmd_len = sizeof (cdb); - io_hdr.dxferp = buf; - io_hdr.dxfer_len = buf_len; - io_hdr.sbp = sense; - io_hdr.mx_sb_len = sizeof (sense); - io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; - io_hdr.timeout = COMMAND_TIMEOUT_MSEC; - - ret = ioctl(fd, SG_IO, &io_hdr); - if (ret != 0) - goto out; - } else { - goto out; - } - } - - if (!(sense[0] == 0x72 && desc[0] == 0x9 && desc[1] == 0x0c)) { - errno = EIO; - ret = -1; - goto out; - } - - out: - return ret; -} - -/** - * disk_identify_get_string: - * @identify: A block of IDENTIFY data - * @offset_words: Offset of the string to get, in words. - * @dest: Destination buffer for the string. - * @dest_len: Length of destination buffer, in bytes. - * - * Copies the ATA string from @identify located at @offset_words into @dest. - */ -static void disk_identify_get_string(uint8_t identify[512], - unsigned int offset_words, - char *dest, - size_t dest_len) -{ - unsigned int c1; - unsigned int c2; - - assert(identify != NULL); - assert(dest != NULL); - assert((dest_len & 1) == 0); - - while (dest_len > 0) { - c1 = identify[offset_words * 2 + 1]; - c2 = identify[offset_words * 2]; - *dest = c1; - dest++; - *dest = c2; - dest++; - offset_words++; - dest_len -= 2; - } -} - -static void disk_identify_fixup_string(uint8_t identify[512], - unsigned int offset_words, - size_t len) -{ - disk_identify_get_string(identify, offset_words, - (char *) identify + offset_words * 2, len); -} - -static void disk_identify_fixup_uint16 (uint8_t identify[512], unsigned int offset_words) -{ - uint16_t *p; - - p = (uint16_t *) identify; - p[offset_words] = le16toh (p[offset_words]); -} - -/** - * disk_identify: - * @udev: The libudev context. - * @fd: File descriptor for the block device. - * @out_identify: Return location for IDENTIFY data. - * @out_is_packet_device: Return location for whether returned data is from a IDENTIFY PACKET DEVICE. - * - * Sends the IDENTIFY DEVICE or IDENTIFY PACKET DEVICE command to the - * device represented by @fd. If successful, then the result will be - * copied into @out_identify and @out_is_packet_device. - * - * This routine is based on code from libatasmart, Copyright 2008 - * Lennart Poettering, LGPL v2.1. - * - * Returns: 0 if the data was successfully obtained, otherwise - * non-zero with errno set. - */ -static int disk_identify(struct udev *udev, - int fd, - uint8_t out_identify[512], - int *out_is_packet_device) -{ - int ret; - uint8_t inquiry_buf[36]; - int peripheral_device_type; - int all_nul_bytes; - int n; - int is_packet_device; - - assert(out_identify != NULL); - - /* init results */ - ret = -1; - memset(out_identify, '\0', 512); - is_packet_device = 0; - - /* If we were to use ATA PASS_THROUGH (12) on an ATAPI device - * we could accidentally blank media. This is because MMC's BLANK - * command has the same op-code (0x61). - * - * To prevent this from happening we bail out if the device - * isn't a Direct Access Block Device, e.g. SCSI type 0x00 - * (CD/DVD devices are type 0x05). So we send a SCSI INQUIRY - * command first... libata is handling this via its SCSI - * emulation layer. - * - * This also ensures that we're actually dealing with a device - * that understands SCSI commands. - * - * (Yes, it is a bit perverse that we're tunneling the ATA - * command through SCSI and relying on the ATA driver - * emulating SCSI well-enough...) - * - * (See commit 160b069c25690bfb0c785994c7c3710289179107 for - * the original bug-fix and see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=556635 - * for the original bug-report.) - */ - ret = disk_scsi_inquiry_command (fd, inquiry_buf, sizeof (inquiry_buf)); - if (ret != 0) - goto out; - - /* SPC-4, section 6.4.2: Standard INQUIRY data */ - peripheral_device_type = inquiry_buf[0] & 0x1f; - if (peripheral_device_type == 0x05) - { - is_packet_device = 1; - ret = disk_identify_packet_device_command(fd, out_identify, 512); - goto check_nul_bytes; - } - if (peripheral_device_type != 0x00) { - ret = -1; - errno = EIO; - goto out; - } - - /* OK, now issue the IDENTIFY DEVICE command */ - ret = disk_identify_command(fd, out_identify, 512); - if (ret != 0) - goto out; - - check_nul_bytes: - /* Check if IDENTIFY data is all NUL bytes - if so, bail */ - all_nul_bytes = 1; - for (n = 0; n < 512; n++) { - if (out_identify[n] != '\0') { - all_nul_bytes = 0; - break; - } - } - - if (all_nul_bytes) { - ret = -1; - errno = EIO; - goto out; - } - -out: - if (out_is_packet_device != NULL) - *out_is_packet_device = is_packet_device; - return ret; -} - -static void log_fn(struct udev *udev, int priority, - const char *file, int line, const char *fn, - const char *format, va_list args) -{ - vsyslog(priority, format, args); -} - -int main(int argc, char *argv[]) -{ - struct udev *udev; - struct hd_driveid id; - uint8_t identify[512]; - uint16_t *identify_words; - char model[41]; - char model_enc[256]; - char serial[21]; - char revision[9]; - const char *node = NULL; - int export = 0; - int fd; - uint16_t word; - int rc = 0; - int is_packet_device = 0; - static const struct option options[] = { - { "export", no_argument, NULL, 'x' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - - udev = udev_new(); - if (udev == NULL) - goto exit; - - udev_log_init("ata_id"); - udev_set_log_fn(udev, log_fn); - - while (1) { - int option; - - option = getopt_long(argc, argv, "xh", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'x': - export = 1; - break; - case 'h': - printf("Usage: ata_id [--export] [--help] \n" - " --export print values as environment keys\n" - " --help print this help text\n\n"); - goto exit; - } - } - - node = argv[optind]; - if (node == NULL) { - err(udev, "no node specified\n"); - rc = 1; - goto exit; - } - - fd = open(node, O_RDONLY|O_NONBLOCK); - if (fd < 0) { - err(udev, "unable to open '%s'\n", node); - rc = 1; - goto exit; - } - - if (disk_identify(udev, fd, identify, &is_packet_device) == 0) { - /* - * fix up only the fields from the IDENTIFY data that we are going to - * use and copy it into the hd_driveid struct for convenience - */ - disk_identify_fixup_string (identify, 10, 20); /* serial */ - disk_identify_fixup_string (identify, 23, 6); /* fwrev */ - disk_identify_fixup_string (identify, 27, 40); /* model */ - disk_identify_fixup_uint16 (identify, 0); /* configuration */ - disk_identify_fixup_uint16 (identify, 75); /* queue depth */ - disk_identify_fixup_uint16 (identify, 75); /* SATA capabilities */ - disk_identify_fixup_uint16 (identify, 82); /* command set supported */ - disk_identify_fixup_uint16 (identify, 83); /* command set supported */ - disk_identify_fixup_uint16 (identify, 84); /* command set supported */ - disk_identify_fixup_uint16 (identify, 85); /* command set supported */ - disk_identify_fixup_uint16 (identify, 86); /* command set supported */ - disk_identify_fixup_uint16 (identify, 87); /* command set supported */ - disk_identify_fixup_uint16 (identify, 89); /* time required for SECURITY ERASE UNIT */ - disk_identify_fixup_uint16 (identify, 90); /* time required for enhanced SECURITY ERASE UNIT */ - disk_identify_fixup_uint16 (identify, 91); /* current APM values */ - disk_identify_fixup_uint16 (identify, 94); /* current AAM value */ - disk_identify_fixup_uint16 (identify, 128); /* device lock function */ - disk_identify_fixup_uint16 (identify, 217); /* nominal media rotation rate */ - memcpy(&id, identify, sizeof id); - } else { - /* If this fails, then try HDIO_GET_IDENTITY */ - if (ioctl(fd, HDIO_GET_IDENTITY, &id) != 0) { - info(udev, "HDIO_GET_IDENTITY failed for '%s': %m\n", node); - rc = 2; - goto close; - } - } - identify_words = (uint16_t *) identify; - - memcpy (model, id.model, 40); - model[40] = '\0'; - udev_util_encode_string(model, model_enc, sizeof(model_enc)); - util_replace_whitespace((char *) id.model, model, 40); - util_replace_chars(model, NULL); - util_replace_whitespace((char *) id.serial_no, serial, 20); - util_replace_chars(serial, NULL); - util_replace_whitespace((char *) id.fw_rev, revision, 8); - util_replace_chars(revision, NULL); - - if (export) { - /* Set this to convey the disk speaks the ATA protocol */ - printf("ID_ATA=1\n"); - - if ((id.config >> 8) & 0x80) { - /* This is an ATAPI device */ - switch ((id.config >> 8) & 0x1f) { - case 0: - printf("ID_TYPE=cd\n"); - break; - case 1: - printf("ID_TYPE=tape\n"); - break; - case 5: - printf("ID_TYPE=cd\n"); - break; - case 7: - printf("ID_TYPE=optical\n"); - break; - default: - printf("ID_TYPE=generic\n"); - break; - } - } else { - printf("ID_TYPE=disk\n"); - } - printf("ID_BUS=ata\n"); - printf("ID_MODEL=%s\n", model); - printf("ID_MODEL_ENC=%s\n", model_enc); - printf("ID_REVISION=%s\n", revision); - if (serial[0] != '\0') { - printf("ID_SERIAL=%s_%s\n", model, serial); - printf("ID_SERIAL_SHORT=%s\n", serial); - } else { - printf("ID_SERIAL=%s\n", model); - } - - if (id.command_set_1 & (1<<5)) { - printf ("ID_ATA_WRITE_CACHE=1\n"); - printf ("ID_ATA_WRITE_CACHE_ENABLED=%d\n", (id.cfs_enable_1 & (1<<5)) ? 1 : 0); - } - if (id.command_set_1 & (1<<10)) { - printf("ID_ATA_FEATURE_SET_HPA=1\n"); - printf("ID_ATA_FEATURE_SET_HPA_ENABLED=%d\n", (id.cfs_enable_1 & (1<<10)) ? 1 : 0); - - /* - * TODO: use the READ NATIVE MAX ADDRESS command to get the native max address - * so it is easy to check whether the protected area is in use. - */ - } - if (id.command_set_1 & (1<<3)) { - printf("ID_ATA_FEATURE_SET_PM=1\n"); - printf("ID_ATA_FEATURE_SET_PM_ENABLED=%d\n", (id.cfs_enable_1 & (1<<3)) ? 1 : 0); - } - if (id.command_set_1 & (1<<1)) { - printf("ID_ATA_FEATURE_SET_SECURITY=1\n"); - printf("ID_ATA_FEATURE_SET_SECURITY_ENABLED=%d\n", (id.cfs_enable_1 & (1<<1)) ? 1 : 0); - printf("ID_ATA_FEATURE_SET_SECURITY_ERASE_UNIT_MIN=%d\n", id.trseuc * 2); - if ((id.cfs_enable_1 & (1<<1))) /* enabled */ { - if (id.dlf & (1<<8)) - printf("ID_ATA_FEATURE_SET_SECURITY_LEVEL=maximum\n"); - else - printf("ID_ATA_FEATURE_SET_SECURITY_LEVEL=high\n"); - } - if (id.dlf & (1<<5)) - printf("ID_ATA_FEATURE_SET_SECURITY_ENHANCED_ERASE_UNIT_MIN=%d\n", id.trsEuc * 2); - if (id.dlf & (1<<4)) - printf("ID_ATA_FEATURE_SET_SECURITY_EXPIRE=1\n"); - if (id.dlf & (1<<3)) - printf("ID_ATA_FEATURE_SET_SECURITY_FROZEN=1\n"); - if (id.dlf & (1<<2)) - printf("ID_ATA_FEATURE_SET_SECURITY_LOCKED=1\n"); - } - if (id.command_set_1 & (1<<0)) { - printf("ID_ATA_FEATURE_SET_SMART=1\n"); - printf("ID_ATA_FEATURE_SET_SMART_ENABLED=%d\n", (id.cfs_enable_1 & (1<<0)) ? 1 : 0); - } - if (id.command_set_2 & (1<<9)) { - printf("ID_ATA_FEATURE_SET_AAM=1\n"); - printf("ID_ATA_FEATURE_SET_AAM_ENABLED=%d\n", (id.cfs_enable_2 & (1<<9)) ? 1 : 0); - printf("ID_ATA_FEATURE_SET_AAM_VENDOR_RECOMMENDED_VALUE=%d\n", id.acoustic >> 8); - printf("ID_ATA_FEATURE_SET_AAM_CURRENT_VALUE=%d\n", id.acoustic & 0xff); - } - if (id.command_set_2 & (1<<5)) { - printf("ID_ATA_FEATURE_SET_PUIS=1\n"); - printf("ID_ATA_FEATURE_SET_PUIS_ENABLED=%d\n", (id.cfs_enable_2 & (1<<5)) ? 1 : 0); - } - if (id.command_set_2 & (1<<3)) { - printf("ID_ATA_FEATURE_SET_APM=1\n"); - printf("ID_ATA_FEATURE_SET_APM_ENABLED=%d\n", (id.cfs_enable_2 & (1<<3)) ? 1 : 0); - if ((id.cfs_enable_2 & (1<<3))) - printf("ID_ATA_FEATURE_SET_APM_CURRENT_VALUE=%d\n", id.CurAPMvalues & 0xff); - } - if (id.command_set_2 & (1<<0)) - printf("ID_ATA_DOWNLOAD_MICROCODE=1\n"); - - /* - * Word 76 indicates the capabilities of a SATA device. A PATA device shall set - * word 76 to 0000h or FFFFh. If word 76 is set to 0000h or FFFFh, then - * the device does not claim compliance with the Serial ATA specification and words - * 76 through 79 are not valid and shall be ignored. - */ - word = *((uint16_t *) identify + 76); - if (word != 0x0000 && word != 0xffff) { - printf("ID_ATA_SATA=1\n"); - /* - * If bit 2 of word 76 is set to one, then the device supports the Gen2 - * signaling rate of 3.0 Gb/s (see SATA 2.6). - * - * If bit 1 of word 76 is set to one, then the device supports the Gen1 - * signaling rate of 1.5 Gb/s (see SATA 2.6). - */ - if (word & (1<<2)) - printf("ID_ATA_SATA_SIGNAL_RATE_GEN2=1\n"); - if (word & (1<<1)) - printf("ID_ATA_SATA_SIGNAL_RATE_GEN1=1\n"); - } - - /* Word 217 indicates the nominal media rotation rate of the device */ - word = *((uint16_t *) identify + 217); - if (word != 0x0000) { - if (word == 0x0001) { - printf ("ID_ATA_ROTATION_RATE_RPM=0\n"); /* non-rotating e.g. SSD */ - } else if (word >= 0x0401 && word <= 0xfffe) { - printf ("ID_ATA_ROTATION_RATE_RPM=%d\n", word); - } - } - - /* - * Words 108-111 contain a mandatory World Wide Name (WWN) in the NAA IEEE Registered identifier - * format. Word 108 bits (15:12) shall contain 5h, indicating that the naming authority is IEEE. - * All other values are reserved. - */ - word = *((uint16_t *) identify + 108); - if ((word & 0xf000) == 0x5000) { - uint64_t wwwn; - - wwwn = *((uint16_t *) identify + 108); - wwwn <<= 16; - wwwn |= *((uint16_t *) identify + 109); - wwwn <<= 16; - wwwn |= *((uint16_t *) identify + 110); - wwwn <<= 16; - wwwn |= *((uint16_t *) identify + 111); - printf("ID_WWN=0x%llx\n", (unsigned long long int) wwwn); - /* ATA devices have no vendor extension */ - printf("ID_WWN_WITH_EXTENSION=0x%llx\n", (unsigned long long int) wwwn); - } - - /* from Linux's include/linux/ata.h */ - if (identify_words[0] == 0x848a || identify_words[0] == 0x844a) { - printf("ID_ATA_CFA=1\n"); - } else { - if ((identify_words[83] & 0xc004) == 0x4004) { - printf("ID_ATA_CFA=1\n"); - } - } - } else { - if (serial[0] != '\0') - printf("%s_%s\n", model, serial); - else - printf("%s\n", model); - } -close: - close(fd); -exit: - udev_unref(udev); - udev_log_close(); - return rc; -} diff --git a/src/udev/src/cdrom_id/60-cdrom_id.rules b/src/udev/src/cdrom_id/60-cdrom_id.rules deleted file mode 100644 index 6eaf76a72c..0000000000 --- a/src/udev/src/cdrom_id/60-cdrom_id.rules +++ /dev/null @@ -1,20 +0,0 @@ -# do not edit this file, it will be overwritten on update - -ACTION=="remove", GOTO="cdrom_end" -SUBSYSTEM!="block", GOTO="cdrom_end" -KERNEL!="sr[0-9]*|xvd*", GOTO="cdrom_end" -ENV{DEVTYPE}!="disk", GOTO="cdrom_end" - -# unconditionally tag device as CDROM -KERNEL=="sr[0-9]*", ENV{ID_CDROM}="1" - -# media eject button pressed -ENV{DISK_EJECT_REQUEST}=="?*", RUN+="cdrom_id --eject-media $devnode", GOTO="cdrom_end" - -# import device and media properties and lock tray to -# enable the receiving of media eject button events -IMPORT{program}="cdrom_id --lock-media $devnode" - -KERNEL=="sr0", SYMLINK+="cdrom", OPTIONS+="link_priority=-100" - -LABEL="cdrom_end" diff --git a/src/udev/src/cdrom_id/cdrom_id.c b/src/udev/src/cdrom_id/cdrom_id.c deleted file mode 100644 index f90d52ec9c..0000000000 --- a/src/udev/src/cdrom_id/cdrom_id.c +++ /dev/null @@ -1,1099 +0,0 @@ -/* - * cdrom_id - optical drive and media information prober - * - * Copyright (C) 2008-2010 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE 1 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libudev.h" -#include "libudev-private.h" - -static bool debug; - -static void log_fn(struct udev *udev, int priority, - const char *file, int line, const char *fn, - const char *format, va_list args) -{ - if (debug) { - fprintf(stderr, "%s: ", fn); - vfprintf(stderr, format, args); - } else { - vsyslog(priority, format, args); - } -} - -/* device info */ -static unsigned int cd_cd_rom; -static unsigned int cd_cd_r; -static unsigned int cd_cd_rw; -static unsigned int cd_dvd_rom; -static unsigned int cd_dvd_r; -static unsigned int cd_dvd_rw; -static unsigned int cd_dvd_ram; -static unsigned int cd_dvd_plus_r; -static unsigned int cd_dvd_plus_rw; -static unsigned int cd_dvd_plus_r_dl; -static unsigned int cd_dvd_plus_rw_dl; -static unsigned int cd_bd; -static unsigned int cd_bd_r; -static unsigned int cd_bd_re; -static unsigned int cd_hddvd; -static unsigned int cd_hddvd_r; -static unsigned int cd_hddvd_rw; -static unsigned int cd_mo; -static unsigned int cd_mrw; -static unsigned int cd_mrw_w; - -/* media info */ -static unsigned int cd_media; -static unsigned int cd_media_cd_rom; -static unsigned int cd_media_cd_r; -static unsigned int cd_media_cd_rw; -static unsigned int cd_media_dvd_rom; -static unsigned int cd_media_dvd_r; -static unsigned int cd_media_dvd_rw; -static unsigned int cd_media_dvd_rw_ro; /* restricted overwrite mode */ -static unsigned int cd_media_dvd_rw_seq; /* sequential mode */ -static unsigned int cd_media_dvd_ram; -static unsigned int cd_media_dvd_plus_r; -static unsigned int cd_media_dvd_plus_rw; -static unsigned int cd_media_dvd_plus_r_dl; -static unsigned int cd_media_dvd_plus_rw_dl; -static unsigned int cd_media_bd; -static unsigned int cd_media_bd_r; -static unsigned int cd_media_bd_re; -static unsigned int cd_media_hddvd; -static unsigned int cd_media_hddvd_r; -static unsigned int cd_media_hddvd_rw; -static unsigned int cd_media_mo; -static unsigned int cd_media_mrw; -static unsigned int cd_media_mrw_w; - -static const char *cd_media_state = NULL; -static unsigned int cd_media_session_next; -static unsigned int cd_media_session_count; -static unsigned int cd_media_track_count; -static unsigned int cd_media_track_count_data; -static unsigned int cd_media_track_count_audio; -static unsigned long long int cd_media_session_last_offset; - -#define ERRCODE(s) ((((s)[2] & 0x0F) << 16) | ((s)[12] << 8) | ((s)[13])) -#define SK(errcode) (((errcode) >> 16) & 0xF) -#define ASC(errcode) (((errcode) >> 8) & 0xFF) -#define ASCQ(errcode) ((errcode) & 0xFF) - -static bool is_mounted(const char *device) -{ - struct stat statbuf; - FILE *fp; - int maj, min; - bool mounted = false; - - if (stat(device, &statbuf) < 0) - return -ENODEV; - - fp = fopen("/proc/self/mountinfo", "r"); - if (fp == NULL) - return -ENOSYS; - while (fscanf(fp, "%*s %*s %i:%i %*[^\n]", &maj, &min) == 2) { - if (makedev(maj, min) == statbuf.st_rdev) { - mounted = true; - break; - } - } - fclose(fp); - return mounted; -} - -static void info_scsi_cmd_err(struct udev *udev, char *cmd, int err) -{ - if (err == -1) { - info(udev, "%s failed\n", cmd); - return; - } - info(udev, "%s failed with SK=%Xh/ASC=%02Xh/ACQ=%02Xh\n", cmd, SK(err), ASC(err), ASCQ(err)); -} - -struct scsi_cmd { - struct cdrom_generic_command cgc; - union { - struct request_sense s; - unsigned char u[18]; - } _sense; - struct sg_io_hdr sg_io; -}; - -static void scsi_cmd_init(struct udev *udev, struct scsi_cmd *cmd) -{ - memset(cmd, 0x00, sizeof(struct scsi_cmd)); - cmd->cgc.quiet = 1; - cmd->cgc.sense = &cmd->_sense.s; - cmd->sg_io.interface_id = 'S'; - cmd->sg_io.mx_sb_len = sizeof(cmd->_sense); - cmd->sg_io.cmdp = cmd->cgc.cmd; - cmd->sg_io.sbp = cmd->_sense.u; - cmd->sg_io.flags = SG_FLAG_LUN_INHIBIT | SG_FLAG_DIRECT_IO; -} - -static void scsi_cmd_set(struct udev *udev, struct scsi_cmd *cmd, size_t i, unsigned char arg) -{ - cmd->sg_io.cmd_len = i + 1; - cmd->cgc.cmd[i] = arg; -} - -#define CHECK_CONDITION 0x01 - -static int scsi_cmd_run(struct udev *udev, struct scsi_cmd *cmd, int fd, unsigned char *buf, size_t bufsize) -{ - int ret = 0; - - if (bufsize > 0) { - cmd->sg_io.dxferp = buf; - cmd->sg_io.dxfer_len = bufsize; - cmd->sg_io.dxfer_direction = SG_DXFER_FROM_DEV; - } else { - cmd->sg_io.dxfer_direction = SG_DXFER_NONE; - } - if (ioctl(fd, SG_IO, &cmd->sg_io)) - return -1; - - if ((cmd->sg_io.info & SG_INFO_OK_MASK) != SG_INFO_OK) { - errno = EIO; - ret = -1; - if (cmd->sg_io.masked_status & CHECK_CONDITION) { - ret = ERRCODE(cmd->_sense.u); - if (ret == 0) - ret = -1; - } - } - return ret; -} - -static int media_lock(struct udev *udev, int fd, bool lock) -{ - int err; - - /* disable the kernel's lock logic */ - err = ioctl(fd, CDROM_CLEAR_OPTIONS, CDO_LOCK); - if (err < 0) - info(udev, "CDROM_CLEAR_OPTIONS, CDO_LOCK failed\n"); - - err = ioctl(fd, CDROM_LOCKDOOR, lock ? 1 : 0); - if (err < 0) - info(udev, "CDROM_LOCKDOOR failed\n"); - - return err; -} - -static int media_eject(struct udev *udev, int fd) -{ - struct scsi_cmd sc; - int err; - - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x1b); - scsi_cmd_set(udev, &sc, 4, 0x02); - scsi_cmd_set(udev, &sc, 5, 0); - err = scsi_cmd_run(udev, &sc, fd, NULL, 0); - if ((err != 0)) { - info_scsi_cmd_err(udev, "START_STOP_UNIT", err); - return -1; - } - return 0; -} - -static int cd_capability_compat(struct udev *udev, int fd) -{ - int capability; - - capability = ioctl(fd, CDROM_GET_CAPABILITY, NULL); - if (capability < 0) { - info(udev, "CDROM_GET_CAPABILITY failed\n"); - return -1; - } - - if (capability & CDC_CD_R) - cd_cd_r = 1; - if (capability & CDC_CD_RW) - cd_cd_rw = 1; - if (capability & CDC_DVD) - cd_dvd_rom = 1; - if (capability & CDC_DVD_R) - cd_dvd_r = 1; - if (capability & CDC_DVD_RAM) - cd_dvd_ram = 1; - if (capability & CDC_MRW) - cd_mrw = 1; - if (capability & CDC_MRW_W) - cd_mrw_w = 1; - return 0; -} - -static int cd_media_compat(struct udev *udev, int fd) -{ - if (ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT) != CDS_DISC_OK) { - info(udev, "CDROM_DRIVE_STATUS != CDS_DISC_OK\n"); - return -1; - } - cd_media = 1; - return 0; -} - -static int cd_inquiry(struct udev *udev, int fd) -{ - struct scsi_cmd sc; - unsigned char inq[128]; - int err; - - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x12); - scsi_cmd_set(udev, &sc, 4, 36); - scsi_cmd_set(udev, &sc, 5, 0); - err = scsi_cmd_run(udev, &sc, fd, inq, 36); - if ((err != 0)) { - info_scsi_cmd_err(udev, "INQUIRY", err); - return -1; - } - - if ((inq[0] & 0x1F) != 5) { - info(udev, "not an MMC unit\n"); - return -1; - } - - info(udev, "INQUIRY: [%.8s][%.16s][%.4s]\n", inq + 8, inq + 16, inq + 32); - return 0; -} - -static void feature_profile_media(struct udev *udev, int cur_profile) -{ - switch (cur_profile) { - case 0x03: - case 0x04: - case 0x05: - info(udev, "profile 0x%02x \n", cur_profile); - cd_media = 1; - cd_media_mo = 1; - break; - case 0x08: - info(udev, "profile 0x%02x media_cd_rom\n", cur_profile); - cd_media = 1; - cd_media_cd_rom = 1; - break; - case 0x09: - info(udev, "profile 0x%02x media_cd_r\n", cur_profile); - cd_media = 1; - cd_media_cd_r = 1; - break; - case 0x0a: - info(udev, "profile 0x%02x media_cd_rw\n", cur_profile); - cd_media = 1; - cd_media_cd_rw = 1; - break; - case 0x10: - info(udev, "profile 0x%02x media_dvd_ro\n", cur_profile); - cd_media = 1; - cd_media_dvd_rom = 1; - break; - case 0x11: - info(udev, "profile 0x%02x media_dvd_r\n", cur_profile); - cd_media = 1; - cd_media_dvd_r = 1; - break; - case 0x12: - info(udev, "profile 0x%02x media_dvd_ram\n", cur_profile); - cd_media = 1; - cd_media_dvd_ram = 1; - break; - case 0x13: - info(udev, "profile 0x%02x media_dvd_rw_ro\n", cur_profile); - cd_media = 1; - cd_media_dvd_rw = 1; - cd_media_dvd_rw_ro = 1; - break; - case 0x14: - info(udev, "profile 0x%02x media_dvd_rw_seq\n", cur_profile); - cd_media = 1; - cd_media_dvd_rw = 1; - cd_media_dvd_rw_seq = 1; - break; - case 0x1B: - info(udev, "profile 0x%02x media_dvd_plus_r\n", cur_profile); - cd_media = 1; - cd_media_dvd_plus_r = 1; - break; - case 0x1A: - info(udev, "profile 0x%02x media_dvd_plus_rw\n", cur_profile); - cd_media = 1; - cd_media_dvd_plus_rw = 1; - break; - case 0x2A: - info(udev, "profile 0x%02x media_dvd_plus_rw_dl\n", cur_profile); - cd_media = 1; - cd_media_dvd_plus_rw_dl = 1; - break; - case 0x2B: - info(udev, "profile 0x%02x media_dvd_plus_r_dl\n", cur_profile); - cd_media = 1; - cd_media_dvd_plus_r_dl = 1; - break; - case 0x40: - info(udev, "profile 0x%02x media_bd\n", cur_profile); - cd_media = 1; - cd_media_bd = 1; - break; - case 0x41: - case 0x42: - info(udev, "profile 0x%02x media_bd_r\n", cur_profile); - cd_media = 1; - cd_media_bd_r = 1; - break; - case 0x43: - info(udev, "profile 0x%02x media_bd_re\n", cur_profile); - cd_media = 1; - cd_media_bd_re = 1; - break; - case 0x50: - info(udev, "profile 0x%02x media_hddvd\n", cur_profile); - cd_media = 1; - cd_media_hddvd = 1; - break; - case 0x51: - info(udev, "profile 0x%02x media_hddvd_r\n", cur_profile); - cd_media = 1; - cd_media_hddvd_r = 1; - break; - case 0x52: - info(udev, "profile 0x%02x media_hddvd_rw\n", cur_profile); - cd_media = 1; - cd_media_hddvd_rw = 1; - break; - default: - info(udev, "profile 0x%02x \n", cur_profile); - break; - } -} - -static int feature_profiles(struct udev *udev, const unsigned char *profiles, size_t size) -{ - unsigned int i; - - for (i = 0; i+4 <= size; i += 4) { - int profile; - - profile = profiles[i] << 8 | profiles[i+1]; - switch (profile) { - case 0x03: - case 0x04: - case 0x05: - info(udev, "profile 0x%02x mo\n", profile); - cd_mo = 1; - break; - case 0x08: - info(udev, "profile 0x%02x cd_rom\n", profile); - cd_cd_rom = 1; - break; - case 0x09: - info(udev, "profile 0x%02x cd_r\n", profile); - cd_cd_r = 1; - break; - case 0x0A: - info(udev, "profile 0x%02x cd_rw\n", profile); - cd_cd_rw = 1; - break; - case 0x10: - info(udev, "profile 0x%02x dvd_rom\n", profile); - cd_dvd_rom = 1; - break; - case 0x12: - info(udev, "profile 0x%02x dvd_ram\n", profile); - cd_dvd_ram = 1; - break; - case 0x13: - case 0x14: - info(udev, "profile 0x%02x dvd_rw\n", profile); - cd_dvd_rw = 1; - break; - case 0x1B: - info(udev, "profile 0x%02x dvd_plus_r\n", profile); - cd_dvd_plus_r = 1; - break; - case 0x1A: - info(udev, "profile 0x%02x dvd_plus_rw\n", profile); - cd_dvd_plus_rw = 1; - break; - case 0x2A: - info(udev, "profile 0x%02x dvd_plus_rw_dl\n", profile); - cd_dvd_plus_rw_dl = 1; - break; - case 0x2B: - info(udev, "profile 0x%02x dvd_plus_r_dl\n", profile); - cd_dvd_plus_r_dl = 1; - break; - case 0x40: - cd_bd = 1; - info(udev, "profile 0x%02x bd\n", profile); - break; - case 0x41: - case 0x42: - cd_bd_r = 1; - info(udev, "profile 0x%02x bd_r\n", profile); - break; - case 0x43: - cd_bd_re = 1; - info(udev, "profile 0x%02x bd_re\n", profile); - break; - case 0x50: - cd_hddvd = 1; - info(udev, "profile 0x%02x hddvd\n", profile); - break; - case 0x51: - cd_hddvd_r = 1; - info(udev, "profile 0x%02x hddvd_r\n", profile); - break; - case 0x52: - cd_hddvd_rw = 1; - info(udev, "profile 0x%02x hddvd_rw\n", profile); - break; - default: - info(udev, "profile 0x%02x \n", profile); - break; - } - } - return 0; -} - -/* returns 0 if media was detected */ -static int cd_profiles_old_mmc(struct udev *udev, int fd) -{ - struct scsi_cmd sc; - int err; - - unsigned char header[32]; - - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x51); - scsi_cmd_set(udev, &sc, 8, sizeof(header)); - scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, header, sizeof(header)); - if ((err != 0)) { - info_scsi_cmd_err(udev, "READ DISC INFORMATION", err); - if (cd_media == 1) { - info(udev, "no current profile, but disc is present; assuming CD-ROM\n"); - cd_media_cd_rom = 1; - return 0; - } else { - info(udev, "no current profile, assuming no media\n"); - return -1; - } - }; - - cd_media = 1; - - if (header[2] & 16) { - cd_media_cd_rw = 1; - info(udev, "profile 0x0a media_cd_rw\n"); - } else if ((header[2] & 3) < 2 && cd_cd_r) { - cd_media_cd_r = 1; - info(udev, "profile 0x09 media_cd_r\n"); - } else { - cd_media_cd_rom = 1; - info(udev, "profile 0x08 media_cd_rom\n"); - } - return 0; -} - -/* returns 0 if media was detected */ -static int cd_profiles(struct udev *udev, int fd) -{ - struct scsi_cmd sc; - unsigned char features[65530]; - unsigned int cur_profile = 0; - unsigned int len; - unsigned int i; - int err; - int ret; - - ret = -1; - - /* First query the current profile */ - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x46); - scsi_cmd_set(udev, &sc, 8, 8); - scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, features, 8); - if ((err != 0)) { - info_scsi_cmd_err(udev, "GET CONFIGURATION", err); - /* handle pre-MMC2 drives which do not support GET CONFIGURATION */ - if (SK(err) == 0x5 && ASC(err) == 0x20) { - info(udev, "drive is pre-MMC2 and does not support 46h get configuration command\n"); - info(udev, "trying to work around the problem\n"); - ret = cd_profiles_old_mmc(udev, fd); - } - goto out; - } - - cur_profile = features[6] << 8 | features[7]; - if (cur_profile > 0) { - info(udev, "current profile 0x%02x\n", cur_profile); - feature_profile_media (udev, cur_profile); - ret = 0; /* we have media */ - } else { - info(udev, "no current profile, assuming no media\n"); - } - - len = features[0] << 24 | features[1] << 16 | features[2] << 8 | features[3]; - info(udev, "GET CONFIGURATION: size of features buffer 0x%04x\n", len); - - if (len > sizeof(features)) { - info(udev, "can not get features in a single query, truncating\n"); - len = sizeof(features); - } else if (len <= 8) { - len = sizeof(features); - } - - /* Now get the full feature buffer */ - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x46); - scsi_cmd_set(udev, &sc, 7, ( len >> 8 ) & 0xff); - scsi_cmd_set(udev, &sc, 8, len & 0xff); - scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, features, len); - if ((err != 0)) { - info_scsi_cmd_err(udev, "GET CONFIGURATION", err); - return -1; - } - - /* parse the length once more, in case the drive decided to have other features suddenly :) */ - len = features[0] << 24 | features[1] << 16 | features[2] << 8 | features[3]; - info(udev, "GET CONFIGURATION: size of features buffer 0x%04x\n", len); - - if (len > sizeof(features)) { - info(udev, "can not get features in a single query, truncating\n"); - len = sizeof(features); - } - - /* device features */ - for (i = 8; i+4 < len; i += (4 + features[i+3])) { - unsigned int feature; - - feature = features[i] << 8 | features[i+1]; - - switch (feature) { - case 0x00: - info(udev, "GET CONFIGURATION: feature 'profiles', with %i entries\n", features[i+3] / 4); - feature_profiles(udev, &features[i]+4, features[i+3]); - break; - default: - info(udev, "GET CONFIGURATION: feature 0x%04x , with 0x%02x bytes\n", feature, features[i+3]); - break; - } - } -out: - return ret; -} - -static int cd_media_info(struct udev *udev, int fd) -{ - struct scsi_cmd sc; - unsigned char header[32]; - static const char *media_status[] = { - "blank", - "appendable", - "complete", - "other" - }; - int err; - - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x51); - scsi_cmd_set(udev, &sc, 8, sizeof(header) & 0xff); - scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, header, sizeof(header)); - if ((err != 0)) { - info_scsi_cmd_err(udev, "READ DISC INFORMATION", err); - return -1; - }; - - cd_media = 1; - info(udev, "disk type %02x\n", header[8]); - info(udev, "hardware reported media status: %s\n", media_status[header[2] & 3]); - - /* exclude plain CDROM, some fake cdroms return 0 for "blank" media here */ - if (!cd_media_cd_rom) - cd_media_state = media_status[header[2] & 3]; - - /* fresh DVD-RW in restricted overwite mode reports itself as - * "appendable"; change it to "blank" to make it consistent with what - * gets reported after blanking, and what userspace expects */ - if (cd_media_dvd_rw_ro && (header[2] & 3) == 1) - cd_media_state = media_status[0]; - - /* DVD+RW discs (and DVD-RW in restricted mode) once formatted are - * always "complete", DVD-RAM are "other" or "complete" if the disc is - * write protected; we need to check the contents if it is blank */ - if ((cd_media_dvd_rw_ro || cd_media_dvd_plus_rw || cd_media_dvd_plus_rw_dl || cd_media_dvd_ram) && (header[2] & 3) > 1) { - unsigned char buffer[32 * 2048]; - unsigned char result, len; - int block, offset; - - if (cd_media_dvd_ram) { - /* a write protected dvd-ram may report "complete" status */ - - unsigned char dvdstruct[8]; - unsigned char format[12]; - - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0xAD); - scsi_cmd_set(udev, &sc, 7, 0xC0); - scsi_cmd_set(udev, &sc, 9, sizeof(dvdstruct)); - scsi_cmd_set(udev, &sc, 11, 0); - err = scsi_cmd_run(udev, &sc, fd, dvdstruct, sizeof(dvdstruct)); - if ((err != 0)) { - info_scsi_cmd_err(udev, "READ DVD STRUCTURE", err); - return -1; - } - if (dvdstruct[4] & 0x02) { - cd_media_state = media_status[2]; - info(udev, "write-protected DVD-RAM media inserted\n"); - goto determined; - } - - /* let's make sure we don't try to read unformatted media */ - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x23); - scsi_cmd_set(udev, &sc, 8, sizeof(format)); - scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, format, sizeof(format)); - if ((err != 0)) { - info_scsi_cmd_err(udev, "READ DVD FORMAT CAPACITIES", err); - return -1; - } - - len = format[3]; - if (len & 7 || len < 16) { - info(udev, "invalid format capacities length\n"); - return -1; - } - - switch(format[8] & 3) { - case 1: - info(udev, "unformatted DVD-RAM media inserted\n"); - /* This means that last format was interrupted - * or failed, blank dvd-ram discs are factory - * formatted. Take no action here as it takes - * quite a while to reformat a dvd-ram and it's - * not automatically started */ - goto determined; - - case 2: - info(udev, "formatted DVD-RAM media inserted\n"); - break; - - case 3: - cd_media = 0; //return no media - info(udev, "format capacities returned no media\n"); - return -1; - } - } - - /* Take a closer look at formatted media (unformatted DVD+RW - * has "blank" status", DVD-RAM was examined earlier) and check - * for ISO and UDF PVDs or a fs superblock presence and do it - * in one ioctl (we need just sectors 0 and 16) */ - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x28); - scsi_cmd_set(udev, &sc, 5, 0); - scsi_cmd_set(udev, &sc, 8, 32); - scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, buffer, sizeof(buffer)); - if ((err != 0)) { - cd_media = 0; - info_scsi_cmd_err(udev, "READ FIRST 32 BLOCKS", err); - return -1; - } - - /* if any non-zero data is found in sector 16 (iso and udf) or - * eventually 0 (fat32 boot sector, ext2 superblock, etc), disc - * is assumed non-blank */ - result = 0; - - for (block = 32768; block >= 0 && !result; block -= 32768) { - offset = block; - while (offset < (block + 2048) && !result) { - result = buffer [offset]; - offset++; - } - } - - if (!result) { - cd_media_state = media_status[0]; - info(udev, "no data in blocks 0 or 16, assuming blank\n"); - } else { - info(udev, "data in blocks 0 or 16, assuming complete\n"); - } - } - -determined: - /* "other" is e. g. DVD-RAM, can't append sessions there; DVDs in - * restricted overwrite mode can never append, only in sequential mode */ - if ((header[2] & 3) < 2 && !cd_media_dvd_rw_ro) - cd_media_session_next = header[10] << 8 | header[5]; - cd_media_session_count = header[9] << 8 | header[4]; - cd_media_track_count = header[11] << 8 | header[6]; - - return 0; -} - -static int cd_media_toc(struct udev *udev, int fd) -{ - struct scsi_cmd sc; - unsigned char header[12]; - unsigned char toc[65536]; - unsigned int len, i, num_tracks; - unsigned char *p; - int err; - - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x43); - scsi_cmd_set(udev, &sc, 6, 1); - scsi_cmd_set(udev, &sc, 8, sizeof(header) & 0xff); - scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, header, sizeof(header)); - if ((err != 0)) { - info_scsi_cmd_err(udev, "READ TOC", err); - return -1; - } - - len = (header[0] << 8 | header[1]) + 2; - info(udev, "READ TOC: len: %d, start track: %d, end track: %d\n", len, header[2], header[3]); - if (len > sizeof(toc)) - return -1; - if (len < 2) - return -1; - /* 2: first track, 3: last track */ - num_tracks = header[3] - header[2] + 1; - - /* empty media has no tracks */ - if (len < 8) - return 0; - - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x43); - scsi_cmd_set(udev, &sc, 6, header[2]); /* First Track/Session Number */ - scsi_cmd_set(udev, &sc, 7, (len >> 8) & 0xff); - scsi_cmd_set(udev, &sc, 8, len & 0xff); - scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, toc, len); - if ((err != 0)) { - info_scsi_cmd_err(udev, "READ TOC (tracks)", err); - return -1; - } - - /* Take care to not iterate beyond the last valid track as specified in - * the TOC, but also avoid going beyond the TOC length, just in case - * the last track number is invalidly large */ - for (p = toc+4, i = 4; i < len-8 && num_tracks > 0; i += 8, p += 8, --num_tracks) { - unsigned int block; - unsigned int is_data_track; - - is_data_track = (p[1] & 0x04) != 0; - - block = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7]; - info(udev, "track=%u info=0x%x(%s) start_block=%u\n", - p[2], p[1] & 0x0f, is_data_track ? "data":"audio", block); - - if (is_data_track) - cd_media_track_count_data++; - else - cd_media_track_count_audio++; - } - - scsi_cmd_init(udev, &sc); - scsi_cmd_set(udev, &sc, 0, 0x43); - scsi_cmd_set(udev, &sc, 2, 1); /* Session Info */ - scsi_cmd_set(udev, &sc, 8, sizeof(header)); - scsi_cmd_set(udev, &sc, 9, 0); - err = scsi_cmd_run(udev, &sc, fd, header, sizeof(header)); - if ((err != 0)) { - info_scsi_cmd_err(udev, "READ TOC (multi session)", err); - return -1; - } - len = header[4+4] << 24 | header[4+5] << 16 | header[4+6] << 8 | header[4+7]; - info(udev, "last track %u starts at block %u\n", header[4+2], len); - cd_media_session_last_offset = (unsigned long long int)len * 2048; - return 0; -} - -int main(int argc, char *argv[]) -{ - struct udev *udev; - static const struct option options[] = { - { "lock-media", no_argument, NULL, 'l' }, - { "unlock-media", no_argument, NULL, 'u' }, - { "eject-media", no_argument, NULL, 'e' }, - { "debug", no_argument, NULL, 'd' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - bool eject = false; - bool lock = false; - bool unlock = false; - const char *node = NULL; - int fd = -1; - int cnt; - int rc = 0; - - udev = udev_new(); - if (udev == NULL) - goto exit; - - udev_log_init("cdrom_id"); - udev_set_log_fn(udev, log_fn); - - while (1) { - int option; - - option = getopt_long(argc, argv, "deluh", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'l': - lock = true; - break; - case 'u': - unlock = true; - break; - case 'e': - eject = true; - break; - case 'd': - debug = true; - if (udev_get_log_priority(udev) < LOG_INFO) - udev_set_log_priority(udev, LOG_INFO); - break; - case 'h': - printf("Usage: cdrom_id [options] \n" - " --lock-media lock the media (to enable eject request events)\n" - " --unlock-media unlock the media\n" - " --eject-media eject the media\n" - " --debug debug to stderr\n" - " --help print this help text\n\n"); - goto exit; - default: - rc = 1; - goto exit; - } - } - - node = argv[optind]; - if (!node) { - err(udev, "no device\n"); - fprintf(stderr, "no device\n"); - rc = 1; - goto exit; - } - - srand((unsigned int)getpid()); - for (cnt = 20; cnt > 0; cnt--) { - struct timespec duration; - - fd = open(node, O_RDONLY|O_NONBLOCK|(is_mounted(node) ? 0 : O_EXCL)); - if (fd >= 0 || errno != EBUSY) - break; - duration.tv_sec = 0; - duration.tv_nsec = (100 * 1000 * 1000) + (rand() % 100 * 1000 * 1000); - nanosleep(&duration, NULL); - } - if (fd < 0) { - info(udev, "unable to open '%s'\n", node); - fprintf(stderr, "unable to open '%s'\n", node); - rc = 1; - goto exit; - } - info(udev, "probing: '%s'\n", node); - - /* same data as original cdrom_id */ - if (cd_capability_compat(udev, fd) < 0) { - rc = 1; - goto exit; - } - - /* check for media - don't bail if there's no media as we still need to - * to read profiles */ - cd_media_compat(udev, fd); - - /* check if drive talks MMC */ - if (cd_inquiry(udev, fd) < 0) - goto work; - - /* read drive and possibly current profile */ - if (cd_profiles(udev, fd) != 0) - goto work; - - /* at this point we are guaranteed to have media in the drive - find out more about it */ - - /* get session/track info */ - cd_media_toc(udev, fd); - - /* get writable media state */ - cd_media_info(udev, fd); - -work: - /* lock the media, so we enable eject button events */ - if (lock && cd_media) { - info(udev, "PREVENT_ALLOW_MEDIUM_REMOVAL (lock)\n"); - media_lock(udev, fd, true); - } - - if (unlock && cd_media) { - info(udev, "PREVENT_ALLOW_MEDIUM_REMOVAL (unlock)\n"); - media_lock(udev, fd, false); - } - - if (eject) { - info(udev, "PREVENT_ALLOW_MEDIUM_REMOVAL (unlock)\n"); - media_lock(udev, fd, false); - info(udev, "START_STOP_UNIT (eject)\n"); - media_eject(udev, fd); - } - - printf("ID_CDROM=1\n"); - if (cd_cd_rom) - printf("ID_CDROM_CD=1\n"); - if (cd_cd_r) - printf("ID_CDROM_CD_R=1\n"); - if (cd_cd_rw) - printf("ID_CDROM_CD_RW=1\n"); - if (cd_dvd_rom) - printf("ID_CDROM_DVD=1\n"); - if (cd_dvd_r) - printf("ID_CDROM_DVD_R=1\n"); - if (cd_dvd_rw) - printf("ID_CDROM_DVD_RW=1\n"); - if (cd_dvd_ram) - printf("ID_CDROM_DVD_RAM=1\n"); - if (cd_dvd_plus_r) - printf("ID_CDROM_DVD_PLUS_R=1\n"); - if (cd_dvd_plus_rw) - printf("ID_CDROM_DVD_PLUS_RW=1\n"); - if (cd_dvd_plus_r_dl) - printf("ID_CDROM_DVD_PLUS_R_DL=1\n"); - if (cd_dvd_plus_rw_dl) - printf("ID_CDROM_DVD_PLUS_RW_DL=1\n"); - if (cd_bd) - printf("ID_CDROM_BD=1\n"); - if (cd_bd_r) - printf("ID_CDROM_BD_R=1\n"); - if (cd_bd_re) - printf("ID_CDROM_BD_RE=1\n"); - if (cd_hddvd) - printf("ID_CDROM_HDDVD=1\n"); - if (cd_hddvd_r) - printf("ID_CDROM_HDDVD_R=1\n"); - if (cd_hddvd_rw) - printf("ID_CDROM_HDDVD_RW=1\n"); - if (cd_mo) - printf("ID_CDROM_MO=1\n"); - if (cd_mrw) - printf("ID_CDROM_MRW=1\n"); - if (cd_mrw_w) - printf("ID_CDROM_MRW_W=1\n"); - - if (cd_media) - printf("ID_CDROM_MEDIA=1\n"); - if (cd_media_mo) - printf("ID_CDROM_MEDIA_MO=1\n"); - if (cd_media_mrw) - printf("ID_CDROM_MEDIA_MRW=1\n"); - if (cd_media_mrw_w) - printf("ID_CDROM_MEDIA_MRW_W=1\n"); - if (cd_media_cd_rom) - printf("ID_CDROM_MEDIA_CD=1\n"); - if (cd_media_cd_r) - printf("ID_CDROM_MEDIA_CD_R=1\n"); - if (cd_media_cd_rw) - printf("ID_CDROM_MEDIA_CD_RW=1\n"); - if (cd_media_dvd_rom) - printf("ID_CDROM_MEDIA_DVD=1\n"); - if (cd_media_dvd_r) - printf("ID_CDROM_MEDIA_DVD_R=1\n"); - if (cd_media_dvd_ram) - printf("ID_CDROM_MEDIA_DVD_RAM=1\n"); - if (cd_media_dvd_rw) - printf("ID_CDROM_MEDIA_DVD_RW=1\n"); - if (cd_media_dvd_plus_r) - printf("ID_CDROM_MEDIA_DVD_PLUS_R=1\n"); - if (cd_media_dvd_plus_rw) - printf("ID_CDROM_MEDIA_DVD_PLUS_RW=1\n"); - if (cd_media_dvd_plus_rw_dl) - printf("ID_CDROM_MEDIA_DVD_PLUS_RW_DL=1\n"); - if (cd_media_dvd_plus_r_dl) - printf("ID_CDROM_MEDIA_DVD_PLUS_R_DL=1\n"); - if (cd_media_bd) - printf("ID_CDROM_MEDIA_BD=1\n"); - if (cd_media_bd_r) - printf("ID_CDROM_MEDIA_BD_R=1\n"); - if (cd_media_bd_re) - printf("ID_CDROM_MEDIA_BD_RE=1\n"); - if (cd_media_hddvd) - printf("ID_CDROM_MEDIA_HDDVD=1\n"); - if (cd_media_hddvd_r) - printf("ID_CDROM_MEDIA_HDDVD_R=1\n"); - if (cd_media_hddvd_rw) - printf("ID_CDROM_MEDIA_HDDVD_RW=1\n"); - - if (cd_media_state != NULL) - printf("ID_CDROM_MEDIA_STATE=%s\n", cd_media_state); - if (cd_media_session_next > 0) - printf("ID_CDROM_MEDIA_SESSION_NEXT=%d\n", cd_media_session_next); - if (cd_media_session_count > 0) - printf("ID_CDROM_MEDIA_SESSION_COUNT=%d\n", cd_media_session_count); - if (cd_media_session_count > 1 && cd_media_session_last_offset > 0) - printf("ID_CDROM_MEDIA_SESSION_LAST_OFFSET=%llu\n", cd_media_session_last_offset); - if (cd_media_track_count > 0) - printf("ID_CDROM_MEDIA_TRACK_COUNT=%d\n", cd_media_track_count); - if (cd_media_track_count_audio > 0) - printf("ID_CDROM_MEDIA_TRACK_COUNT_AUDIO=%d\n", cd_media_track_count_audio); - if (cd_media_track_count_data > 0) - printf("ID_CDROM_MEDIA_TRACK_COUNT_DATA=%d\n", cd_media_track_count_data); -exit: - if (fd >= 0) - close(fd); - udev_unref(udev); - udev_log_close(); - return rc; -} diff --git a/src/udev/src/collect/collect.c b/src/udev/src/collect/collect.c deleted file mode 100644 index 076fe479e2..0000000000 --- a/src/udev/src/collect/collect.c +++ /dev/null @@ -1,473 +0,0 @@ -/* - * Collect variables across events. - * - * usage: collect [--add|--remove] - * - * Adds ID to the list governed by . - * must be part of the ID list . - * If all IDs given by are listed (ie collect has been - * invoked for each ID in ) collect returns 0, the - * number of missing IDs otherwise. - * A negative number is returned on error. - * - * Copyright(C) 2007, Hannes Reinecke - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libudev.h" -#include "libudev-private.h" - -#define BUFSIZE 16 -#define UDEV_ALARM_TIMEOUT 180 - -enum collect_state { - STATE_NONE, - STATE_OLD, - STATE_CONFIRMED, -}; - -struct _mate { - struct udev_list_node node; - char *name; - enum collect_state state; -}; - -static struct udev_list_node bunch; -static int debug; - -/* This can increase dynamically */ -static size_t bufsize = BUFSIZE; - -static struct _mate *node_to_mate(struct udev_list_node *node) -{ - char *mate; - - mate = (char *)node; - mate -= offsetof(struct _mate, node); - return (struct _mate *)mate; -} - -static void sig_alrm(int signo) -{ - exit(4); -} - -static void usage(void) -{ - printf("usage: collect [--add|--remove] [--debug] \n" - "\n" - " Adds ID to the list governed by .\n" - " must be part of the list .\n" - " If all IDs given by are listed (ie collect has been\n" - " invoked for each ID in ) collect returns 0, the\n" - " number of missing IDs otherwise.\n" - " On error a negative number is returned.\n" - "\n"); -} - -/* - * prepare - * - * Prepares the database file - */ -static int prepare(char *dir, char *filename) -{ - struct stat statbuf; - char buf[512]; - int fd; - - if (stat(dir, &statbuf) < 0) - mkdir(dir, 0700); - - sprintf(buf, "%s/%s", dir, filename); - - fd = open(buf,O_RDWR|O_CREAT, S_IRUSR|S_IWUSR); - if (fd < 0) - fprintf(stderr, "Cannot open %s: %s\n", buf, strerror(errno)); - - if (lockf(fd,F_TLOCK,0) < 0) { - if (debug) - fprintf(stderr, "Lock taken, wait for %d seconds\n", UDEV_ALARM_TIMEOUT); - if (errno == EAGAIN || errno == EACCES) { - alarm(UDEV_ALARM_TIMEOUT); - lockf(fd, F_LOCK, 0); - if (debug) - fprintf(stderr, "Acquired lock on %s\n", buf); - } else { - if (debug) - fprintf(stderr, "Could not get lock on %s: %s\n", buf, strerror(errno)); - } - } - - return fd; -} - -/* - * Read checkpoint file - * - * Tricky reading this. We allocate a buffer twice as large - * as we're going to read. Then we read into the upper half - * of that buffer and start parsing. - * Once we do _not_ find end-of-work terminator (whitespace - * character) we move the upper half to the lower half, - * adjust the read pointer and read the next bit. - * Quite clever methinks :-) - * I should become a programmer ... - * - * Yes, one could have used fgets() for this. But then we'd - * have to use freopen etc which I found quite tedious. - */ -static int checkout(int fd) -{ - int len; - char *buf, *ptr, *word = NULL; - struct _mate *him; - - restart: - len = bufsize >> 1; - buf = calloc(1,bufsize + 1); - if (!buf) { - fprintf(stderr, "Out of memory\n"); - return -1; - } - memset(buf, ' ', bufsize); - ptr = buf + len; - while ((read(fd, buf + len, len)) > 0) { - while (ptr && *ptr) { - word = ptr; - ptr = strpbrk(word," \n\t\r"); - if (!ptr && word < (buf + len)) { - bufsize = bufsize << 1; - if (debug) - fprintf(stderr, "ID overflow, restarting with size %zi\n", bufsize); - free(buf); - lseek(fd, 0, SEEK_SET); - goto restart; - } - if (ptr) { - *ptr = '\0'; - ptr++; - if (!strlen(word)) - continue; - - if (debug) - fprintf(stderr, "Found word %s\n", word); - him = malloc(sizeof (struct _mate)); - him->name = strdup(word); - him->state = STATE_OLD; - udev_list_node_append(&him->node, &bunch); - word = NULL; - } - } - memcpy(buf, buf + len, len); - memset(buf + len, ' ', len); - - if (!ptr) - ptr = word; - if (!ptr) - break; - ptr -= len; - } - - free(buf); - return 0; -} - -/* - * invite - * - * Adds a new ID 'us' to the internal list, - * marks it as confirmed. - */ -static void invite(char *us) -{ - struct udev_list_node *him_node; - struct _mate *who = NULL; - - if (debug) - fprintf(stderr, "Adding ID '%s'\n", us); - - udev_list_node_foreach(him_node, &bunch) { - struct _mate *him = node_to_mate(him_node); - - if (!strcmp(him->name, us)) { - him->state = STATE_CONFIRMED; - who = him; - } - } - if (debug && !who) - fprintf(stderr, "ID '%s' not in database\n", us); - -} - -/* - * reject - * - * Marks the ID 'us' as invalid, - * causing it to be removed when the - * list is written out. - */ -static void reject(char *us) -{ - struct udev_list_node *him_node; - struct _mate *who = NULL; - - if (debug) - fprintf(stderr, "Removing ID '%s'\n", us); - - udev_list_node_foreach(him_node, &bunch) { - struct _mate *him = node_to_mate(him_node); - - if (!strcmp(him->name, us)) { - him->state = STATE_NONE; - who = him; - } - } - if (debug && !who) - fprintf(stderr, "ID '%s' not in database\n", us); -} - -/* - * kickout - * - * Remove all IDs in the internal list which are not part - * of the list passed via the commandline. - */ -static void kickout(void) -{ - struct udev_list_node *him_node; - struct udev_list_node *tmp; - - udev_list_node_foreach_safe(him_node, tmp, &bunch) { - struct _mate *him = node_to_mate(him_node); - - if (him->state == STATE_OLD) { - udev_list_node_remove(&him->node); - free(him->name); - free(him); - } - } -} - -/* - * missing - * - * Counts all missing IDs in the internal list. - */ -static int missing(int fd) -{ - char *buf; - int ret = 0; - struct udev_list_node *him_node; - - buf = malloc(bufsize); - if (!buf) - return -1; - - udev_list_node_foreach(him_node, &bunch) { - struct _mate *him = node_to_mate(him_node); - - if (him->state == STATE_NONE) { - ret++; - } else { - while (strlen(him->name)+1 >= bufsize) { - char *tmpbuf; - - bufsize = bufsize << 1; - tmpbuf = realloc(buf, bufsize); - if (!tmpbuf) { - free(buf); - return -1; - } - buf = tmpbuf; - } - snprintf(buf, strlen(him->name)+2, "%s ", him->name); - write(fd, buf, strlen(buf)); - } - } - - free(buf); - return ret; -} - -/* - * everybody - * - * Prints out the status of the internal list. - */ -static void everybody(void) -{ - struct udev_list_node *him_node; - const char *state = ""; - - udev_list_node_foreach(him_node, &bunch) { - struct _mate *him = node_to_mate(him_node); - - switch (him->state) { - case STATE_NONE: - state = "none"; - break; - case STATE_OLD: - state = "old"; - break; - case STATE_CONFIRMED: - state = "confirmed"; - break; - } - fprintf(stderr, "ID: %s=%s\n", him->name, state); - } -} - -int main(int argc, char **argv) -{ - struct udev *udev; - static const struct option options[] = { - { "add", no_argument, NULL, 'a' }, - { "remove", no_argument, NULL, 'r' }, - { "debug", no_argument, NULL, 'd' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - int argi; - char *checkpoint, *us; - int fd; - int i; - int ret = EXIT_SUCCESS; - int prune = 0; - char tmpdir[UTIL_PATH_SIZE]; - - udev = udev_new(); - if (udev == NULL) { - ret = EXIT_FAILURE; - goto exit; - } - - while (1) { - int option; - - option = getopt_long(argc, argv, "ardh", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'a': - prune = 0; - break; - case 'r': - prune = 1; - break; - case 'd': - debug = 1; - break; - case 'h': - usage(); - goto exit; - default: - ret = 1; - goto exit; - } - } - - argi = optind; - if (argi + 2 > argc) { - printf("Missing parameter(s)\n"); - ret = 1; - goto exit; - } - checkpoint = argv[argi++]; - us = argv[argi++]; - - if (signal(SIGALRM, sig_alrm) == SIG_ERR) { - fprintf(stderr, "Cannot set SIGALRM: %s\n", strerror(errno)); - ret = 2; - goto exit; - } - - udev_list_node_init(&bunch); - - if (debug) - fprintf(stderr, "Using checkpoint '%s'\n", checkpoint); - - util_strscpyl(tmpdir, sizeof(tmpdir), udev_get_run_path(udev), "/collect", NULL); - fd = prepare(tmpdir, checkpoint); - if (fd < 0) { - ret = 3; - goto out; - } - - if (checkout(fd) < 0) { - ret = 2; - goto out; - } - - for (i = argi; i < argc; i++) { - struct udev_list_node *him_node; - struct _mate *who; - - who = NULL; - udev_list_node_foreach(him_node, &bunch) { - struct _mate *him = node_to_mate(him_node); - - if (!strcmp(him->name, argv[i])) - who = him; - } - if (!who) { - struct _mate *him; - - if (debug) - fprintf(stderr, "ID %s: not in database\n", argv[i]); - him = malloc(sizeof (struct _mate)); - him->name = malloc(strlen(argv[i]) + 1); - strcpy(him->name, argv[i]); - him->state = STATE_NONE; - udev_list_node_append(&him->node, &bunch); - } else { - if (debug) - fprintf(stderr, "ID %s: found in database\n", argv[i]); - who->state = STATE_CONFIRMED; - } - } - - if (prune) - reject(us); - else - invite(us); - - if (debug) { - everybody(); - fprintf(stderr, "Prune lists\n"); - } - kickout(); - - lseek(fd, 0, SEEK_SET); - ftruncate(fd, 0); - ret = missing(fd); - - lockf(fd, F_ULOCK, 0); - close(fd); -out: - if (debug) - everybody(); - if (ret >= 0) - printf("COLLECT_%s=%d\n", checkpoint, ret); -exit: - udev_unref(udev); - return ret; -} diff --git a/src/udev/src/docs/.gitignore b/src/udev/src/docs/.gitignore deleted file mode 100644 index dca700a998..0000000000 --- a/src/udev/src/docs/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -libudev-overrides.txt -html/ -tmpl/ -xml/ -*.stamp -*.bak -version.xml -libudev-decl-list.txt -libudev-decl.txt -libudev-undeclared.txt -libudev-undocumented.txt -libudev-unused.txt -libudev.args -libudev.hierarchy -libudev.interfaces -libudev.prerequisites -libudev.signals diff --git a/src/udev/src/docs/Makefile.am b/src/udev/src/docs/Makefile.am deleted file mode 100644 index 07d06eb14f..0000000000 --- a/src/udev/src/docs/Makefile.am +++ /dev/null @@ -1,99 +0,0 @@ -## Process this file with automake to produce Makefile.in - -# We require automake 1.10 at least. -AUTOMAKE_OPTIONS = 1.10 - -# This is a blank Makefile.am for using gtk-doc. -# Copy this to your project's API docs directory and modify the variables to -# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples -# of using the various options. - -# The name of the module, e.g. 'glib'. -DOC_MODULE=libudev - -# Uncomment for versioned docs and specify the version of the module, e.g. '2'. -#DOC_MODULE_VERSION=2 - -# The top-level SGML file. You can change this if you want to. -DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.xml - -# The directory containing the source code. Relative to $(srcdir). -# gtk-doc will search all .c & .h files beneath here for inline comments -# documenting the functions and macros. -# e.g. DOC_SOURCE_DIR=../../../gtk -DOC_SOURCE_DIR=$(top_srcdir)/src - -# Extra options to pass to gtkdoc-scangobj. Not normally needed. -SCANGOBJ_OPTIONS= - -# Extra options to supply to gtkdoc-scan. -# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" -SCAN_OPTIONS= - -# Extra options to supply to gtkdoc-mkdb. -# e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml -MKDB_OPTIONS=--sgml-mode --output-format=xml --name-space udev - -# Extra options to supply to gtkdoc-mktmpl -# e.g. MKTMPL_OPTIONS=--only-section-tmpl -MKTMPL_OPTIONS= - -# Extra options to supply to gtkdoc-mkhtml -MKHTML_OPTIONS=--path=$(abs_srcdir) --path=$(abs_builddir) - -# Extra options to supply to gtkdoc-fixref. Not normally needed. -# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html -FIXXREF_OPTIONS= - -# Used for dependencies. The docs will be rebuilt if any of these change. -# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h -# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c -HFILE_GLOB=$(top_srcdir)/src/libudev*.h -CFILE_GLOB=$(top_srcdir)/src/libudev*.c - -# Extra header to include when scanning, which are not under DOC_SOURCE_DIR -# e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h -EXTRA_HFILES= - -# Header files to ignore when scanning. Use base file name, no paths -# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h -IGNORE_HFILES= libudev-private.h - -# Images to copy into HTML directory. -# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png -HTML_IMAGES= - -# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). -# e.g. content_files=running.sgml building.sgml changes-2.0.sgml -content_files = version.xml - -# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded -# These files must be listed here *and* in content_files -# e.g. expand_content_files=running.sgml -expand_content_files= - -# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. -# Only needed if you are using gtkdoc-scangobj to dynamically query widget -# signals and properties. -# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) -# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) -GTKDOC_CFLAGS= -GTKDOC_LIBS= - -# This includes the standard gtk-doc make rules, copied by gtkdocize. -include $(top_srcdir)/gtk-doc.make - -# Other files to distribute -# e.g. EXTRA_DIST += version.xml.in -EXTRA_DIST += version.xml.in - -# Files not to distribute -# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types -# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt -#DISTCLEANFILES += - -# Comment this out if you want your docs-status tested during 'make check' -if ENABLE_GTK_DOC -#TESTS_ENVIRONMENT = cd $(srcsrc) -#TESTS = $(GTKDOC_CHECK) -endif diff --git a/src/udev/src/docs/libudev-docs.xml b/src/udev/src/docs/libudev-docs.xml deleted file mode 100644 index b7feb45529..0000000000 --- a/src/udev/src/docs/libudev-docs.xml +++ /dev/null @@ -1,32 +0,0 @@ - - -]> - - - libudev Reference Manual - for libudev version &version; - - 2009-2011 - Kay Sievers <kay.sievers@vrfy.org> - - - - - libudev - - - - - - - - - - - API Index - - - diff --git a/src/udev/src/docs/libudev-sections.txt b/src/udev/src/docs/libudev-sections.txt deleted file mode 100644 index 15c3e934b5..0000000000 --- a/src/udev/src/docs/libudev-sections.txt +++ /dev/null @@ -1,127 +0,0 @@ -
-libudev -udev -udev -udev_ref -udev_unref -udev_new -udev_set_log_fn -udev_get_log_priority -udev_set_log_priority -udev_get_sys_path -udev_get_dev_path -udev_get_run_path -udev_get_userdata -udev_set_userdata -
- -
-libudev-list -udev_list -udev_list_entry -udev_list_entry_get_next -udev_list_entry_get_by_name -udev_list_entry_get_name -udev_list_entry_get_value -udev_list_entry_foreach -
- -
-libudev-device -udev_device -udev_device -udev_device_ref -udev_device_unref -udev_device_get_udev -udev_device_new_from_syspath -udev_device_new_from_devnum -udev_device_new_from_subsystem_sysname -udev_device_new_from_environment -udev_device_get_parent -udev_device_get_parent_with_subsystem_devtype -udev_device_get_devpath -udev_device_get_subsystem -udev_device_get_devtype -udev_device_get_syspath -udev_device_get_sysname -udev_device_get_sysnum -udev_device_get_devnode -udev_device_get_is_initialized -udev_device_get_devlinks_list_entry -udev_device_get_properties_list_entry -udev_device_get_tags_list_entry -udev_device_get_property_value -udev_device_get_driver -udev_device_get_devnum -udev_device_get_action -udev_device_get_sysattr_value -udev_device_get_sysattr_list_entry -udev_device_get_seqnum -udev_device_get_usec_since_initialized -udev_device_has_tag -
- -
-libudev-monitor -udev_monitor -udev_monitor -udev_monitor_ref -udev_monitor_unref -udev_monitor_get_udev -udev_monitor_new_from_netlink -udev_monitor_new_from_socket -udev_monitor_enable_receiving -udev_monitor_set_receive_buffer_size -udev_monitor_get_fd -udev_monitor_receive_device -udev_monitor_filter_add_match_subsystem_devtype -udev_monitor_filter_add_match_tag -udev_monitor_filter_update -udev_monitor_filter_remove -
- -
-libudev-enumerate -udev_enumerate -udev_enumerate -udev_enumerate_ref -udev_enumerate_unref -udev_enumerate_get_udev -udev_enumerate_new -udev_enumerate_add_match_subsystem -udev_enumerate_add_nomatch_subsystem -udev_enumerate_add_match_sysattr -udev_enumerate_add_nomatch_sysattr -udev_enumerate_add_match_property -udev_enumerate_add_match_tag -udev_enumerate_add_match_parent -udev_enumerate_add_match_is_initialized -udev_enumerate_add_match_sysname -udev_enumerate_add_syspath -udev_enumerate_scan_devices -udev_enumerate_scan_subsystems -udev_enumerate_get_list_entry -
- -
-libudev-queue -udev_queue -udev_queue -udev_queue_ref -udev_queue_unref -udev_queue_get_udev -udev_queue_new -udev_queue_get_udev_is_active -udev_queue_get_queue_is_empty -udev_queue_get_seqnum_is_finished -udev_queue_get_seqnum_sequence_is_finished -udev_queue_get_queued_list_entry -udev_queue_get_kernel_seqnum -udev_queue_get_udev_seqnum -
- -
-libudev-util -udev_util -udev_util_encode_string -
diff --git a/src/udev/src/docs/libudev.types b/src/udev/src/docs/libudev.types deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/udev/src/docs/version.xml.in b/src/udev/src/docs/version.xml.in deleted file mode 100644 index d78bda9342..0000000000 --- a/src/udev/src/docs/version.xml.in +++ /dev/null @@ -1 +0,0 @@ -@VERSION@ diff --git a/src/udev/src/floppy/60-floppy.rules b/src/udev/src/floppy/60-floppy.rules deleted file mode 100644 index 53e4a9e59a..0000000000 --- a/src/udev/src/floppy/60-floppy.rules +++ /dev/null @@ -1,4 +0,0 @@ -# do not edit this file, it will be overwritten on update - -SUBSYSTEM=="block", KERNEL=="fd[0-9]", ACTION=="add", ATTRS{cmos}=="?*", ENV{CMOS_TYPE}="$attr{cmos}", \ - RUN+="create_floppy_devices -c -t $env{CMOS_TYPE} -m %M -M 0660 -G floppy $root/%k" diff --git a/src/udev/src/floppy/create_floppy_devices.c b/src/udev/src/floppy/create_floppy_devices.c deleted file mode 100644 index f71ef0d809..0000000000 --- a/src/udev/src/floppy/create_floppy_devices.c +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Create all possible floppy device based on the CMOS type. - * Based upon code from drivers/block/floppy.c - * - * Copyright(C) 2005, SUSE Linux Products GmbH - * - * Author: Hannes Reinecke - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libudev.h" -#include "libudev-private.h" - -static char *table[] = { - "", "d360", "h1200", "u360", "u720", "h360", "h720", - "u1440", "u2880", "CompaQ", "h1440", "u1680", "h410", - "u820", "h1476", "u1722", "h420", "u830", "h1494", "u1743", - "h880", "u1040", "u1120", "h1600", "u1760", "u1920", - "u3200", "u3520", "u3840", "u1840", "u800", "u1600", - NULL -}; - -static int t360[] = { 1, 0 }; -static int t1200[] = { 2, 5, 6, 10, 12, 14, 16, 18, 20, 23, 0 }; -static int t3in[] = { 8, 9, 26, 27, 28, 7, 11, 15, 19, 24, 25, 29, 31, 3, 4, 13, 17, 21, 22, 30, 0 }; -static int *table_sup[] = { NULL, t360, t1200, t3in+5+8, t3in+5, t3in, t3in }; - -static void log_fn(struct udev *udev, int priority, - const char *file, int line, const char *fn, - const char *format, va_list args) -{ - vsyslog(priority, format, args); -} - -int main(int argc, char **argv) -{ - struct udev *udev; - char *dev; - char *devname; - char node[64]; - int type = 0, i, fdnum, c; - int major = 2, minor; - uid_t uid = 0; - gid_t gid = 0; - mode_t mode = 0660; - int create_nodes = 0; - int print_nodes = 0; - int is_err = 0; - - udev = udev_new(); - if (udev == NULL) - goto exit; - - udev_log_init("create_floppy_devices"); - udev_set_log_fn(udev, log_fn); - udev_selinux_init(udev); - - while ((c = getopt(argc, argv, "cudm:U:G:M:t:")) != -1) { - switch (c) { - case 'c': - create_nodes = 1; - break; - case 'd': - print_nodes = 1; - break; - case 'U': - uid = util_lookup_user(udev, optarg); - break; - case 'G': - gid = util_lookup_group(udev, optarg); - break; - case 'M': - mode = strtol(optarg, NULL, 0); - mode = mode & 0666; - break; - case 'm': - major = strtol(optarg, NULL, 0); - break; - case 't': - type = strtol(optarg, NULL, 0); - break; - default: - is_err++; - break; - } - } - - if (is_err || optind >= argc) { - printf("Usage: %s [OPTION] device\n" - " -c create\n" - " -d debug\n" - " -m Major number\n" - " -t floppy type number\n" - " -U device node user ownership\n" - " -G device node group owner\n" - " -M device node mode\n" - "\n", argv[0]); - return 1; - } - - dev = argv[optind]; - devname = strrchr(dev, '/'); - if (devname != NULL) - devname = &devname[1]; - else - devname = dev; - if (strncmp(devname, "fd", 2) != 0) { - fprintf(stderr,"Device '%s' is not a floppy device\n", dev); - return 1; - } - - fdnum = strtol(&devname[2], NULL, 10); - if (fdnum < 0 || fdnum > 7) { - fprintf(stderr,"Floppy device number %d out of range (0-7)\n", fdnum); - return 1; - } - if (fdnum > 3) - fdnum += 124; - - if (major < 1) { - fprintf(stderr,"Invalid major number %d\n", major); - return 1; - } - - if (type < 0 || type >= (int) ARRAY_SIZE(table_sup)) { - fprintf(stderr,"Invalid CMOS type %d\n", type); - return 1; - } - - if (type == 0) - return 0; - - i = 0; - while (table_sup[type][i]) { - sprintf(node, "%s%s", dev, table[table_sup[type][i]]); - minor = (table_sup[type][i] << 2) + fdnum; - if (print_nodes) - printf("%s b %.4o %d %d\n", node, mode, major, minor); - if (create_nodes) { - unlink(node); - udev_selinux_setfscreatecon(udev, node, S_IFBLK | mode); - mknod(node, S_IFBLK | mode, makedev(major,minor)); - udev_selinux_resetfscreatecon(udev); - chown(node, uid, gid); - chmod(node, S_IFBLK | mode); - } - i++; - } - - udev_selinux_exit(udev); - udev_unref(udev); - udev_log_close(); -exit: - return 0; -} diff --git a/src/udev/src/gudev/.gitignore b/src/udev/src/gudev/.gitignore deleted file mode 100644 index d20fa523e4..0000000000 --- a/src/udev/src/gudev/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -gtk-doc.make -docs/version.xml -gudev-1.0.pc -gudevenumtypes.c -gudevenumtypes.h -gudevmarshal.c -gudevmarshal.h -GUdev-1.0.gir -GUdev-1.0.typelib diff --git a/src/udev/src/gudev/COPYING b/src/udev/src/gudev/COPYING deleted file mode 100644 index da97db2bd0..0000000000 --- a/src/udev/src/gudev/COPYING +++ /dev/null @@ -1,502 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/src/udev/src/gudev/docs/.gitignore b/src/udev/src/gudev/docs/.gitignore deleted file mode 100644 index 8eada6d409..0000000000 --- a/src/udev/src/gudev/docs/.gitignore +++ /dev/null @@ -1,16 +0,0 @@ -gudev-overrides.txt -gudev-decl-list.txt -gudev-decl.txt -gudev-undeclared.txt -gudev-undocumented.txt -gudev-unused.txt -gudev.args -gudev.hierarchy -gudev.interfaces -gudev.prerequisites -gudev.signals -html.stamp -html/* -xml/* -tmpl/* -*.stamp diff --git a/src/udev/src/gudev/docs/Makefile.am b/src/udev/src/gudev/docs/Makefile.am deleted file mode 100644 index cfe696c503..0000000000 --- a/src/udev/src/gudev/docs/Makefile.am +++ /dev/null @@ -1,106 +0,0 @@ -## Process this file with automake to produce Makefile.in - -# We require automake 1.10 at least. -AUTOMAKE_OPTIONS = 1.10 - -# This is a blank Makefile.am for using gtk-doc. -# Copy this to your project's API docs directory and modify the variables to -# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples -# of using the various options. - -# The name of the module, e.g. 'glib'. -DOC_MODULE=gudev - -# Uncomment for versioned docs and specify the version of the module, e.g. '2'. -#DOC_MODULE_VERSION=2 - -# The top-level SGML file. You can change this if you want to. -DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.xml - -# The directory containing the source code. Relative to $(srcdir). -# gtk-doc will search all .c & .h files beneath here for inline comments -# documenting the functions and macros. -# e.g. DOC_SOURCE_DIR=../../../gtk -DOC_SOURCE_DIR=$(top_srcdir)/src - -# Extra options to pass to gtkdoc-scangobj. Not normally needed. -SCANGOBJ_OPTIONS= - -# Extra options to supply to gtkdoc-scan. -# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" -SCAN_OPTIONS= - -# Extra options to supply to gtkdoc-mkdb. -# e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml -MKDB_OPTIONS=--sgml-mode --output-format=xml --name-space=g_udev - -# Extra options to supply to gtkdoc-mktmpl -# e.g. MKTMPL_OPTIONS=--only-section-tmpl -MKTMPL_OPTIONS= - -# Extra options to supply to gtkdoc-mkhtml -MKHTML_OPTIONS=--path=$(abs_srcdir) --path=$(abs_builddir) - -# Extra options to supply to gtkdoc-fixref. Not normally needed. -# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html -FIXXREF_OPTIONS= - -# Used for dependencies. The docs will be rebuilt if any of these change. -# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h -# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c -HFILE_GLOB=$(top_srcdir)/src/gudev/*.h -CFILE_GLOB=$(top_srcdir)/src/gudev/*.c - -# Extra header to include when scanning, which are not under DOC_SOURCE_DIR -# e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h -EXTRA_HFILES= - -# Header files to ignore when scanning. Use base file name, no paths -# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h -IGNORE_HFILES= - -# Images to copy into HTML directory. -# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png -HTML_IMAGES= - -# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). -# e.g. content_files=running.sgml building.sgml changes-2.0.sgml -content_files = version.xml - -# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded -# These files must be listed here *and* in content_files -# e.g. expand_content_files=running.sgml -expand_content_files= - -# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. -# Only needed if you are using gtkdoc-scangobj to dynamically query widget -# signals and properties. -# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) -# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) -GTKDOC_CFLAGS = \ - $(DBUS_GLIB_CFLAGS) \ - $(GLIB_CFLAGS) \ - -I$(top_srcdir)/src/gudev \ - -I$(top_builddir)/src/gudev - -GTKDOC_LIBS = \ - $(GLIB_LIBS) \ - $(top_builddir)/libgudev-1.0.la - -# This includes the standard gtk-doc make rules, copied by gtkdocize. -include $(top_srcdir)/gtk-doc.make - -# Other files to distribute -# e.g. EXTRA_DIST += version.xml.in -EXTRA_DIST += version.xml.in - -# Files not to distribute -# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types -# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt -#DISTCLEANFILES += - -# Comment this out if you want your docs-status tested during 'make check' -if ENABLE_GTK_DOC -#TESTS_ENVIRONMENT = cd $(srcsrc) -#TESTS = $(GTKDOC_CHECK) -endif diff --git a/src/udev/src/gudev/docs/gudev-docs.xml b/src/udev/src/gudev/docs/gudev-docs.xml deleted file mode 100644 index f876c3bc05..0000000000 --- a/src/udev/src/gudev/docs/gudev-docs.xml +++ /dev/null @@ -1,93 +0,0 @@ - - -]> - - - GUDev Reference Manual - For GUdev version &version; - - - David - Zeuthen - -
- davidz@redhat.com -
-
-
- - Bastien - Nocera - -
- hadess@hadess.net -
-
-
-
- - - 2011 - The GUDev Authors - - - - - Permission is granted to copy, distribute and/or modify this - document under the terms of the GNU Free - Documentation License, Version 1.1 or any later - version published by the Free Software Foundation with no - Invariant Sections, no Front-Cover Texts, and no Back-Cover - Texts. You may obtain a copy of the GNU Free - Documentation License from the Free Software - Foundation by visiting their Web site or by writing - to: - -
- The Free Software Foundation, Inc., - 59 Temple Place - Suite 330, - Boston, MA 02111-1307, - USA -
-
- - - Many of the names used by companies to distinguish their - products and services are claimed as trademarks. Where those - names appear in any freedesktop.org documentation, and those - trademarks are made aware to the members of the - freedesktop.org Project, the names have been printed in caps - or initial caps. - -
-
- - - API Reference - - - This part presents the class and function reference for the - libgudev library. - - - - - - - - - Object Hierarchy - - - - Index - - - Index of new symbols in 165 - - - -
diff --git a/src/udev/src/gudev/docs/gudev-sections.txt b/src/udev/src/gudev/docs/gudev-sections.txt deleted file mode 100644 index 213e1a7465..0000000000 --- a/src/udev/src/gudev/docs/gudev-sections.txt +++ /dev/null @@ -1,113 +0,0 @@ -
-gudevclient -GUdevClient -GUdevClient -GUdevClientClass -GUdevDeviceType -GUdevDeviceNumber -g_udev_client_new -g_udev_client_query_by_subsystem -g_udev_client_query_by_device_number -g_udev_client_query_by_device_file -g_udev_client_query_by_sysfs_path -g_udev_client_query_by_subsystem_and_name - -G_UDEV_CLIENT -G_UDEV_IS_CLIENT -G_UDEV_TYPE_CLIENT -g_udev_client_get_type -G_UDEV_CLIENT_CLASS -G_UDEV_IS_CLIENT_CLASS -G_UDEV_CLIENT_GET_CLASS - -GUdevClientPrivate -
- -
-gudevdevice -GUdevDevice -GUdevDevice -GUdevDeviceClass -g_udev_device_get_subsystem -g_udev_device_get_devtype -g_udev_device_get_name -g_udev_device_get_number -g_udev_device_get_sysfs_path -g_udev_device_get_driver -g_udev_device_get_action -g_udev_device_get_seqnum -g_udev_device_get_device_type -g_udev_device_get_device_number -g_udev_device_get_device_file -g_udev_device_get_device_file_symlinks -g_udev_device_get_parent -g_udev_device_get_parent_with_subsystem -g_udev_device_get_tags -g_udev_device_get_is_initialized -g_udev_device_get_usec_since_initialized -g_udev_device_get_property_keys -g_udev_device_has_property -g_udev_device_get_property -g_udev_device_get_property_as_int -g_udev_device_get_property_as_uint64 -g_udev_device_get_property_as_double -g_udev_device_get_property_as_boolean -g_udev_device_get_property_as_strv -g_udev_device_get_sysfs_attr -g_udev_device_get_sysfs_attr_as_int -g_udev_device_get_sysfs_attr_as_uint64 -g_udev_device_get_sysfs_attr_as_double -g_udev_device_get_sysfs_attr_as_boolean -g_udev_device_get_sysfs_attr_as_strv - -G_UDEV_DEVICE -G_UDEV_IS_DEVICE -G_UDEV_TYPE_DEVICE -g_udev_device_get_type -G_UDEV_DEVICE_CLASS -G_UDEV_IS_DEVICE_CLASS -G_UDEV_DEVICE_GET_CLASS - -GUdevDevicePrivate -
- -
-gudevenumerator -GUdevEnumerator -GUdevEnumerator -GUdevEnumeratorClass -g_udev_enumerator_new -g_udev_enumerator_add_match_subsystem -g_udev_enumerator_add_nomatch_subsystem -g_udev_enumerator_add_match_sysfs_attr -g_udev_enumerator_add_nomatch_sysfs_attr -g_udev_enumerator_add_match_property -g_udev_enumerator_add_match_name -g_udev_enumerator_add_match_tag -g_udev_enumerator_add_match_is_initialized -g_udev_enumerator_add_sysfs_path -g_udev_enumerator_execute - -G_UDEV_ENUMERATOR -G_UDEV_IS_ENUMERATOR -G_UDEV_TYPE_ENUMERATOR -g_udev_enumerator_get_type -G_UDEV_ENUMERATOR_CLASS -G_UDEV_IS_ENUMERATOR_CLASS -G_UDEV_ENUMERATOR_GET_CLASS - -GUdevEnumeratorPrivate -
- -
-gudevmarshal - -g_udev_marshal_VOID__STRING_OBJECT -
- -
-gudevenumtypes - -G_TYPE_UDEV_DEVICE_TYPE -g_udev_device_type_get_type -
diff --git a/src/udev/src/gudev/docs/gudev.types b/src/udev/src/gudev/docs/gudev.types deleted file mode 100644 index a89857a04d..0000000000 --- a/src/udev/src/gudev/docs/gudev.types +++ /dev/null @@ -1,4 +0,0 @@ -g_udev_device_type_get_type -g_udev_device_get_type -g_udev_client_get_type -g_udev_enumerator_get_type diff --git a/src/udev/src/gudev/docs/version.xml.in b/src/udev/src/gudev/docs/version.xml.in deleted file mode 100644 index d78bda9342..0000000000 --- a/src/udev/src/gudev/docs/version.xml.in +++ /dev/null @@ -1 +0,0 @@ -@VERSION@ diff --git a/src/udev/src/gudev/gjs-example.js b/src/udev/src/gudev/gjs-example.js deleted file mode 100755 index 5586fd6a61..0000000000 --- a/src/udev/src/gudev/gjs-example.js +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env gjs-console - -// This currently depends on the following patches to gjs -// -// http://bugzilla.gnome.org/show_bug.cgi?id=584558 -// http://bugzilla.gnome.org/show_bug.cgi?id=584560 -// http://bugzilla.gnome.org/show_bug.cgi?id=584568 - -const GUdev = imports.gi.GUdev; -const Mainloop = imports.mainloop; - -function print_device (device) { - print (" subsystem: " + device.get_subsystem ()); - print (" devtype: " + device.get_devtype ()); - print (" name: " + device.get_name ()); - print (" number: " + device.get_number ()); - print (" sysfs_path: " + device.get_sysfs_path ()); - print (" driver: " + device.get_driver ()); - print (" action: " + device.get_action ()); - print (" seqnum: " + device.get_seqnum ()); - print (" device type: " + device.get_device_type ()); - print (" device number: " + device.get_device_number ()); - print (" device file: " + device.get_device_file ()); - print (" device file symlinks: " + device.get_device_file_symlinks ()); - print (" foo: " + device.get_sysfs_attr_as_strv ("stat")); - var keys = device.get_property_keys (); - for (var n = 0; n < keys.length; n++) { - print (" " + keys[n] + "=" + device.get_property (keys[n])); - } -} - -function on_uevent (client, action, device) { - print ("action " + action + " on device " + device.get_sysfs_path()); - print_device (device); - print (""); -} - -var client = new GUdev.Client ({subsystems: ["block", "usb/usb_interface"]}); -client.connect ("uevent", on_uevent); - -var block_devices = client.query_by_subsystem ("block"); -for (var n = 0; n < block_devices.length; n++) { - print ("block device: " + block_devices[n].get_device_file ()); -} - -var d; - -d = client.query_by_device_number (GUdev.DeviceType.BLOCK, 0x0810); -if (d == null) { - print ("query_by_device_number 0x810 -> null"); -} else { - print ("query_by_device_number 0x810 -> " + d.get_device_file ()); - var dd = d.get_parent_with_subsystem ("usb", null); - print_device (dd); - print ("--------------------------------------------------------------------------"); - while (d != null) { - print_device (d); - print (""); - d = d.get_parent (); - } -} - -d = client.query_by_sysfs_path ("/sys/block/sda/sda1"); -print ("query_by_sysfs_path (\"/sys/block/sda1\") -> " + d.get_device_file ()); - -d = client.query_by_subsystem_and_name ("block", "sda2"); -print ("query_by_subsystem_and_name (\"block\", \"sda2\") -> " + d.get_device_file ()); - -d = client.query_by_device_file ("/dev/sda"); -print ("query_by_device_file (\"/dev/sda\") -> " + d.get_device_file ()); - -d = client.query_by_device_file ("/dev/block/8:0"); -print ("query_by_device_file (\"/dev/block/8:0\") -> " + d.get_device_file ()); - -Mainloop.run('udev-example'); diff --git a/src/udev/src/gudev/gudev-1.0.pc.in b/src/udev/src/gudev/gudev-1.0.pc.in deleted file mode 100644 index 058262d767..0000000000 --- a/src/udev/src/gudev/gudev-1.0.pc.in +++ /dev/null @@ -1,11 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: gudev-1.0 -Description: GObject bindings for libudev -Version: @VERSION@ -Requires: glib-2.0, gobject-2.0 -Libs: -L${libdir} -lgudev-1.0 -Cflags: -I${includedir}/gudev-1.0 diff --git a/src/udev/src/gudev/gudev.h b/src/udev/src/gudev/gudev.h deleted file mode 100644 index a313460817..0000000000 --- a/src/udev/src/gudev/gudev.h +++ /dev/null @@ -1,33 +0,0 @@ -/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- - * - * Copyright (C) 2008 David Zeuthen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __G_UDEV_H__ -#define __G_UDEV_H__ - -#define _GUDEV_INSIDE_GUDEV_H 1 -#include -#include -#include -#include -#include -#include -#undef _GUDEV_INSIDE_GUDEV_H - -#endif /* __G_UDEV_H__ */ diff --git a/src/udev/src/gudev/gudevclient.c b/src/udev/src/gudev/gudevclient.c deleted file mode 100644 index 2b94102ac5..0000000000 --- a/src/udev/src/gudev/gudevclient.c +++ /dev/null @@ -1,527 +0,0 @@ -/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- - * - * Copyright (C) 2008-2010 David Zeuthen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - * - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include -#include - -#include "gudevclient.h" -#include "gudevdevice.h" -#include "gudevmarshal.h" -#include "gudevprivate.h" - -/** - * SECTION:gudevclient - * @short_description: Query devices and listen to uevents - * - * #GUdevClient is used to query information about devices on a Linux - * system from the Linux kernel and the udev device - * manager. - * - * Device information is retrieved from the kernel (through the - * sysfs filesystem) and the udev daemon (through a - * tmpfs filesystem) and presented through - * #GUdevDevice objects. This means that no blocking IO ever happens - * (in both cases, we are essentially just reading data from kernel - * memory) and as such there are no asynchronous versions of the - * provided methods. - * - * To get #GUdevDevice objects, use - * g_udev_client_query_by_subsystem(), - * g_udev_client_query_by_device_number(), - * g_udev_client_query_by_device_file(), - * g_udev_client_query_by_sysfs_path(), - * g_udev_client_query_by_subsystem_and_name() - * or the #GUdevEnumerator type. - * - * To listen to uevents, connect to the #GUdevClient::uevent signal. - */ - -struct _GUdevClientPrivate -{ - GSource *watch_source; - struct udev *udev; - struct udev_monitor *monitor; - - gchar **subsystems; -}; - -enum -{ - PROP_0, - PROP_SUBSYSTEMS, -}; - -enum -{ - UEVENT_SIGNAL, - LAST_SIGNAL, -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - -G_DEFINE_TYPE (GUdevClient, g_udev_client, G_TYPE_OBJECT) - -/* ---------------------------------------------------------------------------------------------------- */ - -static gboolean -monitor_event (GIOChannel *source, - GIOCondition condition, - gpointer data) -{ - GUdevClient *client = (GUdevClient *) data; - GUdevDevice *device; - struct udev_device *udevice; - - if (client->priv->monitor == NULL) - goto out; - udevice = udev_monitor_receive_device (client->priv->monitor); - if (udevice == NULL) - goto out; - - device = _g_udev_device_new (udevice); - udev_device_unref (udevice); - g_signal_emit (client, - signals[UEVENT_SIGNAL], - 0, - g_udev_device_get_action (device), - device); - g_object_unref (device); - - out: - return TRUE; -} - -static void -g_udev_client_finalize (GObject *object) -{ - GUdevClient *client = G_UDEV_CLIENT (object); - - if (client->priv->watch_source != NULL) - { - g_source_destroy (client->priv->watch_source); - client->priv->watch_source = NULL; - } - - if (client->priv->monitor != NULL) - { - udev_monitor_unref (client->priv->monitor); - client->priv->monitor = NULL; - } - - if (client->priv->udev != NULL) - { - udev_unref (client->priv->udev); - client->priv->udev = NULL; - } - - g_strfreev (client->priv->subsystems); - - if (G_OBJECT_CLASS (g_udev_client_parent_class)->finalize != NULL) - G_OBJECT_CLASS (g_udev_client_parent_class)->finalize (object); -} - -static void -g_udev_client_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GUdevClient *client = G_UDEV_CLIENT (object); - - switch (prop_id) - { - case PROP_SUBSYSTEMS: - if (client->priv->subsystems != NULL) - g_strfreev (client->priv->subsystems); - client->priv->subsystems = g_strdupv (g_value_get_boxed (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -g_udev_client_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GUdevClient *client = G_UDEV_CLIENT (object); - - switch (prop_id) - { - case PROP_SUBSYSTEMS: - g_value_set_boxed (value, client->priv->subsystems); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -g_udev_client_constructed (GObject *object) -{ - GUdevClient *client = G_UDEV_CLIENT (object); - GIOChannel *channel; - guint n; - - client->priv->udev = udev_new (); - - /* connect to event source */ - client->priv->monitor = udev_monitor_new_from_netlink (client->priv->udev, "udev"); - - //g_debug ("ss = %p", client->priv->subsystems); - - if (client->priv->subsystems != NULL) - { - /* install subsystem filters to only wake up for certain events */ - for (n = 0; client->priv->subsystems[n] != NULL; n++) - { - gchar *subsystem; - gchar *devtype; - gchar *s; - - subsystem = g_strdup (client->priv->subsystems[n]); - devtype = NULL; - - //g_debug ("s = '%s'", subsystem); - - s = strstr (subsystem, "/"); - if (s != NULL) - { - devtype = s + 1; - *s = '\0'; - } - - if (client->priv->monitor != NULL) - udev_monitor_filter_add_match_subsystem_devtype (client->priv->monitor, subsystem, devtype); - - g_free (subsystem); - } - - /* listen to events, and buffer them */ - if (client->priv->monitor != NULL) - { - udev_monitor_enable_receiving (client->priv->monitor); - channel = g_io_channel_unix_new (udev_monitor_get_fd (client->priv->monitor)); - client->priv->watch_source = g_io_create_watch (channel, G_IO_IN); - g_io_channel_unref (channel); - g_source_set_callback (client->priv->watch_source, (GSourceFunc) monitor_event, client, NULL); - g_source_attach (client->priv->watch_source, g_main_context_get_thread_default ()); - g_source_unref (client->priv->watch_source); - } - else - { - client->priv->watch_source = NULL; - } - } - - if (G_OBJECT_CLASS (g_udev_client_parent_class)->constructed != NULL) - G_OBJECT_CLASS (g_udev_client_parent_class)->constructed (object); -} - - -static void -g_udev_client_class_init (GUdevClientClass *klass) -{ - GObjectClass *gobject_class = (GObjectClass *) klass; - - gobject_class->constructed = g_udev_client_constructed; - gobject_class->set_property = g_udev_client_set_property; - gobject_class->get_property = g_udev_client_get_property; - gobject_class->finalize = g_udev_client_finalize; - - /** - * GUdevClient:subsystems: - * - * The subsystems to listen for uevents on. - * - * To listen for only a specific DEVTYPE for a given SUBSYSTEM, use - * "subsystem/devtype". For example, to only listen for uevents - * where SUBSYSTEM is usb and DEVTYPE is usb_interface, use - * "usb/usb_interface". - * - * If this property is %NULL, then no events will be reported. If - * it's the empty array, events from all subsystems will be - * reported. - */ - g_object_class_install_property (gobject_class, - PROP_SUBSYSTEMS, - g_param_spec_boxed ("subsystems", - "The subsystems to listen for changes on", - "The subsystems to listen for changes on", - G_TYPE_STRV, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE)); - - /** - * GUdevClient::uevent: - * @client: The #GUdevClient receiving the event. - * @action: The action for the uevent e.g. "add", "remove", "change", "move", etc. - * @device: Details about the #GUdevDevice the event is for. - * - * Emitted when @client receives an uevent. - * - * This signal is emitted in the - * thread-default main loop - * of the thread that @client was created in. - */ - signals[UEVENT_SIGNAL] = g_signal_new ("uevent", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GUdevClientClass, uevent), - NULL, - NULL, - g_udev_marshal_VOID__STRING_OBJECT, - G_TYPE_NONE, - 2, - G_TYPE_STRING, - G_UDEV_TYPE_DEVICE); - - g_type_class_add_private (klass, sizeof (GUdevClientPrivate)); -} - -static void -g_udev_client_init (GUdevClient *client) -{ - client->priv = G_TYPE_INSTANCE_GET_PRIVATE (client, - G_UDEV_TYPE_CLIENT, - GUdevClientPrivate); -} - -/** - * g_udev_client_new: - * @subsystems: (array zero-terminated=1) (element-type utf8) (transfer none) (allow-none): A %NULL terminated string array of subsystems to listen for uevents on, %NULL to not listen on uevents at all, or an empty array to listen to uevents on all subsystems. See the documentation for the #GUdevClient:subsystems property for details on this parameter. - * - * Constructs a #GUdevClient object that can be used to query - * information about devices. Connect to the #GUdevClient::uevent - * signal to listen for uevents. Note that signals are emitted in the - * thread-default main loop - * of the thread that you call this constructor from. - * - * Returns: A new #GUdevClient object. Free with g_object_unref(). - */ -GUdevClient * -g_udev_client_new (const gchar * const *subsystems) -{ - return G_UDEV_CLIENT (g_object_new (G_UDEV_TYPE_CLIENT, "subsystems", subsystems, NULL)); -} - -/** - * g_udev_client_query_by_subsystem: - * @client: A #GUdevClient. - * @subsystem: (allow-none): The subsystem to get devices for or %NULL to get all devices. - * - * Gets all devices belonging to @subsystem. - * - * Returns: (element-type GUdevDevice) (transfer full): A list of #GUdevDevice objects. The caller should free the result by using g_object_unref() on each element in the list and then g_list_free() on the list. - */ -GList * -g_udev_client_query_by_subsystem (GUdevClient *client, - const gchar *subsystem) -{ - struct udev_enumerate *enumerate; - struct udev_list_entry *l, *devices; - GList *ret; - - g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL); - - ret = NULL; - - /* prepare a device scan */ - enumerate = udev_enumerate_new (client->priv->udev); - - /* filter for subsystem */ - if (subsystem != NULL) - udev_enumerate_add_match_subsystem (enumerate, subsystem); - /* retrieve the list */ - udev_enumerate_scan_devices (enumerate); - - /* add devices to the list */ - devices = udev_enumerate_get_list_entry (enumerate); - for (l = devices; l != NULL; l = udev_list_entry_get_next (l)) - { - struct udev_device *udevice; - GUdevDevice *device; - - udevice = udev_device_new_from_syspath (udev_enumerate_get_udev (enumerate), - udev_list_entry_get_name (l)); - if (udevice == NULL) - continue; - device = _g_udev_device_new (udevice); - udev_device_unref (udevice); - ret = g_list_prepend (ret, device); - } - udev_enumerate_unref (enumerate); - - ret = g_list_reverse (ret); - - return ret; -} - -/** - * g_udev_client_query_by_device_number: - * @client: A #GUdevClient. - * @type: A value from the #GUdevDeviceType enumeration. - * @number: A device number. - * - * Looks up a device for a type and device number. - * - * Returns: (transfer full): A #GUdevDevice object or %NULL if the device was not found. Free with g_object_unref(). - */ -GUdevDevice * -g_udev_client_query_by_device_number (GUdevClient *client, - GUdevDeviceType type, - GUdevDeviceNumber number) -{ - struct udev_device *udevice; - GUdevDevice *device; - - g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL); - - device = NULL; - udevice = udev_device_new_from_devnum (client->priv->udev, type, number); - - if (udevice == NULL) - goto out; - - device = _g_udev_device_new (udevice); - udev_device_unref (udevice); - - out: - return device; -} - -/** - * g_udev_client_query_by_device_file: - * @client: A #GUdevClient. - * @device_file: A device file. - * - * Looks up a device for a device file. - * - * Returns: (transfer full): A #GUdevDevice object or %NULL if the device was not found. Free with g_object_unref(). - */ -GUdevDevice * -g_udev_client_query_by_device_file (GUdevClient *client, - const gchar *device_file) -{ - struct stat stat_buf; - GUdevDevice *device; - - g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL); - g_return_val_if_fail (device_file != NULL, NULL); - - device = NULL; - - if (stat (device_file, &stat_buf) != 0) - goto out; - - if (stat_buf.st_rdev == 0) - goto out; - - if (S_ISBLK (stat_buf.st_mode)) - device = g_udev_client_query_by_device_number (client, G_UDEV_DEVICE_TYPE_BLOCK, stat_buf.st_rdev); - else if (S_ISCHR (stat_buf.st_mode)) - device = g_udev_client_query_by_device_number (client, G_UDEV_DEVICE_TYPE_CHAR, stat_buf.st_rdev); - - out: - return device; -} - -/** - * g_udev_client_query_by_sysfs_path: - * @client: A #GUdevClient. - * @sysfs_path: A sysfs path. - * - * Looks up a device for a sysfs path. - * - * Returns: (transfer full): A #GUdevDevice object or %NULL if the device was not found. Free with g_object_unref(). - */ -GUdevDevice * -g_udev_client_query_by_sysfs_path (GUdevClient *client, - const gchar *sysfs_path) -{ - struct udev_device *udevice; - GUdevDevice *device; - - g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL); - g_return_val_if_fail (sysfs_path != NULL, NULL); - - device = NULL; - udevice = udev_device_new_from_syspath (client->priv->udev, sysfs_path); - if (udevice == NULL) - goto out; - - device = _g_udev_device_new (udevice); - udev_device_unref (udevice); - - out: - return device; -} - -/** - * g_udev_client_query_by_subsystem_and_name: - * @client: A #GUdevClient. - * @subsystem: A subsystem name. - * @name: The name of the device. - * - * Looks up a device for a subsystem and name. - * - * Returns: (transfer full): A #GUdevDevice object or %NULL if the device was not found. Free with g_object_unref(). - */ -GUdevDevice * -g_udev_client_query_by_subsystem_and_name (GUdevClient *client, - const gchar *subsystem, - const gchar *name) -{ - struct udev_device *udevice; - GUdevDevice *device; - - g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL); - g_return_val_if_fail (subsystem != NULL, NULL); - g_return_val_if_fail (name != NULL, NULL); - - device = NULL; - udevice = udev_device_new_from_subsystem_sysname (client->priv->udev, subsystem, name); - if (udevice == NULL) - goto out; - - device = _g_udev_device_new (udevice); - udev_device_unref (udevice); - - out: - return device; -} - -struct udev * -_g_udev_client_get_udev (GUdevClient *client) -{ - g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL); - return client->priv->udev; -} diff --git a/src/udev/src/gudev/gudevclient.h b/src/udev/src/gudev/gudevclient.h deleted file mode 100644 index b425d03d48..0000000000 --- a/src/udev/src/gudev/gudevclient.h +++ /dev/null @@ -1,100 +0,0 @@ -/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- - * - * Copyright (C) 2008 David Zeuthen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#if !defined (_GUDEV_COMPILATION) && !defined(_GUDEV_INSIDE_GUDEV_H) -#error "Only can be included directly, this file may disappear or change contents." -#endif - -#ifndef __G_UDEV_CLIENT_H__ -#define __G_UDEV_CLIENT_H__ - -#include - -G_BEGIN_DECLS - -#define G_UDEV_TYPE_CLIENT (g_udev_client_get_type ()) -#define G_UDEV_CLIENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_UDEV_TYPE_CLIENT, GUdevClient)) -#define G_UDEV_CLIENT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_UDEV_TYPE_CLIENT, GUdevClientClass)) -#define G_UDEV_IS_CLIENT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_UDEV_TYPE_CLIENT)) -#define G_UDEV_IS_CLIENT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_UDEV_TYPE_CLIENT)) -#define G_UDEV_CLIENT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_UDEV_TYPE_CLIENT, GUdevClientClass)) - -typedef struct _GUdevClientClass GUdevClientClass; -typedef struct _GUdevClientPrivate GUdevClientPrivate; - -/** - * GUdevClient: - * - * The #GUdevClient struct is opaque and should not be accessed directly. - */ -struct _GUdevClient -{ - GObject parent; - - /*< private >*/ - GUdevClientPrivate *priv; -}; - -/** - * GUdevClientClass: - * @parent_class: Parent class. - * @uevent: Signal class handler for the #GUdevClient::uevent signal. - * - * Class structure for #GUdevClient. - */ -struct _GUdevClientClass -{ - GObjectClass parent_class; - - /* signals */ - void (*uevent) (GUdevClient *client, - const gchar *action, - GUdevDevice *device); - - /*< private >*/ - /* Padding for future expansion */ - void (*reserved1) (void); - void (*reserved2) (void); - void (*reserved3) (void); - void (*reserved4) (void); - void (*reserved5) (void); - void (*reserved6) (void); - void (*reserved7) (void); - void (*reserved8) (void); -}; - -GType g_udev_client_get_type (void) G_GNUC_CONST; -GUdevClient *g_udev_client_new (const gchar* const *subsystems); -GList *g_udev_client_query_by_subsystem (GUdevClient *client, - const gchar *subsystem); -GUdevDevice *g_udev_client_query_by_device_number (GUdevClient *client, - GUdevDeviceType type, - GUdevDeviceNumber number); -GUdevDevice *g_udev_client_query_by_device_file (GUdevClient *client, - const gchar *device_file); -GUdevDevice *g_udev_client_query_by_sysfs_path (GUdevClient *client, - const gchar *sysfs_path); -GUdevDevice *g_udev_client_query_by_subsystem_and_name (GUdevClient *client, - const gchar *subsystem, - const gchar *name); - -G_END_DECLS - -#endif /* __G_UDEV_CLIENT_H__ */ diff --git a/src/udev/src/gudev/gudevdevice.c b/src/udev/src/gudev/gudevdevice.c deleted file mode 100644 index 62a26f99b7..0000000000 --- a/src/udev/src/gudev/gudevdevice.c +++ /dev/null @@ -1,963 +0,0 @@ -/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- - * - * Copyright (C) 2008 David Zeuthen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - * - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include -#include - -#include "gudevdevice.h" -#include "gudevprivate.h" - -/** - * SECTION:gudevdevice - * @short_description: Get information about a device - * - * The #GUdevDevice class is used to get information about a specific - * device. Note that you cannot instantiate a #GUdevDevice object - * yourself. Instead you must use #GUdevClient to obtain #GUdevDevice - * objects. - * - * To get basic information about a device, use - * g_udev_device_get_subsystem(), g_udev_device_get_devtype(), - * g_udev_device_get_name(), g_udev_device_get_number(), - * g_udev_device_get_sysfs_path(), g_udev_device_get_driver(), - * g_udev_device_get_action(), g_udev_device_get_seqnum(), - * g_udev_device_get_device_type(), g_udev_device_get_device_number(), - * g_udev_device_get_device_file(), - * g_udev_device_get_device_file_symlinks(). - * - * To navigate the device tree, use g_udev_device_get_parent() and - * g_udev_device_get_parent_with_subsystem(). - * - * To access udev properties for the device, use - * g_udev_device_get_property_keys(), - * g_udev_device_has_property(), - * g_udev_device_get_property(), - * g_udev_device_get_property_as_int(), - * g_udev_device_get_property_as_uint64(), - * g_udev_device_get_property_as_double(), - * g_udev_device_get_property_as_boolean() and - * g_udev_device_get_property_as_strv(). - * - * To access sysfs attributes for the device, use - * g_udev_device_get_sysfs_attr(), - * g_udev_device_get_sysfs_attr_as_int(), - * g_udev_device_get_sysfs_attr_as_uint64(), - * g_udev_device_get_sysfs_attr_as_double(), - * g_udev_device_get_sysfs_attr_as_boolean() and - * g_udev_device_get_sysfs_attr_as_strv(). - * - * Note that all getters on #GUdevDevice are non-reffing – returned - * values are owned by the object, should not be freed and are only - * valid as long as the object is alive. - * - * By design, #GUdevDevice will not react to changes for a device – it - * only contains a snapshot of information when the #GUdevDevice - * object was created. To work with changes, you typically connect to - * the #GUdevClient::uevent signal on a #GUdevClient and get a new - * #GUdevDevice whenever an event happens. - */ - -struct _GUdevDevicePrivate -{ - struct udev_device *udevice; - - /* computed ondemand and cached */ - gchar **device_file_symlinks; - gchar **property_keys; - gchar **tags; - GHashTable *prop_strvs; - GHashTable *sysfs_attr_strvs; -}; - -G_DEFINE_TYPE (GUdevDevice, g_udev_device, G_TYPE_OBJECT) - -static void -g_udev_device_finalize (GObject *object) -{ - GUdevDevice *device = G_UDEV_DEVICE (object); - - g_strfreev (device->priv->device_file_symlinks); - g_strfreev (device->priv->property_keys); - g_strfreev (device->priv->tags); - - if (device->priv->udevice != NULL) - udev_device_unref (device->priv->udevice); - - if (device->priv->prop_strvs != NULL) - g_hash_table_unref (device->priv->prop_strvs); - - if (device->priv->sysfs_attr_strvs != NULL) - g_hash_table_unref (device->priv->sysfs_attr_strvs); - - if (G_OBJECT_CLASS (g_udev_device_parent_class)->finalize != NULL) - (* G_OBJECT_CLASS (g_udev_device_parent_class)->finalize) (object); -} - -static void -g_udev_device_class_init (GUdevDeviceClass *klass) -{ - GObjectClass *gobject_class = (GObjectClass *) klass; - - gobject_class->finalize = g_udev_device_finalize; - - g_type_class_add_private (klass, sizeof (GUdevDevicePrivate)); -} - -static void -g_udev_device_init (GUdevDevice *device) -{ - device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device, - G_UDEV_TYPE_DEVICE, - GUdevDevicePrivate); -} - - -GUdevDevice * -_g_udev_device_new (struct udev_device *udevice) -{ - GUdevDevice *device; - - device = G_UDEV_DEVICE (g_object_new (G_UDEV_TYPE_DEVICE, NULL)); - device->priv->udevice = udev_device_ref (udevice); - - return device; -} - -/** - * g_udev_device_get_subsystem: - * @device: A #GUdevDevice. - * - * Gets the subsystem for @device. - * - * Returns: The subsystem for @device. - */ -const gchar * -g_udev_device_get_subsystem (GUdevDevice *device) -{ - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); - return udev_device_get_subsystem (device->priv->udevice); -} - -/** - * g_udev_device_get_devtype: - * @device: A #GUdevDevice. - * - * Gets the device type for @device. - * - * Returns: The devtype for @device. - */ -const gchar * -g_udev_device_get_devtype (GUdevDevice *device) -{ - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); - return udev_device_get_devtype (device->priv->udevice); -} - -/** - * g_udev_device_get_name: - * @device: A #GUdevDevice. - * - * Gets the name of @device, e.g. "sda3". - * - * Returns: The name of @device. - */ -const gchar * -g_udev_device_get_name (GUdevDevice *device) -{ - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); - return udev_device_get_sysname (device->priv->udevice); -} - -/** - * g_udev_device_get_number: - * @device: A #GUdevDevice. - * - * Gets the number of @device, e.g. "3" if g_udev_device_get_name() returns "sda3". - * - * Returns: The number of @device. - */ -const gchar * -g_udev_device_get_number (GUdevDevice *device) -{ - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); - return udev_device_get_sysnum (device->priv->udevice); -} - -/** - * g_udev_device_get_sysfs_path: - * @device: A #GUdevDevice. - * - * Gets the sysfs path for @device. - * - * Returns: The sysfs path for @device. - */ -const gchar * -g_udev_device_get_sysfs_path (GUdevDevice *device) -{ - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); - return udev_device_get_syspath (device->priv->udevice); -} - -/** - * g_udev_device_get_driver: - * @device: A #GUdevDevice. - * - * Gets the name of the driver used for @device. - * - * Returns: The name of the driver for @device or %NULL if unknown. - */ -const gchar * -g_udev_device_get_driver (GUdevDevice *device) -{ - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); - return udev_device_get_driver (device->priv->udevice); -} - -/** - * g_udev_device_get_action: - * @device: A #GUdevDevice. - * - * Gets the most recent action (e.g. "add", "remove", "change", etc.) for @device. - * - * Returns: An action string. - */ -const gchar * -g_udev_device_get_action (GUdevDevice *device) -{ - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); - return udev_device_get_action (device->priv->udevice); -} - -/** - * g_udev_device_get_seqnum: - * @device: A #GUdevDevice. - * - * Gets the most recent sequence number for @device. - * - * Returns: A sequence number. - */ -guint64 -g_udev_device_get_seqnum (GUdevDevice *device) -{ - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0); - return udev_device_get_seqnum (device->priv->udevice); -} - -/** - * g_udev_device_get_device_type: - * @device: A #GUdevDevice. - * - * Gets the type of the device file, if any, for @device. - * - * Returns: The device number for @device or #G_UDEV_DEVICE_TYPE_NONE if the device does not have a device file. - */ -GUdevDeviceType -g_udev_device_get_device_type (GUdevDevice *device) -{ - struct stat stat_buf; - const gchar *device_file; - GUdevDeviceType type; - - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), G_UDEV_DEVICE_TYPE_NONE); - - type = G_UDEV_DEVICE_TYPE_NONE; - - /* TODO: would be better to have support for this in libudev... */ - - device_file = g_udev_device_get_device_file (device); - if (device_file == NULL) - goto out; - - if (stat (device_file, &stat_buf) != 0) - goto out; - - if (S_ISBLK (stat_buf.st_mode)) - type = G_UDEV_DEVICE_TYPE_BLOCK; - else if (S_ISCHR (stat_buf.st_mode)) - type = G_UDEV_DEVICE_TYPE_CHAR; - - out: - return type; -} - -/** - * g_udev_device_get_device_number: - * @device: A #GUdevDevice. - * - * Gets the device number, if any, for @device. - * - * Returns: The device number for @device or 0 if unknown. - */ -GUdevDeviceNumber -g_udev_device_get_device_number (GUdevDevice *device) -{ - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0); - return udev_device_get_devnum (device->priv->udevice); -} - -/** - * g_udev_device_get_device_file: - * @device: A #GUdevDevice. - * - * Gets the device file for @device. - * - * Returns: The device file for @device or %NULL if no device file - * exists. - */ -const gchar * -g_udev_device_get_device_file (GUdevDevice *device) -{ - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); - return udev_device_get_devnode (device->priv->udevice); -} - -/** - * g_udev_device_get_device_file_symlinks: - * @device: A #GUdevDevice. - * - * Gets a list of symlinks (in /dev) that points to - * the device file for @device. - * - * Returns: (transfer none) (array zero-terminated=1) (element-type utf8): A %NULL terminated string array of symlinks. This array is owned by @device and should not be freed by the caller. - */ -const gchar * const * -g_udev_device_get_device_file_symlinks (GUdevDevice *device) -{ - struct udev_list_entry *l; - GPtrArray *p; - - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); - - if (device->priv->device_file_symlinks != NULL) - goto out; - - p = g_ptr_array_new (); - for (l = udev_device_get_devlinks_list_entry (device->priv->udevice); l != NULL; l = udev_list_entry_get_next (l)) - { - g_ptr_array_add (p, g_strdup (udev_list_entry_get_name (l))); - } - g_ptr_array_add (p, NULL); - device->priv->device_file_symlinks = (gchar **) g_ptr_array_free (p, FALSE); - - out: - return (const gchar * const *) device->priv->device_file_symlinks; -} - -/* ---------------------------------------------------------------------------------------------------- */ - -/** - * g_udev_device_get_parent: - * @device: A #GUdevDevice. - * - * Gets the immediate parent of @device, if any. - * - * Returns: (transfer full): A #GUdevDevice or %NULL if @device has no parent. Free with g_object_unref(). - */ -GUdevDevice * -g_udev_device_get_parent (GUdevDevice *device) -{ - GUdevDevice *ret; - struct udev_device *udevice; - - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); - - ret = NULL; - - udevice = udev_device_get_parent (device->priv->udevice); - if (udevice == NULL) - goto out; - - ret = _g_udev_device_new (udevice); - - out: - return ret; -} - -/** - * g_udev_device_get_parent_with_subsystem: - * @device: A #GUdevDevice. - * @subsystem: The subsystem of the parent to get. - * @devtype: (allow-none): The devtype of the parent to get or %NULL. - * - * Walks up the chain of parents of @device and returns the first - * device encountered where @subsystem and @devtype matches, if any. - * - * Returns: (transfer full): A #GUdevDevice or %NULL if @device has no parent with @subsystem and @devtype. Free with g_object_unref(). - */ -GUdevDevice * -g_udev_device_get_parent_with_subsystem (GUdevDevice *device, - const gchar *subsystem, - const gchar *devtype) -{ - GUdevDevice *ret; - struct udev_device *udevice; - - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); - g_return_val_if_fail (subsystem != NULL, NULL); - - ret = NULL; - - udevice = udev_device_get_parent_with_subsystem_devtype (device->priv->udevice, - subsystem, - devtype); - if (udevice == NULL) - goto out; - - ret = _g_udev_device_new (udevice); - - out: - return ret; -} - -/* ---------------------------------------------------------------------------------------------------- */ - -/** - * g_udev_device_get_property_keys: - * @device: A #GUdevDevice. - * - * Gets all keys for properties on @device. - * - * Returns: (transfer none) (array zero-terminated=1) (element-type utf8): A %NULL terminated string array of property keys. This array is owned by @device and should not be freed by the caller. - */ -const gchar* const * -g_udev_device_get_property_keys (GUdevDevice *device) -{ - struct udev_list_entry *l; - GPtrArray *p; - - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); - - if (device->priv->property_keys != NULL) - goto out; - - p = g_ptr_array_new (); - for (l = udev_device_get_properties_list_entry (device->priv->udevice); l != NULL; l = udev_list_entry_get_next (l)) - { - g_ptr_array_add (p, g_strdup (udev_list_entry_get_name (l))); - } - g_ptr_array_add (p, NULL); - device->priv->property_keys = (gchar **) g_ptr_array_free (p, FALSE); - - out: - return (const gchar * const *) device->priv->property_keys; -} - - -/** - * g_udev_device_has_property: - * @device: A #GUdevDevice. - * @key: Name of property. - * - * Check if a the property with the given key exists. - * - * Returns: %TRUE only if the value for @key exist. - */ -gboolean -g_udev_device_has_property (GUdevDevice *device, - const gchar *key) -{ - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), FALSE); - g_return_val_if_fail (key != NULL, FALSE); - return udev_device_get_property_value (device->priv->udevice, key) != NULL; -} - -/** - * g_udev_device_get_property: - * @device: A #GUdevDevice. - * @key: Name of property. - * - * Look up the value for @key on @device. - * - * Returns: The value for @key or %NULL if @key doesn't exist on @device. Do not free this string, it is owned by @device. - */ -const gchar * -g_udev_device_get_property (GUdevDevice *device, - const gchar *key) -{ - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); - g_return_val_if_fail (key != NULL, NULL); - return udev_device_get_property_value (device->priv->udevice, key); -} - -/** - * g_udev_device_get_property_as_int: - * @device: A #GUdevDevice. - * @key: Name of property. - * - * Look up the value for @key on @device and convert it to an integer - * using strtol(). - * - * Returns: The value for @key or 0 if @key doesn't exist or - * isn't an integer. - */ -gint -g_udev_device_get_property_as_int (GUdevDevice *device, - const gchar *key) -{ - gint result; - const gchar *s; - - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0); - g_return_val_if_fail (key != NULL, 0); - - result = 0; - s = g_udev_device_get_property (device, key); - if (s == NULL) - goto out; - - result = strtol (s, NULL, 0); -out: - return result; -} - -/** - * g_udev_device_get_property_as_uint64: - * @device: A #GUdevDevice. - * @key: Name of property. - * - * Look up the value for @key on @device and convert it to an unsigned - * 64-bit integer using g_ascii_strtoull(). - * - * Returns: The value for @key or 0 if @key doesn't exist or isn't a - * #guint64. - */ -guint64 -g_udev_device_get_property_as_uint64 (GUdevDevice *device, - const gchar *key) -{ - guint64 result; - const gchar *s; - - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0); - g_return_val_if_fail (key != NULL, 0); - - result = 0; - s = g_udev_device_get_property (device, key); - if (s == NULL) - goto out; - - result = g_ascii_strtoull (s, NULL, 0); -out: - return result; -} - -/** - * g_udev_device_get_property_as_double: - * @device: A #GUdevDevice. - * @key: Name of property. - * - * Look up the value for @key on @device and convert it to a double - * precision floating point number using strtod(). - * - * Returns: The value for @key or 0.0 if @key doesn't exist or isn't a - * #gdouble. - */ -gdouble -g_udev_device_get_property_as_double (GUdevDevice *device, - const gchar *key) -{ - gdouble result; - const gchar *s; - - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0.0); - g_return_val_if_fail (key != NULL, 0.0); - - result = 0.0; - s = g_udev_device_get_property (device, key); - if (s == NULL) - goto out; - - result = strtod (s, NULL); -out: - return result; -} - -/** - * g_udev_device_get_property_as_boolean: - * @device: A #GUdevDevice. - * @key: Name of property. - * - * Look up the value for @key on @device and convert it to an - * boolean. This is done by doing a case-insensitive string comparison - * on the string value against "1" and "true". - * - * Returns: The value for @key or %FALSE if @key doesn't exist or - * isn't a #gboolean. - */ -gboolean -g_udev_device_get_property_as_boolean (GUdevDevice *device, - const gchar *key) -{ - gboolean result; - const gchar *s; - - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), FALSE); - g_return_val_if_fail (key != NULL, FALSE); - - result = FALSE; - s = g_udev_device_get_property (device, key); - if (s == NULL) - goto out; - - if (strcmp (s, "1") == 0 || g_ascii_strcasecmp (s, "true") == 0) - result = TRUE; - out: - return result; -} - -static gchar ** -split_at_whitespace (const gchar *s) -{ - gchar **result; - guint n; - guint m; - - result = g_strsplit_set (s, " \v\t\r\n", 0); - - /* remove empty strings, thanks GLib */ - for (n = 0; result[n] != NULL; n++) - { - if (strlen (result[n]) == 0) - { - g_free (result[n]); - for (m = n; result[m] != NULL; m++) - result[m] = result[m + 1]; - n--; - } - } - - return result; -} - -/** - * g_udev_device_get_property_as_strv: - * @device: A #GUdevDevice. - * @key: Name of property. - * - * Look up the value for @key on @device and return the result of - * splitting it into non-empty tokens split at white space (only space - * (' '), form-feed ('\f'), newline ('\n'), carriage return ('\r'), - * horizontal tab ('\t'), and vertical tab ('\v') are considered; the - * locale is not taken into account). - * - * Returns: (transfer none) (array zero-terminated=1) (element-type utf8): The value of @key on @device split into tokens or %NULL if @key doesn't exist. This array is owned by @device and should not be freed by the caller. - */ -const gchar* const * -g_udev_device_get_property_as_strv (GUdevDevice *device, - const gchar *key) -{ - gchar **result; - const gchar *s; - - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); - g_return_val_if_fail (key != NULL, NULL); - - if (device->priv->prop_strvs != NULL) - { - result = g_hash_table_lookup (device->priv->prop_strvs, key); - if (result != NULL) - goto out; - } - - result = NULL; - s = g_udev_device_get_property (device, key); - if (s == NULL) - goto out; - - result = split_at_whitespace (s); - if (result == NULL) - goto out; - - if (device->priv->prop_strvs == NULL) - device->priv->prop_strvs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_strfreev); - g_hash_table_insert (device->priv->prop_strvs, g_strdup (key), result); - -out: - return (const gchar* const *) result; -} - -/* ---------------------------------------------------------------------------------------------------- */ - -/** - * g_udev_device_get_sysfs_attr: - * @device: A #GUdevDevice. - * @name: Name of the sysfs attribute. - * - * Look up the sysfs attribute with @name on @device. - * - * Returns: The value of the sysfs attribute or %NULL if there is no - * such attribute. Do not free this string, it is owned by @device. - */ -const gchar * -g_udev_device_get_sysfs_attr (GUdevDevice *device, - const gchar *name) -{ - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); - g_return_val_if_fail (name != NULL, NULL); - return udev_device_get_sysattr_value (device->priv->udevice, name); -} - -/** - * g_udev_device_get_sysfs_attr_as_int: - * @device: A #GUdevDevice. - * @name: Name of the sysfs attribute. - * - * Look up the sysfs attribute with @name on @device and convert it to an integer - * using strtol(). - * - * Returns: The value of the sysfs attribute or 0 if there is no such - * attribute. - */ -gint -g_udev_device_get_sysfs_attr_as_int (GUdevDevice *device, - const gchar *name) -{ - gint result; - const gchar *s; - - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0); - g_return_val_if_fail (name != NULL, 0); - - result = 0; - s = g_udev_device_get_sysfs_attr (device, name); - if (s == NULL) - goto out; - - result = strtol (s, NULL, 0); -out: - return result; -} - -/** - * g_udev_device_get_sysfs_attr_as_uint64: - * @device: A #GUdevDevice. - * @name: Name of the sysfs attribute. - * - * Look up the sysfs attribute with @name on @device and convert it to an unsigned - * 64-bit integer using g_ascii_strtoull(). - * - * Returns: The value of the sysfs attribute or 0 if there is no such - * attribute. - */ -guint64 -g_udev_device_get_sysfs_attr_as_uint64 (GUdevDevice *device, - const gchar *name) -{ - guint64 result; - const gchar *s; - - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0); - g_return_val_if_fail (name != NULL, 0); - - result = 0; - s = g_udev_device_get_sysfs_attr (device, name); - if (s == NULL) - goto out; - - result = g_ascii_strtoull (s, NULL, 0); -out: - return result; -} - -/** - * g_udev_device_get_sysfs_attr_as_double: - * @device: A #GUdevDevice. - * @name: Name of the sysfs attribute. - * - * Look up the sysfs attribute with @name on @device and convert it to a double - * precision floating point number using strtod(). - * - * Returns: The value of the sysfs attribute or 0.0 if there is no such - * attribute. - */ -gdouble -g_udev_device_get_sysfs_attr_as_double (GUdevDevice *device, - const gchar *name) -{ - gdouble result; - const gchar *s; - - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0.0); - g_return_val_if_fail (name != NULL, 0.0); - - result = 0.0; - s = g_udev_device_get_sysfs_attr (device, name); - if (s == NULL) - goto out; - - result = strtod (s, NULL); -out: - return result; -} - -/** - * g_udev_device_get_sysfs_attr_as_boolean: - * @device: A #GUdevDevice. - * @name: Name of the sysfs attribute. - * - * Look up the sysfs attribute with @name on @device and convert it to an - * boolean. This is done by doing a case-insensitive string comparison - * on the string value against "1" and "true". - * - * Returns: The value of the sysfs attribute or %FALSE if there is no such - * attribute. - */ -gboolean -g_udev_device_get_sysfs_attr_as_boolean (GUdevDevice *device, - const gchar *name) -{ - gboolean result; - const gchar *s; - - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), FALSE); - g_return_val_if_fail (name != NULL, FALSE); - - result = FALSE; - s = g_udev_device_get_sysfs_attr (device, name); - if (s == NULL) - goto out; - - if (strcmp (s, "1") == 0 || g_ascii_strcasecmp (s, "true") == 0) - result = TRUE; - out: - return result; -} - -/** - * g_udev_device_get_sysfs_attr_as_strv: - * @device: A #GUdevDevice. - * @name: Name of the sysfs attribute. - * - * Look up the sysfs attribute with @name on @device and return the result of - * splitting it into non-empty tokens split at white space (only space (' '), - * form-feed ('\f'), newline ('\n'), carriage return ('\r'), horizontal - * tab ('\t'), and vertical tab ('\v') are considered; the locale is - * not taken into account). - * - * Returns: (transfer none) (array zero-terminated=1) (element-type utf8): The value of the sysfs attribute split into tokens or %NULL if there is no such attribute. This array is owned by @device and should not be freed by the caller. - */ -const gchar * const * -g_udev_device_get_sysfs_attr_as_strv (GUdevDevice *device, - const gchar *name) -{ - gchar **result; - const gchar *s; - - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); - g_return_val_if_fail (name != NULL, NULL); - - if (device->priv->sysfs_attr_strvs != NULL) - { - result = g_hash_table_lookup (device->priv->sysfs_attr_strvs, name); - if (result != NULL) - goto out; - } - - result = NULL; - s = g_udev_device_get_sysfs_attr (device, name); - if (s == NULL) - goto out; - - result = split_at_whitespace (s); - if (result == NULL) - goto out; - - if (device->priv->sysfs_attr_strvs == NULL) - device->priv->sysfs_attr_strvs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_strfreev); - g_hash_table_insert (device->priv->sysfs_attr_strvs, g_strdup (name), result); - -out: - return (const gchar* const *) result; -} - -/** - * g_udev_device_get_tags: - * @device: A #GUdevDevice. - * - * Gets all tags for @device. - * - * Returns: (transfer none) (array zero-terminated=1) (element-type utf8): A %NULL terminated string array of tags. This array is owned by @device and should not be freed by the caller. - * - * Since: 165 - */ -const gchar* const * -g_udev_device_get_tags (GUdevDevice *device) -{ - struct udev_list_entry *l; - GPtrArray *p; - - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL); - - if (device->priv->tags != NULL) - goto out; - - p = g_ptr_array_new (); - for (l = udev_device_get_tags_list_entry (device->priv->udevice); l != NULL; l = udev_list_entry_get_next (l)) - { - g_ptr_array_add (p, g_strdup (udev_list_entry_get_name (l))); - } - g_ptr_array_add (p, NULL); - device->priv->tags = (gchar **) g_ptr_array_free (p, FALSE); - - out: - return (const gchar * const *) device->priv->tags; -} - -/** - * g_udev_device_get_is_initialized: - * @device: A #GUdevDevice. - * - * Gets whether @device has been initalized. - * - * Returns: Whether @device has been initialized. - * - * Since: 165 - */ -gboolean -g_udev_device_get_is_initialized (GUdevDevice *device) -{ - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), FALSE); - return udev_device_get_is_initialized (device->priv->udevice); -} - -/** - * g_udev_device_get_usec_since_initialized: - * @device: A #GUdevDevice. - * - * Gets number of micro-seconds since @device was initialized. - * - * This only works for devices with properties in the udev - * database. All other devices return 0. - * - * Returns: Number of micro-seconds since @device was initialized or 0 if unknown. - * - * Since: 165 - */ -guint64 -g_udev_device_get_usec_since_initialized (GUdevDevice *device) -{ - g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0); - return udev_device_get_usec_since_initialized (device->priv->udevice); -} diff --git a/src/udev/src/gudev/gudevdevice.h b/src/udev/src/gudev/gudevdevice.h deleted file mode 100644 index d4873bad0f..0000000000 --- a/src/udev/src/gudev/gudevdevice.h +++ /dev/null @@ -1,128 +0,0 @@ -/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- - * - * Copyright (C) 2008 David Zeuthen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#if !defined (_GUDEV_COMPILATION) && !defined(_GUDEV_INSIDE_GUDEV_H) -#error "Only can be included directly, this file may disappear or change contents." -#endif - -#ifndef __G_UDEV_DEVICE_H__ -#define __G_UDEV_DEVICE_H__ - -#include - -G_BEGIN_DECLS - -#define G_UDEV_TYPE_DEVICE (g_udev_device_get_type ()) -#define G_UDEV_DEVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_UDEV_TYPE_DEVICE, GUdevDevice)) -#define G_UDEV_DEVICE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_UDEV_TYPE_DEVICE, GUdevDeviceClass)) -#define G_UDEV_IS_DEVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_UDEV_TYPE_DEVICE)) -#define G_UDEV_IS_DEVICE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_UDEV_TYPE_DEVICE)) -#define G_UDEV_DEVICE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_UDEV_TYPE_DEVICE, GUdevDeviceClass)) - -typedef struct _GUdevDeviceClass GUdevDeviceClass; -typedef struct _GUdevDevicePrivate GUdevDevicePrivate; - -/** - * GUdevDevice: - * - * The #GUdevDevice struct is opaque and should not be accessed directly. - */ -struct _GUdevDevice -{ - GObject parent; - - /*< private >*/ - GUdevDevicePrivate *priv; -}; - -/** - * GUdevDeviceClass: - * @parent_class: Parent class. - * - * Class structure for #GUdevDevice. - */ -struct _GUdevDeviceClass -{ - GObjectClass parent_class; - - /*< private >*/ - /* Padding for future expansion */ - void (*reserved1) (void); - void (*reserved2) (void); - void (*reserved3) (void); - void (*reserved4) (void); - void (*reserved5) (void); - void (*reserved6) (void); - void (*reserved7) (void); - void (*reserved8) (void); -}; - -GType g_udev_device_get_type (void) G_GNUC_CONST; -gboolean g_udev_device_get_is_initialized (GUdevDevice *device); -guint64 g_udev_device_get_usec_since_initialized (GUdevDevice *device); -const gchar *g_udev_device_get_subsystem (GUdevDevice *device); -const gchar *g_udev_device_get_devtype (GUdevDevice *device); -const gchar *g_udev_device_get_name (GUdevDevice *device); -const gchar *g_udev_device_get_number (GUdevDevice *device); -const gchar *g_udev_device_get_sysfs_path (GUdevDevice *device); -const gchar *g_udev_device_get_driver (GUdevDevice *device); -const gchar *g_udev_device_get_action (GUdevDevice *device); -guint64 g_udev_device_get_seqnum (GUdevDevice *device); -GUdevDeviceType g_udev_device_get_device_type (GUdevDevice *device); -GUdevDeviceNumber g_udev_device_get_device_number (GUdevDevice *device); -const gchar *g_udev_device_get_device_file (GUdevDevice *device); -const gchar* const *g_udev_device_get_device_file_symlinks (GUdevDevice *device); -GUdevDevice *g_udev_device_get_parent (GUdevDevice *device); -GUdevDevice *g_udev_device_get_parent_with_subsystem (GUdevDevice *device, - const gchar *subsystem, - const gchar *devtype); -const gchar* const *g_udev_device_get_property_keys (GUdevDevice *device); -gboolean g_udev_device_has_property (GUdevDevice *device, - const gchar *key); -const gchar *g_udev_device_get_property (GUdevDevice *device, - const gchar *key); -gint g_udev_device_get_property_as_int (GUdevDevice *device, - const gchar *key); -guint64 g_udev_device_get_property_as_uint64 (GUdevDevice *device, - const gchar *key); -gdouble g_udev_device_get_property_as_double (GUdevDevice *device, - const gchar *key); -gboolean g_udev_device_get_property_as_boolean (GUdevDevice *device, - const gchar *key); -const gchar* const *g_udev_device_get_property_as_strv (GUdevDevice *device, - const gchar *key); - -const gchar *g_udev_device_get_sysfs_attr (GUdevDevice *device, - const gchar *name); -gint g_udev_device_get_sysfs_attr_as_int (GUdevDevice *device, - const gchar *name); -guint64 g_udev_device_get_sysfs_attr_as_uint64 (GUdevDevice *device, - const gchar *name); -gdouble g_udev_device_get_sysfs_attr_as_double (GUdevDevice *device, - const gchar *name); -gboolean g_udev_device_get_sysfs_attr_as_boolean (GUdevDevice *device, - const gchar *name); -const gchar* const *g_udev_device_get_sysfs_attr_as_strv (GUdevDevice *device, - const gchar *name); -const gchar* const *g_udev_device_get_tags (GUdevDevice *device); - -G_END_DECLS - -#endif /* __G_UDEV_DEVICE_H__ */ diff --git a/src/udev/src/gudev/gudevenumerator.c b/src/udev/src/gudev/gudevenumerator.c deleted file mode 100644 index db09074625..0000000000 --- a/src/udev/src/gudev/gudevenumerator.c +++ /dev/null @@ -1,431 +0,0 @@ -/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- - * - * Copyright (C) 2008-2010 David Zeuthen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - * - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include -#include - -#include "gudevclient.h" -#include "gudevenumerator.h" -#include "gudevdevice.h" -#include "gudevmarshal.h" -#include "gudevprivate.h" - -/** - * SECTION:gudevenumerator - * @short_description: Lookup and sort devices - * - * #GUdevEnumerator is used to lookup and sort devices. - * - * Since: 165 - */ - -struct _GUdevEnumeratorPrivate -{ - GUdevClient *client; - struct udev_enumerate *e; -}; - -enum -{ - PROP_0, - PROP_CLIENT, -}; - -G_DEFINE_TYPE (GUdevEnumerator, g_udev_enumerator, G_TYPE_OBJECT) - -/* ---------------------------------------------------------------------------------------------------- */ - -static void -g_udev_enumerator_finalize (GObject *object) -{ - GUdevEnumerator *enumerator = G_UDEV_ENUMERATOR (object); - - if (enumerator->priv->client != NULL) - { - g_object_unref (enumerator->priv->client); - enumerator->priv->client = NULL; - } - - if (enumerator->priv->e != NULL) - { - udev_enumerate_unref (enumerator->priv->e); - enumerator->priv->e = NULL; - } - - if (G_OBJECT_CLASS (g_udev_enumerator_parent_class)->finalize != NULL) - G_OBJECT_CLASS (g_udev_enumerator_parent_class)->finalize (object); -} - -static void -g_udev_enumerator_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GUdevEnumerator *enumerator = G_UDEV_ENUMERATOR (object); - - switch (prop_id) - { - case PROP_CLIENT: - if (enumerator->priv->client != NULL) - g_object_unref (enumerator->priv->client); - enumerator->priv->client = g_value_dup_object (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -g_udev_enumerator_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GUdevEnumerator *enumerator = G_UDEV_ENUMERATOR (object); - - switch (prop_id) - { - case PROP_CLIENT: - g_value_set_object (value, enumerator->priv->client); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -g_udev_enumerator_constructed (GObject *object) -{ - GUdevEnumerator *enumerator = G_UDEV_ENUMERATOR (object); - - g_assert (G_UDEV_IS_CLIENT (enumerator->priv->client)); - - enumerator->priv->e = udev_enumerate_new (_g_udev_client_get_udev (enumerator->priv->client)); - - if (G_OBJECT_CLASS (g_udev_enumerator_parent_class)->constructed != NULL) - G_OBJECT_CLASS (g_udev_enumerator_parent_class)->constructed (object); -} - -static void -g_udev_enumerator_class_init (GUdevEnumeratorClass *klass) -{ - GObjectClass *gobject_class = (GObjectClass *) klass; - - gobject_class->finalize = g_udev_enumerator_finalize; - gobject_class->set_property = g_udev_enumerator_set_property; - gobject_class->get_property = g_udev_enumerator_get_property; - gobject_class->constructed = g_udev_enumerator_constructed; - - /** - * GUdevEnumerator:client: - * - * The #GUdevClient to enumerate devices from. - * - * Since: 165 - */ - g_object_class_install_property (gobject_class, - PROP_CLIENT, - g_param_spec_object ("client", - "The client to enumerate devices from", - "The client to enumerate devices from", - G_UDEV_TYPE_CLIENT, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE)); - - g_type_class_add_private (klass, sizeof (GUdevEnumeratorPrivate)); -} - -static void -g_udev_enumerator_init (GUdevEnumerator *enumerator) -{ - enumerator->priv = G_TYPE_INSTANCE_GET_PRIVATE (enumerator, - G_UDEV_TYPE_ENUMERATOR, - GUdevEnumeratorPrivate); -} - -/** - * g_udev_enumerator_new: - * @client: A #GUdevClient to enumerate devices from. - * - * Constructs a #GUdevEnumerator object that can be used to enumerate - * and sort devices. Use the add_match_*() and add_nomatch_*() methods - * and execute the query to get a list of devices with - * g_udev_enumerator_execute(). - * - * Returns: A new #GUdevEnumerator object. Free with g_object_unref(). - * - * Since: 165 - */ -GUdevEnumerator * -g_udev_enumerator_new (GUdevClient *client) -{ - g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL); - return G_UDEV_ENUMERATOR (g_object_new (G_UDEV_TYPE_ENUMERATOR, "client", client, NULL)); -} - - -/** - * g_udev_enumerator_add_match_subsystem: - * @enumerator: A #GUdevEnumerator. - * @subsystem: Wildcard for subsystem name e.g. 'scsi' or 'a*'. - * - * All returned devices will match the given @subsystem. - * - * Returns: (transfer none): The passed in @enumerator. - * - * Since: 165 - */ -GUdevEnumerator * -g_udev_enumerator_add_match_subsystem (GUdevEnumerator *enumerator, - const gchar *subsystem) -{ - g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL); - g_return_val_if_fail (subsystem != NULL, NULL); - udev_enumerate_add_match_subsystem (enumerator->priv->e, subsystem); - return enumerator; -} - -/** - * g_udev_enumerator_add_nomatch_subsystem: - * @enumerator: A #GUdevEnumerator. - * @subsystem: Wildcard for subsystem name e.g. 'scsi' or 'a*'. - * - * All returned devices will not match the given @subsystem. - * - * Returns: (transfer none): The passed in @enumerator. - * - * Since: 165 - */ -GUdevEnumerator * -g_udev_enumerator_add_nomatch_subsystem (GUdevEnumerator *enumerator, - const gchar *subsystem) -{ - g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL); - g_return_val_if_fail (subsystem != NULL, NULL); - udev_enumerate_add_nomatch_subsystem (enumerator->priv->e, subsystem); - return enumerator; -} - -/** - * g_udev_enumerator_add_match_sysfs_attr: - * @enumerator: A #GUdevEnumerator. - * @name: Wildcard filter for sysfs attribute key. - * @value: Wildcard filter for sysfs attribute value. - * - * All returned devices will have a sysfs attribute matching the given @name and @value. - * - * Returns: (transfer none): The passed in @enumerator. - * - * Since: 165 - */ -GUdevEnumerator * -g_udev_enumerator_add_match_sysfs_attr (GUdevEnumerator *enumerator, - const gchar *name, - const gchar *value) -{ - g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL); - g_return_val_if_fail (name != NULL, NULL); - g_return_val_if_fail (value != NULL, NULL); - udev_enumerate_add_match_sysattr (enumerator->priv->e, name, value); - return enumerator; -} - -/** - * g_udev_enumerator_add_nomatch_sysfs_attr: - * @enumerator: A #GUdevEnumerator. - * @name: Wildcard filter for sysfs attribute key. - * @value: Wildcard filter for sysfs attribute value. - * - * All returned devices will not have a sysfs attribute matching the given @name and @value. - * - * Returns: (transfer none): The passed in @enumerator. - * - * Since: 165 - */ -GUdevEnumerator * -g_udev_enumerator_add_nomatch_sysfs_attr (GUdevEnumerator *enumerator, - const gchar *name, - const gchar *value) -{ - g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL); - g_return_val_if_fail (name != NULL, NULL); - g_return_val_if_fail (value != NULL, NULL); - udev_enumerate_add_nomatch_sysattr (enumerator->priv->e, name, value); - return enumerator; -} - -/** - * g_udev_enumerator_add_match_property: - * @enumerator: A #GUdevEnumerator. - * @name: Wildcard filter for property name. - * @value: Wildcard filter for property value. - * - * All returned devices will have a property matching the given @name and @value. - * - * Returns: (transfer none): The passed in @enumerator. - * - * Since: 165 - */ -GUdevEnumerator * -g_udev_enumerator_add_match_property (GUdevEnumerator *enumerator, - const gchar *name, - const gchar *value) -{ - g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL); - g_return_val_if_fail (name != NULL, NULL); - g_return_val_if_fail (value != NULL, NULL); - udev_enumerate_add_match_property (enumerator->priv->e, name, value); - return enumerator; -} - -/** - * g_udev_enumerator_add_match_name: - * @enumerator: A #GUdevEnumerator. - * @name: Wildcard filter for kernel name e.g. "sda*". - * - * All returned devices will match the given @name. - * - * Returns: (transfer none): The passed in @enumerator. - * - * Since: 165 - */ -GUdevEnumerator * -g_udev_enumerator_add_match_name (GUdevEnumerator *enumerator, - const gchar *name) -{ - g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL); - g_return_val_if_fail (name != NULL, NULL); - udev_enumerate_add_match_sysname (enumerator->priv->e, name); - return enumerator; -} - -/** - * g_udev_enumerator_add_sysfs_path: - * @enumerator: A #GUdevEnumerator. - * @sysfs_path: A sysfs path, e.g. "/sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda" - * - * Add a device to the list of devices, to retrieve it back sorted in dependency order. - * - * Returns: (transfer none): The passed in @enumerator. - * - * Since: 165 - */ -GUdevEnumerator * -g_udev_enumerator_add_sysfs_path (GUdevEnumerator *enumerator, - const gchar *sysfs_path) -{ - g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL); - g_return_val_if_fail (sysfs_path != NULL, NULL); - udev_enumerate_add_syspath (enumerator->priv->e, sysfs_path); - return enumerator; -} - -/** - * g_udev_enumerator_add_match_tag: - * @enumerator: A #GUdevEnumerator. - * @tag: A udev tag e.g. "udev-acl". - * - * All returned devices will match the given @tag. - * - * Returns: (transfer none): The passed in @enumerator. - * - * Since: 165 - */ -GUdevEnumerator * -g_udev_enumerator_add_match_tag (GUdevEnumerator *enumerator, - const gchar *tag) -{ - g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL); - g_return_val_if_fail (tag != NULL, NULL); - udev_enumerate_add_match_tag (enumerator->priv->e, tag); - return enumerator; -} - -/** - * g_udev_enumerator_add_match_is_initialized: - * @enumerator: A #GUdevEnumerator. - * - * All returned devices will be initialized. - * - * Returns: (transfer none): The passed in @enumerator. - * - * Since: 165 - */ -GUdevEnumerator * -g_udev_enumerator_add_match_is_initialized (GUdevEnumerator *enumerator) -{ - g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL); - udev_enumerate_add_match_is_initialized (enumerator->priv->e); - return enumerator; -} - -/** - * g_udev_enumerator_execute: - * @enumerator: A #GUdevEnumerator. - * - * Executes the query in @enumerator. - * - * Returns: (element-type GUdevDevice) (transfer full): A list of #GUdevDevice objects. The caller should free the result by using g_object_unref() on each element in the list and then g_list_free() on the list. - * - * Since: 165 - */ -GList * -g_udev_enumerator_execute (GUdevEnumerator *enumerator) -{ - GList *ret; - struct udev_list_entry *l, *devices; - - g_return_val_if_fail (G_UDEV_IS_ENUMERATOR (enumerator), NULL); - - ret = NULL; - - /* retrieve the list */ - udev_enumerate_scan_devices (enumerator->priv->e); - - devices = udev_enumerate_get_list_entry (enumerator->priv->e); - for (l = devices; l != NULL; l = udev_list_entry_get_next (l)) - { - struct udev_device *udevice; - GUdevDevice *device; - - udevice = udev_device_new_from_syspath (udev_enumerate_get_udev (enumerator->priv->e), - udev_list_entry_get_name (l)); - if (udevice == NULL) - continue; - - device = _g_udev_device_new (udevice); - udev_device_unref (udevice); - ret = g_list_prepend (ret, device); - } - - ret = g_list_reverse (ret); - - return ret; -} diff --git a/src/udev/src/gudev/gudevenumerator.h b/src/udev/src/gudev/gudevenumerator.h deleted file mode 100644 index 3fddccf573..0000000000 --- a/src/udev/src/gudev/gudevenumerator.h +++ /dev/null @@ -1,107 +0,0 @@ -/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- - * - * Copyright (C) 2008-2010 David Zeuthen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#if !defined (_GUDEV_COMPILATION) && !defined(_GUDEV_INSIDE_GUDEV_H) -#error "Only can be included directly, this file may disappear or change contents." -#endif - -#ifndef __G_UDEV_ENUMERATOR_H__ -#define __G_UDEV_ENUMERATOR_H__ - -#include - -G_BEGIN_DECLS - -#define G_UDEV_TYPE_ENUMERATOR (g_udev_enumerator_get_type ()) -#define G_UDEV_ENUMERATOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_UDEV_TYPE_ENUMERATOR, GUdevEnumerator)) -#define G_UDEV_ENUMERATOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_UDEV_TYPE_ENUMERATOR, GUdevEnumeratorClass)) -#define G_UDEV_IS_ENUMERATOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_UDEV_TYPE_ENUMERATOR)) -#define G_UDEV_IS_ENUMERATOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_UDEV_TYPE_ENUMERATOR)) -#define G_UDEV_ENUMERATOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_UDEV_TYPE_ENUMERATOR, GUdevEnumeratorClass)) - -typedef struct _GUdevEnumeratorClass GUdevEnumeratorClass; -typedef struct _GUdevEnumeratorPrivate GUdevEnumeratorPrivate; - -/** - * GUdevEnumerator: - * - * The #GUdevEnumerator struct is opaque and should not be accessed directly. - * - * Since: 165 - */ -struct _GUdevEnumerator -{ - GObject parent; - - /*< private >*/ - GUdevEnumeratorPrivate *priv; -}; - -/** - * GUdevEnumeratorClass: - * @parent_class: Parent class. - * - * Class structure for #GUdevEnumerator. - * - * Since: 165 - */ -struct _GUdevEnumeratorClass -{ - GObjectClass parent_class; - - /*< private >*/ - /* Padding for future expansion */ - void (*reserved1) (void); - void (*reserved2) (void); - void (*reserved3) (void); - void (*reserved4) (void); - void (*reserved5) (void); - void (*reserved6) (void); - void (*reserved7) (void); - void (*reserved8) (void); -}; - -GType g_udev_enumerator_get_type (void) G_GNUC_CONST; -GUdevEnumerator *g_udev_enumerator_new (GUdevClient *client); -GUdevEnumerator *g_udev_enumerator_add_match_subsystem (GUdevEnumerator *enumerator, - const gchar *subsystem); -GUdevEnumerator *g_udev_enumerator_add_nomatch_subsystem (GUdevEnumerator *enumerator, - const gchar *subsystem); -GUdevEnumerator *g_udev_enumerator_add_match_sysfs_attr (GUdevEnumerator *enumerator, - const gchar *name, - const gchar *value); -GUdevEnumerator *g_udev_enumerator_add_nomatch_sysfs_attr (GUdevEnumerator *enumerator, - const gchar *name, - const gchar *value); -GUdevEnumerator *g_udev_enumerator_add_match_property (GUdevEnumerator *enumerator, - const gchar *name, - const gchar *value); -GUdevEnumerator *g_udev_enumerator_add_match_name (GUdevEnumerator *enumerator, - const gchar *name); -GUdevEnumerator *g_udev_enumerator_add_match_tag (GUdevEnumerator *enumerator, - const gchar *tag); -GUdevEnumerator *g_udev_enumerator_add_match_is_initialized (GUdevEnumerator *enumerator); -GUdevEnumerator *g_udev_enumerator_add_sysfs_path (GUdevEnumerator *enumerator, - const gchar *sysfs_path); -GList *g_udev_enumerator_execute (GUdevEnumerator *enumerator); - -G_END_DECLS - -#endif /* __G_UDEV_ENUMERATOR_H__ */ diff --git a/src/udev/src/gudev/gudevenums.h b/src/udev/src/gudev/gudevenums.h deleted file mode 100644 index c3a0aa8747..0000000000 --- a/src/udev/src/gudev/gudevenums.h +++ /dev/null @@ -1,49 +0,0 @@ -/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- - * - * Copyright (C) 2008 David Zeuthen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#if !defined (_GUDEV_COMPILATION) && !defined(_GUDEV_INSIDE_GUDEV_H) -#error "Only can be included directly, this file may disappear or change contents." -#endif - -#ifndef __G_UDEV_ENUMS_H__ -#define __G_UDEV_ENUMS_H__ - -#include - -G_BEGIN_DECLS - -/** - * GUdevDeviceType: - * @G_UDEV_DEVICE_TYPE_NONE: Device does not have a device file. - * @G_UDEV_DEVICE_TYPE_BLOCK: Device is a block device. - * @G_UDEV_DEVICE_TYPE_CHAR: Device is a character device. - * - * Enumeration used to specify a the type of a device. - */ -typedef enum -{ - G_UDEV_DEVICE_TYPE_NONE = 0, - G_UDEV_DEVICE_TYPE_BLOCK = 'b', - G_UDEV_DEVICE_TYPE_CHAR = 'c', -} GUdevDeviceType; - -G_END_DECLS - -#endif /* __G_UDEV_ENUMS_H__ */ diff --git a/src/udev/src/gudev/gudevenumtypes.c.template b/src/udev/src/gudev/gudevenumtypes.c.template deleted file mode 100644 index fc30b39e2e..0000000000 --- a/src/udev/src/gudev/gudevenumtypes.c.template +++ /dev/null @@ -1,39 +0,0 @@ -/*** BEGIN file-header ***/ -#include - -/*** END file-header ***/ - -/*** BEGIN file-production ***/ -/* enumerations from "@filename@" */ -/*** END file-production ***/ - -/*** BEGIN value-header ***/ -GType -@enum_name@_get_type (void) -{ - static volatile gsize g_define_type_id__volatile = 0; - - if (g_once_init_enter (&g_define_type_id__volatile)) - { - static const G@Type@Value values[] = { -/*** END value-header ***/ - -/*** BEGIN value-production ***/ - { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, -/*** END value-production ***/ - -/*** BEGIN value-tail ***/ - { 0, NULL, NULL } - }; - GType g_define_type_id = - g_@type@_register_static (g_intern_static_string ("@EnumName@"), values); - g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); - } - - return g_define_type_id__volatile; -} - -/*** END value-tail ***/ - -/*** BEGIN file-tail ***/ -/*** END file-tail ***/ diff --git a/src/udev/src/gudev/gudevenumtypes.h.template b/src/udev/src/gudev/gudevenumtypes.h.template deleted file mode 100644 index d0ab3393e6..0000000000 --- a/src/udev/src/gudev/gudevenumtypes.h.template +++ /dev/null @@ -1,24 +0,0 @@ -/*** BEGIN file-header ***/ -#ifndef __GUDEV_ENUM_TYPES_H__ -#define __GUDEV_ENUM_TYPES_H__ - -#include - -G_BEGIN_DECLS -/*** END file-header ***/ - -/*** BEGIN file-production ***/ - -/* enumerations from "@filename@" */ -/*** END file-production ***/ - -/*** BEGIN value-header ***/ -GType @enum_name@_get_type (void) G_GNUC_CONST; -#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ()) -/*** END value-header ***/ - -/*** BEGIN file-tail ***/ -G_END_DECLS - -#endif /* __GUDEV_ENUM_TYPES_H__ */ -/*** END file-tail ***/ diff --git a/src/udev/src/gudev/gudevmarshal.list b/src/udev/src/gudev/gudevmarshal.list deleted file mode 100644 index 7e665999e8..0000000000 --- a/src/udev/src/gudev/gudevmarshal.list +++ /dev/null @@ -1 +0,0 @@ -VOID:STRING,OBJECT diff --git a/src/udev/src/gudev/gudevprivate.h b/src/udev/src/gudev/gudevprivate.h deleted file mode 100644 index 8866f52b88..0000000000 --- a/src/udev/src/gudev/gudevprivate.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- - * - * Copyright (C) 2008 David Zeuthen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#if !defined (_GUDEV_COMPILATION) && !defined(_GUDEV_INSIDE_GUDEV_H) -#error "Only can be included directly, this file may disappear or change contents." -#endif - -#ifndef __G_UDEV_PRIVATE_H__ -#define __G_UDEV_PRIVATE_H__ - -#include - -#include - -G_BEGIN_DECLS - -GUdevDevice * -_g_udev_device_new (struct udev_device *udevice); - -struct udev *_g_udev_client_get_udev (GUdevClient *client); - -G_END_DECLS - -#endif /* __G_UDEV_PRIVATE_H__ */ diff --git a/src/udev/src/gudev/gudevtypes.h b/src/udev/src/gudev/gudevtypes.h deleted file mode 100644 index 888482783d..0000000000 --- a/src/udev/src/gudev/gudevtypes.h +++ /dev/null @@ -1,51 +0,0 @@ -/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- - * - * Copyright (C) 2008 David Zeuthen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#if !defined (_GUDEV_COMPILATION) && !defined(_GUDEV_INSIDE_GUDEV_H) -#error "Only can be included directly, this file may disappear or change contents." -#endif - -#ifndef __G_UDEV_TYPES_H__ -#define __G_UDEV_TYPES_H__ - -#include -#include - -G_BEGIN_DECLS - -typedef struct _GUdevClient GUdevClient; -typedef struct _GUdevDevice GUdevDevice; -typedef struct _GUdevEnumerator GUdevEnumerator; - -/** - * GUdevDeviceNumber: - * - * Corresponds to the standard #dev_t type as defined by POSIX (Until - * bug 584517 is resolved this work-around is needed). - */ -#ifdef _GUDEV_WORK_AROUND_DEV_T_BUG -typedef guint64 GUdevDeviceNumber; /* __UQUAD_TYPE */ -#else -typedef dev_t GUdevDeviceNumber; -#endif - -G_END_DECLS - -#endif /* __G_UDEV_TYPES_H__ */ diff --git a/src/udev/src/gudev/seed-example-enum.js b/src/udev/src/gudev/seed-example-enum.js deleted file mode 100755 index 66206ad806..0000000000 --- a/src/udev/src/gudev/seed-example-enum.js +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env seed - -const GLib = imports.gi.GLib; -const GUdev = imports.gi.GUdev; - -function print_device(device) { - print(" initialized: " + device.get_is_initialized()); - print(" usec since initialized: " + device.get_usec_since_initialized()); - print(" subsystem: " + device.get_subsystem()); - print(" devtype: " + device.get_devtype()); - print(" name: " + device.get_name()); - print(" number: " + device.get_number()); - print(" sysfs_path: " + device.get_sysfs_path()); - print(" driver: " + device.get_driver()); - print(" action: " + device.get_action()); - print(" seqnum: " + device.get_seqnum()); - print(" device type: " + device.get_device_type()); - print(" device number: " + device.get_device_number()); - print(" device file: " + device.get_device_file()); - print(" device file symlinks: " + device.get_device_file_symlinks()); - print(" tags: " + device.get_tags()); - var keys = device.get_property_keys(); - for (var n = 0; n < keys.length; n++) { - print(" " + keys[n] + "=" + device.get_property(keys[n])); - } -} - -var client = new GUdev.Client({subsystems: []}); -var enumerator = new GUdev.Enumerator({client: client}); -enumerator.add_match_subsystem('b*') - -var devices = enumerator.execute(); - -for (var n=0; n < devices.length; n++) { - var device = devices[n]; - print_device(device); - print(""); -} diff --git a/src/udev/src/gudev/seed-example.js b/src/udev/src/gudev/seed-example.js deleted file mode 100755 index e2ac324d23..0000000000 --- a/src/udev/src/gudev/seed-example.js +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env seed - -// seed example - -const GLib = imports.gi.GLib; -const GUdev = imports.gi.GUdev; - -function print_device (device) { - print (" subsystem: " + device.get_subsystem ()); - print (" devtype: " + device.get_devtype ()); - print (" name: " + device.get_name ()); - print (" number: " + device.get_number ()); - print (" sysfs_path: " + device.get_sysfs_path ()); - print (" driver: " + device.get_driver ()); - print (" action: " + device.get_action ()); - print (" seqnum: " + device.get_seqnum ()); - print (" device type: " + device.get_device_type ()); - print (" device number: " + device.get_device_number ()); - print (" device file: " + device.get_device_file ()); - print (" device file symlinks: " + device.get_device_file_symlinks ()); - print (" foo: " + device.get_sysfs_attr_as_strv ("stat")); - var keys = device.get_property_keys (); - for (var n = 0; n < keys.length; n++) { - print (" " + keys[n] + "=" + device.get_property (keys[n])); - } -} - -function on_uevent (client, action, device) { - print ("action " + action + " on device " + device.get_sysfs_path()); - print_device (device); - print (""); -} - -var client = new GUdev.Client ({subsystems: ["block", "usb/usb_interface"]}); -client.signal.connect ("uevent", on_uevent); - -var block_devices = client.query_by_subsystem ("block"); -for (var n = 0; n < block_devices.length; n++) { - print ("block device: " + block_devices[n].get_device_file ()); -} - -var d; - -d = client.query_by_device_number (GUdev.DeviceType.BLOCK, 0x0810); -if (d == null) { - print ("query_by_device_number 0x810 -> null"); -} else { - print ("query_by_device_number 0x810 -> " + d.get_device_file ()); - dd = d.get_parent_with_subsystem ("usb", null); - print_device (dd); - print ("--------------------------------------------------------------------------"); - while (d != null) { - print_device (d); - print (""); - d = d.get_parent (); - } -} - -d = client.query_by_sysfs_path ("/sys/block/sda/sda1"); -print ("query_by_sysfs_path (\"/sys/block/sda1\") -> " + d.get_device_file ()); - -d = client.query_by_subsystem_and_name ("block", "sda2"); -print ("query_by_subsystem_and_name (\"block\", \"sda2\") -> " + d.get_device_file ()); - -d = client.query_by_device_file ("/dev/sda"); -print ("query_by_device_file (\"/dev/sda\") -> " + d.get_device_file ()); - -d = client.query_by_device_file ("/dev/block/8:0"); -print ("query_by_device_file (\"/dev/block/8:0\") -> " + d.get_device_file ()); - -var mainloop = GLib.main_loop_new (); -GLib.main_loop_run (mainloop); diff --git a/src/udev/src/keymap/.gitignore b/src/udev/src/keymap/.gitignore deleted file mode 100644 index 4567584f4e..0000000000 --- a/src/udev/src/keymap/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -keyboard-force-release.sh -keys-from-name.gperf -keys-from-name.h -keys-to-name.h -keys.txt diff --git a/src/udev/src/keymap/95-keyboard-force-release.rules b/src/udev/src/keymap/95-keyboard-force-release.rules deleted file mode 100644 index 03d56e8aa4..0000000000 --- a/src/udev/src/keymap/95-keyboard-force-release.rules +++ /dev/null @@ -1,54 +0,0 @@ -# Set model specific atkbd force_release quirk -# -# Several laptops have hotkeys which don't generate release events, -# which can cause problems with software key repeat. -# The atkbd driver has a quirk handler for generating synthetic -# release events, which can be configured via sysfs since 2.6.32. -# Simply add a file with a list of scancodes for your laptop model -# in /usr/lib/udev/keymaps, and add a rule here. -# If the hotkeys also need a keymap assignment you can copy the -# scancodes from the keymap file, otherwise you can run -# /usr/lib/udev/keymap -i /dev/input/eventX -# on a Linux vt to find out. - -ACTION=="remove", GOTO="force_release_end" -SUBSYSTEM!="serio", GOTO="force_release_end" -KERNEL!="serio*", GOTO="force_release_end" -DRIVER!="atkbd", GOTO="force_release_end" - -ENV{DMI_VENDOR}="$attr{[dmi/id]sys_vendor}" - -ENV{DMI_VENDOR}=="[sS][aA][mM][sS][uU][nN][gG]*", RUN+="keyboard-force-release.sh $devpath samsung-other" -ENV{DMI_VENDOR}=="[sS][aA][mM][sS][uU][nN][gG]*", ATTR{[dmi/id]product_name}=="*90X3A*", RUN+="keyboard-force-release.sh $devpath samsung-90x3a" - -ENV{DMI_VENDOR}=="Dell Inc.", ATTR{[dmi/id]product_name}=="Studio 1557|Studio 1558", RUN+="keyboard-force-release.sh $devpath common-volume-keys" -ENV{DMI_VENDOR}=="Dell Inc.", ATTR{[dmi/id]product_name}=="Latitude E*|Precision M*", RUN+="keyboard-force-release.sh $devpath dell-touchpad" - -ENV{DMI_VENDOR}=="FUJITSU SIEMENS", ATTR{[dmi/id]product_name}=="AMILO Si 1848+u|AMILO Xi 2428", RUN+="keyboard-force-release.sh $devpath common-volume-keys" - -ENV{DMI_VENDOR}=="FOXCONN", ATTR{[dmi/id]product_name}=="QBOOK", RUN+="keyboard-force-release.sh $devpath common-volume-keys" - -ENV{DMI_VENDOR}=="MTC", ATTR{[dmi/id]product_version}=="A0", RUN+="keyboard-force-release.sh $devpath common-volume-keys" - -ENV{DMI_VENDOR}=="PEGATRON CORP.", ATTR{[dmi/id]product_name}=="Spring Peak", RUN+="keyboard-force-release.sh $devpath common-volume-keys" - -ENV{DMI_VENDOR}=="TOSHIBA", ATTR{[dmi/id]product_name}=="Satellite [uU]300*|Satellite Pro [uU]300*|Satellite [uU]305*|SATELLITE [uU]500*", RUN+="keyboard-force-release.sh $devpath common-volume-keys" - -ENV{DMI_VENDOR}=="Viooo Corporation", ATTR{[dmi/id]product_name}=="PT17", RUN+="keyboard-force-release.sh $devpath common-volume-keys" - -# These are all the HP laptops that setup a touchpad toggle key -ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*[pP][aA][vV][iI][lL][iI][oO][nN]*", RUN+="keyboard-force-release.sh $devpath hp-other" -ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*[tT][xX]2*", RUN+="keyboard-force-release.sh $devpath hp-other" -ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*2510p*|*2530p*|HP G60 Notebook PC", RUN+="keyboard-force-release.sh $devpath hp-other" - -ENV{DMI_VENDOR}=="Zepto", ATTR{[dmi/id]product_name}=="Znote 6615WD", RUN+="keyboard-force-release.sh $devpath common-volume-keys" - -ENV{DMI_VENDOR}=="Zepto", ATTR{[dmi/id]product_name}=="Znote", ATTR{[dmi/id]product_version}=="6625WD", RUN+="keyboard-force-release.sh $devpath common-volume-keys" - -ENV{DMI_VENDOR}=="HANNspree", ATTR{[dmi/id]product_name}=="SN10E100", RUN+="keyboard-force-release.sh $devpath common-volume-keys" - -ENV{DMI_VENDOR}=="GIGABYTE", ATTR{[dmi/id]product_name}=="i1520M", RUN+="keyboard-force-release.sh $devpath common-volume-keys" - -ENV{DMI_VENDOR}=="BenQ", ATTR{[dmi/id]product_name}=="*nScreen*", RUN+="keyboard-force-release.sh $devpath common-volume-keys" - -LABEL="force_release_end" diff --git a/src/udev/src/keymap/95-keymap.rules b/src/udev/src/keymap/95-keymap.rules deleted file mode 100644 index bbf311a17a..0000000000 --- a/src/udev/src/keymap/95-keymap.rules +++ /dev/null @@ -1,170 +0,0 @@ -# Set model specific hotkey keycodes. -# -# Key map overrides can be specified by either giving scancode/keyname pairs -# directly as keymap arguments (if there are just one or two to change), or as -# a file name (in /usr/lib/udev/keymaps), which has to contain scancode/keyname -# pairs. - -ACTION=="remove", GOTO="keyboard_end" -KERNEL!="event*", GOTO="keyboard_end" -ENV{ID_INPUT_KEY}=="", GOTO="keyboard_end" -SUBSYSTEMS=="bluetooth", GOTO="keyboard_end" - -SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" -SUBSYSTEMS=="usb", GOTO="keyboard_usbcheck" -GOTO="keyboard_modulecheck" - -# -# The following are external USB keyboards -# - -LABEL="keyboard_usbcheck" - -ENV{ID_VENDOR}=="Genius", ENV{ID_MODEL_ID}=="0708", ENV{ID_USB_INTERFACE_NUM}=="01", RUN+="keymap $name genius-slimstar-320" -ENV{ID_VENDOR}=="Logitech*", ATTRS{name}=="Logitech USB Multimedia Keyboard", RUN+="keymap $name logitech-wave" -ENV{ID_VENDOR}=="Logitech*", ATTRS{name}=="Logitech USB Receiver", RUN+="keymap $name logitech-wave-cordless" -# Logitech Cordless Wave Pro looks slightly weird; some hotkeys are coming through the mouse interface -ENV{ID_VENDOR_ID}=="046d", ENV{ID_MODEL_ID}=="c52[9b]", ATTRS{name}=="Logitech USB Receiver", RUN+="keymap $name logitech-wave-pro-cordless" - -ENV{ID_VENDOR}=="Lite-On_Technology_Corp*", ATTRS{name}=="Lite-On Technology Corp. ThinkPad USB Keyboard with TrackPoint", RUN+="keymap $name lenovo-thinkpad-usb-keyboard-trackpoint" -ENV{ID_VENDOR_ID}=="04b3", ENV{ID_MODEL_ID}=="301[89]", RUN+="keymap $name ibm-thinkpad-usb-keyboard-trackpoint" - -ENV{ID_VENDOR}=="Microsoft", ENV{ID_MODEL_ID}=="00db", RUN+="keymap $name 0xc022d zoomin 0xc022e zoomout" - -GOTO="keyboard_end" - -# -# The following are exposed as separate input devices with low key codes, thus -# we need to check their input device product name -# - -LABEL="keyboard_modulecheck" - -ENV{DMI_VENDOR}="$attr{[dmi/id]sys_vendor}" -ENV{DMI_VENDOR}=="", GOTO="keyboard_end" - -ENV{DMI_VENDOR}=="LENOVO*", KERNELS=="input*", ATTRS{name}=="ThinkPad Extra Buttons", RUN+="keymap $name module-lenovo" -ENV{DMI_VENDOR}=="LENOVO*", KERNELS=="input*", ATTRS{name}=="Lenovo ThinkPad SL Series extra buttons", RUN+="keymap $name 0x0E bluetooth" - -ENV{DMI_VENDOR}=="ASUS*", KERNELS=="input*", ATTRS{name}=="Asus Extra Buttons", ATTR{[dmi/id]product_name}=="W3J", RUN+="keymap $name module-asus-w3j" -ENV{DMI_VENDOR}=="ASUS*", KERNELS=="input*", ATTRS{name}=="Eee PC WMI hotkeys|Asus Laptop Support|Asus*WMI*", RUN+="keymap $name 0x6B f21" -ENV{DMI_VENDOR}=="ASUS*", KERNELS=="input*", ATTRS{name}=="Eee PC Hotkey Driver", RUN+="keymap $name 0x37 f21" - -ENV{DMI_VENDOR}=="IBM*", KERNELS=="input*", ATTRS{name}=="ThinkPad Extra Buttons", RUN+="keymap $name module-ibm" -ENV{DMI_VENDOR}=="Sony*", KERNELS=="input*", ATTRS{name}=="Sony Vaio Keys", RUN+="keymap $name module-sony" -ENV{DMI_VENDOR}=="Acer*", KERNELS=="input*", ATTRS{name}=="Acer WMI hotkeys", RUN+="keymap $name 0x82 f21" -ENV{DMI_VENDOR}=="MICRO-STAR*|Micro-Star*", KERNELS=="input*", ATTRS{name}=="MSI Laptop hotkeys", RUN+="keymap $name 0x213 f22 0x214 f23" - -# Older Vaios have some different keys -ENV{DMI_VENDOR}=="Sony*", ATTR{[dmi/id]product_name}=="*PCG-C1*|*PCG-K25*|*PCG-F1*|*PCG-F2*|*PCG-F3*|*PCG-F4*|*PCG-F5*|*PCG-F6*|*PCG-FX*|*PCG-FRV*|*PCG-GR*|*PCG-TR*|*PCG-NV*|*PCG-Z*|*VGN-S360*", ATTRS{name}=="Sony Vaio Keys", RUN+="keymap $name module-sony-old" - -# Some Sony VGN models have yet another one -ENV{DMI_VENDOR}=="Sony*", ATTR{[dmi/id]product_name}=="VGN-AR71*|VGN-FW*|VGN-Z21*", ATTRS{name}=="Sony Vaio Keys", RUN+="keymap $name module-sony-vgn" - - -# -# The following rules belong to standard i8042 AT keyboard with high key codes. -# - -DRIVERS=="atkbd", GOTO="keyboard_vendorcheck" -GOTO="keyboard_end" - -LABEL="keyboard_vendorcheck" - -ENV{DMI_VENDOR}=="Dell*", RUN+="keymap $name dell" -ENV{DMI_VENDOR}=="Dell*", ATTR{[dmi/id]product_name}=="Inspiron 910|Inspiron 1010|Inspiron 1011|Inspiron 1012|Inspiron 1110|Inspiron 1210", RUN+="keymap $name 0x84 wlan" -ENV{DMI_VENDOR}=="Dell*", ATTR{[dmi/id]product_name}=="Latitude XT2", RUN+="keymap $name dell-latitude-xt2" - -ENV{DMI_VENDOR}=="Compaq*", ATTR{[dmi/id]product_name}=="*E500*|*Evo N*", RUN+="keymap $name compaq-e_evo" - -ENV{DMI_VENDOR}=="LENOVO*", ATTR{[dmi/id]product_version}=="*3000*", RUN+="keymap $name lenovo-3000" -ENV{DMI_VENDOR}=="LENOVO*", ATTR{[dmi/id]product_version}=="ThinkPad X6*", ATTR{[dmi/id]product_version}=="* Tablet", RUN+="keymap $name lenovo-thinkpad_x6_tablet" -ENV{DMI_VENDOR}=="LENOVO*", ATTR{[dmi/id]product_version}=="ThinkPad X2[02]* Tablet*", ATTR{[dmi/id]product_version}=="* Tablet", RUN+="keymap $name lenovo-thinkpad_x200_tablet" -ENV{DMI_VENDOR}=="LENOVO*", ATTR{[dmi/id]product_version}=="*IdeaPad*", RUN+="keymap $name lenovo-ideapad" -ENV{DMI_VENDOR}=="LENOVO*", ATTR{[dmi/id]product_name}=="S10-*", RUN+="keymap $name lenovo-ideapad" -ENV{DMI_VENDOR}=="LENOVO", ATTR{[dmi/id]product_version}=="*IdeaPad Y550*", RUN+="keymap $name 0x95 media 0xA3 play" - -ENV{DMI_VENDOR}=="Hewlett-Packard*", RUN+="keymap $name hewlett-packard" -ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*[tT][aA][bB][lL][eE][tT]*", RUN+="keymap $name hewlett-packard-tablet" -ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*[pP][aA][vV][iI][lL][iI][oO][nN]*", RUN+="keymap $name hewlett-packard-pavilion" -ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*Compaq*|*EliteBook*|*2230s*", RUN+="keymap $name hewlett-packard-compaq_elitebook" -ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*2510p*|*2530p*|HP G60 Notebook PC", RUN+="keymap $name hewlett-packard-2510p_2530p" -ENV{DMI_VENDOR}=="Hewlett-Packard*", ATTR{[dmi/id]product_name}=="*[tT][xX]2*", RUN+="keymap $name hewlett-packard-tx2" -ENV{DMI_VENDOR}=="Hewlett-Packard", ATTR{[dmi/id]product_name}=="Presario 2100*", RUN+="keymap $name hewlett-packard-presario-2100" -ENV{DMI_VENDOR}=="Hewlett-Packard", ATTR{[dmi/id]product_name}=="HP G62 Notebook PC", RUN+="keymap $name 0xB2 www" -ENV{DMI_VENDOR}=="Hewlett-Packard", ATTR{[dmi/id]product_name}=="HP ProBook*", RUN+="keymap $name 0xF8 rfkill" -# HP Pavillion dv6315ea has empty DMI_VENDOR -ATTR{[dmi/id]board_vendor}=="Quanta", ATTR{[dmi/id]board_name}=="30B7", ATTR{[dmi/id]board_version}=="65.2B", RUN+="keymap $name 0x88 media" # "quick play - -# Gateway clone of Acer Aspire One AOA110/AOA150 -ENV{DMI_VENDOR}=="Gateway*", ATTR{[dmi/id]product_name}=="*AOA1*", RUN+="keymap $name acer" - -ENV{DMI_VENDOR}=="Acer*", RUN+="keymap $name acer" -ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_name}=="Extensa*", ATTR{[dmi/id]product_name}=="*5210*|*5220*|*5610*|*5620*|*5720*", RUN+="keymap $name 0xEE screenlock" -ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_name}=="TravelMate*C3[01]0*", RUN+="keymap $name acer-travelmate_c300" -ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_name}=="TravelMate*6292*|TravelMate*8471*|TravelMate*4720*|TravelMate*7720*|Aspire 1810T*|AO751h|AO531h", RUN+="keymap $name 0xD9 bluetooth" -ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_name}=="TravelMate*4720*", RUN+="keymap $name 0xB2 www 0xEE screenlock" -ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_name}=="TravelMate 6593|Aspire 1640", RUN+="keymap $name 0xB2 www 0xEE screenlock" -ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_name}=="Aspire 6920", RUN+="keymap $name acer-aspire_6920" -ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_name}=="Aspire 5920G", RUN+="keymap $name acer-aspire_5920g" -ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_name}=="Aspire 5720*", RUN+="keymap $name acer-aspire_5720" -ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_name}=="Aspire 8930", RUN+="keymap $name acer-aspire_8930" -ENV{DMI_VENDOR}=="Acer*", ATTR{[dmi/id]product_serial}=="ZG8*", RUN+="keymap $name acer-aspire_5720" - -ENV{DMI_VENDOR}=="*BenQ*", ATTR{[dmi/id]product_name}=="*Joybook R22*", RUN+="keymap $name 0x6E wlan" - -ENV{DMI_VENDOR}=="FUJITSU*", ATTR{[dmi/id]product_name}=="*AMILO Pro V3205*", RUN+="keymap $name fujitsu-amilo_pro_v3205" -ENV{DMI_VENDOR}=="FUJITSU*", ATTR{[dmi/id]product_name}=="*AMILO Pa 2548*", RUN+="keymap $name fujitsu-amilo_pa_2548" -ENV{DMI_VENDOR}=="FUJITSU*", ATTR{[dmi/id]product_name}=="*ESPRIMO Mobile V5*", RUN+="keymap $name fujitsu-esprimo_mobile_v5" -ENV{DMI_VENDOR}=="FUJITSU*", ATTR{[dmi/id]product_name}=="*ESPRIMO Mobile V6*", RUN+="keymap $name fujitsu-esprimo_mobile_v6" -ENV{DMI_VENDOR}=="FUJITSU*", ATTR{[dmi/id]product_name}=="*AMILO Pro Edition V3505*", RUN+="keymap $name fujitsu-amilo_pro_edition_v3505" -ENV{DMI_VENDOR}=="FUJITSU*", ATTR{[dmi/id]product_name}=="*Amilo Si 1520*", RUN+="keymap $name fujitsu-amilo_si_1520" -ENV{DMI_VENDOR}=="FUJITSU*", ATTR{[dmi/id]product_name}=="AMILO*M*", RUN+="keymap $name 0x97 prog2 0x9F prog1" -ENV{DMI_VENDOR}=="FUJITSU*", ATTR{[dmi/id]product_name}=="Amilo Li 1718", RUN+="keymap $name 0xD6 wlan" -ENV{DMI_VENDOR}=="FUJITSU*", ATTR{[dmi/id]product_name}=="AMILO Li 2732", RUN+="keymap $name fujitsu-amilo_li_2732" - -ENV{DMI_VENDOR}=="LG*", ATTR{[dmi/id]product_name}=="*X110*", RUN+="keymap $name lg-x110" - -ENV{DMI_VENDOR}=="MEDION*", ATTR{[dmi/id]product_name}=="*FID2060*", RUN+="keymap $name medion-fid2060" -ENV{DMI_VENDOR}=="MEDIONNB", ATTR{[dmi/id]product_name}=="A555*", RUN+="keymap $name medionnb-a555" - -ENV{DMI_VENDOR}=="MICRO-STAR*|Micro-Star*", RUN+="keymap $name micro-star" - -# some MSI models generate ACPI/input events on the LNXVIDEO input devices, -# plus some extra synthesized ones on atkbd as an echo of actually changing the -# brightness; so ignore those atkbd ones, to avoid loops -ENV{DMI_VENDOR}=="MICRO-STAR*", ATTR{[dmi/id]product_name}=="*U-100*|*U100*|*N033", RUN+="keymap $name 0xF7 reserved 0xF8 reserved" - -ENV{DMI_VENDOR}=="INVENTEC", ATTR{[dmi/id]product_name}=="SYMPHONY 6.0/7.0", RUN+="keymap $name inventec-symphony_6.0_7.0" - -ENV{DMI_VENDOR}=="MAXDATA", ATTR{[dmi/id]product_name}=="Pro 7000*", RUN+="keymap $name maxdata-pro_7000" - -ENV{DMI_VENDOR}=="[sS][aA][mM][sS][uU][nN][gG]*", RUN+="keymap $name samsung-other" -ENV{DMI_VENDOR}=="[sS][aA][mM][sS][uU][nN][gG]*", ATTR{[dmi/id]product_name}=="*SX20S*", RUN+="keymap $name samsung-sx20s" -ENV{DMI_VENDOR}=="[sS][aA][mM][sS][uU][nN][gG]*", ATTR{[dmi/id]product_name}=="SQ1US", RUN+="keymap $name samsung-sq1us" -ENV{DMI_VENDOR}=="[sS][aA][mM][sS][uU][nN][gG]*", ATTR{[dmi/id]product_name}=="*700Z*", RUN+="keymap $name 0xBA ejectcd 0x96 keyboardbrightnessup 0x97 keyboardbrightnessdown" -ENV{DMI_VENDOR}=="[sS][aA][mM][sS][uU][nN][gG]*", ATTR{[dmi/id]product_name}=="*90X3A*", RUN+="keymap $name samsung-90x3a" - -ENV{DMI_VENDOR}=="TOSHIBA", ATTR{[dmi/id]product_name}=="SATELLITE A100", RUN+="keymap $name toshiba-satellite_a100" -ENV{DMI_VENDOR}=="TOSHIBA", ATTR{[dmi/id]product_name}=="Satellite A110", RUN+="keymap $name toshiba-satellite_a110" -ENV{DMI_VENDOR}=="TOSHIBA", ATTR{[dmi/id]product_name}=="Satellite M30X", RUN+="keymap $name toshiba-satellite_m30x" - -ENV{DMI_VENDOR}=="OQO Inc.*", ATTR{[dmi/id]product_name}=="OQO Model 2*", RUN+="keymap $name oqo-model2" - -ENV{DMI_VENDOR}=="ONKYO CORPORATION", ATTR{[dmi/id]product_name}=="ONKYOPC", RUN+="keymap $name onkyo" - -ENV{DMI_VENDOR}=="ASUS", RUN+="keymap $name asus" - -ENV{DMI_VENDOR}=="VIA", ATTR{[dmi/id]product_name}=="K8N800", ATTR{[dmi/id]product_version}=="VT8204B", RUN+="keymap $name 0x81 prog1" - -ENV{DMI_VENDOR}=="Zepto", ATTR{[dmi/id]product_name}=="Znote", ATTR{[dmi/id]product_version}=="62*|63*", RUN+="keymap $name zepto-znote" - -ENV{DMI_VENDOR}=="Everex", ATTR{[dmi/id]product_name}=="XT5000*", RUN+="keymap $name everex-xt5000" - -ENV{DMI_VENDOR}=="COMPAL", ATTR{[dmi/id]product_name}=="HEL80I", RUN+="keymap $name 0x84 wlan" - -ENV{DMI_VENDOR}=="OLPC", ATTR{[dmi/id]product_name}=="XO", RUN+="keymap $name olpc-xo" - -ENV{DMI_VENDOR}=="Alienware*", ATTR{[dmi/id]product_name}=="M14xR1", RUN+="keymap $name 0x8A ejectcd" - -LABEL="keyboard_end" diff --git a/src/udev/src/keymap/README.keymap.txt b/src/udev/src/keymap/README.keymap.txt deleted file mode 100644 index 52d50ed2de..0000000000 --- a/src/udev/src/keymap/README.keymap.txt +++ /dev/null @@ -1,101 +0,0 @@ -= The udev keymap tool = - -== Introduction == - -This udev extension configures computer model specific key mappings. This is -particularly necessary for the non-standard extra keys found on many laptops, -such as "brightness up", "next song", "www browser", or "suspend". Often these -are accessed with the Fn key. - -Every key produces a "scan code", which is highly vendor/model specific for the -nonstandard keys. This tool maintains mappings for these scan codes to standard -"key codes", which denote the "meaning" of the key. The key codes are defined -in /usr/include/linux/input.h. - -If some of your keys on your keyboard are not working at all, or produce the -wrong effect, then a very likely cause of this is that the scan code -> key -code mapping is incorrect on your computer. - -== Structure == - -udev-keymap consists of the following parts: - - keymaps/*:: mappings of scan codes to key code names - - 95-keymap.rules:: udev rules for mapping system vendor/product names and - input module names to one of the keymaps above - - keymap:: manipulate an evdev input device: - * write a key map file into a device (used by udev rules) - * dump current scan → key code mapping - * interactively display scan and key codes of pressed keys - - findkeyboards:: display evdev input devices which belong to actual keyboards, - i. e. those suitable for the keymap program - - fdi2rules.py:: convert hal keymap FDIs into udev rules and key map files - (Please note that this is far from perfect, since the mapping between fdi and - udev rules is not straightforward, and impossible in some cases.) - -== Fixing broken keys == - -In order to make a broken key work on your system and send it back to upstream -for inclusion you need to do the following steps: - - 1. Find the keyboard device. - - Run /usr/lib/udev/findkeyboards. This should always give you an "AT - keyboard" and possibly a "module". Some laptops (notably Thinkpads, Sonys, and - Acers) have multimedia/function keys on a separate input device instead of the - primary keyboard. The keyboard device should have a name like "input/event3". - In the following commands, the name will be written as "input/eventX" (replace - X with the appropriate number). - - 2. Find broken scan codes: - - sudo /usr/lib/udev/keymap -i input/eventX - - Press all multimedia/function keys and check if the key name that gets printed - out is plausible. If it is unknown or wrong, write down the scan code (looks - like "0x1E") and the intended functionality of this key. Look in - /usr/include/linux/input.h for an available KEY_XXXXX constant which most - closely approximates this functionality and write it down as the new key code. - - For example, you might press a key labeled "web browser" which currently - produces "unknown". Note down this: - - 0x1E www # Fn+F2 web browser - - Repeat that for all other keys. Write the resulting list into a file. Look at - /usr/lib/udev/keymaps/ for existing key map files and make sure that you use the - same structure. - - If the key only ever works once and then your keyboard (or the entire desktop) - gets stuck for a long time, then it is likely that the BIOS fails to send a - corresponding "key release" event after the key press event. Please note down - this case as well, as it can be worked around in - /usr/lib/udev/keymaps/95-keyboard-force-release.rules . - - 3. Find out your system vendor and product: - - cat /sys/class/dmi/id/sys_vendor - cat /sys/class/dmi/id/product_name - - 4. Generate a device dump with "udevadm info --export-db > /tmp/udev-db.txt". - - 6. Send the system vendor/product names, the key mapping from step 2, - and /tmp/udev-db.txt from step 4 to the linux-hotplug@vger.kernel.org mailing - list, so that they can be included in the next release. - -For local testing, copy your map file to /usr/lib/udev/keymaps/ with an appropriate -name, and add an appropriate udev rule to /usr/lib/udev/rules.d/95-keymap.rules: - - * If you selected an "AT keyboard", add the rule to the section after - 'LABEL="keyboard_vendorcheck"'. - - * If you selected a "module", add the rule to the top section where the - "ThinkPad Extra Buttons" are. - -== Author == - -keymap is written and maintained by Martin Pitt . diff --git a/src/udev/src/keymap/check-keymaps.sh b/src/udev/src/keymap/check-keymaps.sh deleted file mode 100755 index 405168c667..0000000000 --- a/src/udev/src/keymap/check-keymaps.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -# check that all key names in keymaps/* are known in -# and that all key maps listed in the rules are valid and present in -# Makefile.am -SRCDIR=${1:-.} -KEYLIST=${2:-src/keymap/keys.txt} -KEYMAPS_DIR=$SRCDIR/src/keymap/keymaps -RULES=$SRCDIR/src/keymap/95-keymap.rules - -[ -e "$KEYLIST" ] || { - echo "need $KEYLIST please build first" >&2 - exit 1 -} - -missing=$(join -v 2 <(awk '{print tolower(substr($1,5))}' $KEYLIST | sort -u) \ - <(grep -hv '^#' ${KEYMAPS_DIR}/*| awk '{print $2}' | sort -u)) -[ -z "$missing" ] || { - echo "ERROR: unknown key names in src/keymap/keymaps/*:" >&2 - echo "$missing" >&2 - exit 1 -} - -# check that all maps referred to in $RULES exist -maps=$(sed -rn '/keymap \$name/ { s/^.*\$name ([^"[:space:]]+).*$/\1/; p }' $RULES) -for m in $maps; do - # ignore inline mappings - [ "$m" = "${m#0x}" ] || continue - - [ -e ${KEYMAPS_DIR}/$m ] || { - echo "ERROR: unknown map name in $RULES: $m" >&2 - exit 1 - } - grep -q "src/keymap/keymaps/$m\>" $SRCDIR/Makefile.am || { - echo "ERROR: map file $m is not added to Makefile.am" >&2 - exit 1 - } -done diff --git a/src/udev/src/keymap/findkeyboards b/src/udev/src/keymap/findkeyboards deleted file mode 100755 index 9ce27429b2..0000000000 --- a/src/udev/src/keymap/findkeyboards +++ /dev/null @@ -1,68 +0,0 @@ -#!/bin/sh -e -# Find "real" keyboard devices and print their device path. -# Author: Martin Pitt -# -# Copyright (C) 2009, Canonical Ltd. -# -# This program is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. - -# returns OK if $1 contains $2 -strstr() { - [ "${1#*$2*}" != "$1" ] -} - -# returns OK if $1 contains $2 at the beginning -str_starts() { - [ "${1#$2*}" != "$1" ] -} - -str_line_starts() { - while read a; do str_starts "$a" "$1" && return 0;done - return 1; -} - -# print a list of input devices which are keyboard-like -keyboard_devices() { - # standard AT keyboard - for dev in `udevadm trigger --dry-run --verbose --property-match=ID_INPUT_KEYBOARD=1`; do - walk=`udevadm info --attribute-walk --path=$dev` - env=`udevadm info --query=env --path=$dev` - # filter out non-event devices, such as the parent input devices which have no devnode - if ! echo "$env" | str_line_starts 'DEVNAME='; then - continue - fi - if strstr "$walk" 'DRIVERS=="atkbd"'; then - echo -n 'AT keyboard: ' - elif echo "$env" | str_line_starts 'ID_USB_DRIVER=usbhid'; then - echo -n 'USB keyboard: ' - else - echo -n 'Unknown type: ' - fi - udevadm info --query=name --path=$dev - done - - # modules - module=$(udevadm trigger --verbose --dry-run --subsystem-match=input --attr-match=name='*Extra Buttons') - module="$module - $(udevadm trigger --verbose --dry-run --subsystem-match=input --attr-match=name='*extra buttons')" - module="$module - $(udevadm trigger --verbose --dry-run --subsystem-match=input --attr-match=name='Sony Vaio Keys')" - for m in $module; do - for evdev in $m/event*/dev; do - if [ -e "$evdev" ]; then - echo -n 'module: ' - udevadm info --query=name --path=${evdev%%/dev} - fi - done - done -} - -keyboard_devices diff --git a/src/udev/src/keymap/force-release-maps/common-volume-keys b/src/udev/src/keymap/force-release-maps/common-volume-keys deleted file mode 100644 index 3a7654d735..0000000000 --- a/src/udev/src/keymap/force-release-maps/common-volume-keys +++ /dev/null @@ -1,3 +0,0 @@ -0xa0 #mute -0xae #volume down -0xb0 #volume up diff --git a/src/udev/src/keymap/force-release-maps/dell-touchpad b/src/udev/src/keymap/force-release-maps/dell-touchpad deleted file mode 100644 index 18e9bdee66..0000000000 --- a/src/udev/src/keymap/force-release-maps/dell-touchpad +++ /dev/null @@ -1 +0,0 @@ -0x9E diff --git a/src/udev/src/keymap/force-release-maps/hp-other b/src/udev/src/keymap/force-release-maps/hp-other deleted file mode 100644 index 6621370095..0000000000 --- a/src/udev/src/keymap/force-release-maps/hp-other +++ /dev/null @@ -1,3 +0,0 @@ -# list of scancodes (hex or decimal), optional comment -0xd8 # Touchpad off -0xd9 # Touchpad on diff --git a/src/udev/src/keymap/force-release-maps/samsung-90x3a b/src/udev/src/keymap/force-release-maps/samsung-90x3a deleted file mode 100644 index 65707effb7..0000000000 --- a/src/udev/src/keymap/force-release-maps/samsung-90x3a +++ /dev/null @@ -1,6 +0,0 @@ -# list of scancodes (hex or decimal), optional comment -0xCE # Fn+F8 keyboard backlit up -0x8D # Fn+F7 keyboard backlit down -0x97 # Fn+F12 wifi on/off -0x96 # Fn+F1 performance mode (?) -0xD5 # Fn+F6 battery life extender diff --git a/src/udev/src/keymap/force-release-maps/samsung-other b/src/udev/src/keymap/force-release-maps/samsung-other deleted file mode 100644 index c51123a0b6..0000000000 --- a/src/udev/src/keymap/force-release-maps/samsung-other +++ /dev/null @@ -1,10 +0,0 @@ -# list of scancodes (hex or decimal), optional comment -0x82 # Fn+F4 CRT/LCD -0x83 # Fn+F2 battery -0x84 # Fn+F5 backlight on/off -0x86 # Fn+F9 WLAN -0x88 # Fn-Up brightness up -0x89 # Fn-Down brightness down -0xB3 # Fn+F8 switch power mode (battery/dynamic/performance) -0xF7 # Fn+F10 Touchpad on -0xF9 # Fn+F10 Touchpad off diff --git a/src/udev/src/keymap/keyboard-force-release.sh.in b/src/udev/src/keymap/keyboard-force-release.sh.in deleted file mode 100755 index dd040cebc3..0000000000 --- a/src/udev/src/keymap/keyboard-force-release.sh.in +++ /dev/null @@ -1,22 +0,0 @@ -#!@rootprefix@/bin/sh -e -# read list of scancodes, convert hex to decimal and -# append to the atkbd force_release sysfs attribute -# $1 sysfs devpath for serioX -# $2 file with scancode list (hex or dec) - -case "$2" in - /*) scf="$2" ;; - *) scf="@pkglibexecdir@/keymaps/force-release/$2" ;; -esac - -read attr <"/sys/$1/force_release" -while read scancode dummy; do - case "$scancode" in - \#*) ;; - *) - scancode=$(($scancode)) - attr="$attr${attr:+,}$scancode" - ;; - esac -done <"$scf" -echo "$attr" >"/sys/$1/force_release" diff --git a/src/udev/src/keymap/keymap.c b/src/udev/src/keymap/keymap.c deleted file mode 100644 index 92ec67b3a6..0000000000 --- a/src/udev/src/keymap/keymap.c +++ /dev/null @@ -1,447 +0,0 @@ -/* - * keymap - dump keymap of an evdev device or set a new keymap from a file - * - * Based on keyfuzz by Lennart Poettering - * Adapted for udev-extras by Martin Pitt - * - * Copyright (C) 2006, Lennart Poettering - * Copyright (C) 2009, Canonical Ltd. - * - * keymap is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * keymap is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with keymap; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -const struct key* lookup_key (const char *str, unsigned int len); - -#include "keys-from-name.h" -#include "keys-to-name.h" - -#define MAX_SCANCODES 1024 - -static int evdev_open(const char *dev) -{ - int fd; - char fn[PATH_MAX]; - - if (strncmp(dev, "/dev", 4) != 0) { - snprintf(fn, sizeof(fn), "/dev/%s", dev); - dev = fn; - } - - if ((fd = open(dev, O_RDWR)) < 0) { - fprintf(stderr, "error open('%s'): %m\n", dev); - return -1; - } - return fd; -} - -static int evdev_get_keycode(int fd, int scancode, int e) -{ - int codes[2]; - - codes[0] = scancode; - if (ioctl(fd, EVIOCGKEYCODE, codes) < 0) { - if (e && errno == EINVAL) { - return -2; - } else { - fprintf(stderr, "EVIOCGKEYCODE: %m\n"); - return -1; - } - } - return codes[1]; -} - -static int evdev_set_keycode(int fd, int scancode, int keycode) -{ - int codes[2]; - - codes[0] = scancode; - codes[1] = keycode; - - if (ioctl(fd, EVIOCSKEYCODE, codes) < 0) { - fprintf(stderr, "EVIOCSKEYCODE: %m\n"); - return -1; - } - return 0; -} - -static int evdev_driver_version(int fd, char *v, size_t l) -{ - int version; - - if (ioctl(fd, EVIOCGVERSION, &version)) { - fprintf(stderr, "EVIOCGVERSION: %m\n"); - return -1; - } - - snprintf(v, l, "%i.%i.%i.", version >> 16, (version >> 8) & 0xff, version & 0xff); - return 0; -} - -static int evdev_device_name(int fd, char *n, size_t l) -{ - if (ioctl(fd, EVIOCGNAME(l), n) < 0) { - fprintf(stderr, "EVIOCGNAME: %m\n"); - return -1; - } - return 0; -} - -/* Return a lower-case string with KEY_ prefix removed */ -static const char* format_keyname(const char* key) { - static char result[101]; - const char* s; - int len; - - for (s = key+4, len = 0; *s && len < 100; ++len, ++s) - result[len] = tolower(*s); - result[len] = '\0'; - return result; -} - -static int dump_table(int fd) { - char version[256], name[256]; - int scancode, r = -1; - - if (evdev_driver_version(fd, version, sizeof(version)) < 0) - goto fail; - - if (evdev_device_name(fd, name, sizeof(name)) < 0) - goto fail; - - printf("### evdev %s, driver '%s'\n", version, name); - - r = 0; - for (scancode = 0; scancode < MAX_SCANCODES; scancode++) { - int keycode; - - if ((keycode = evdev_get_keycode(fd, scancode, 1)) < 0) { - if (keycode == -2) - continue; - r = -1; - break; - } - - if (keycode < KEY_MAX && key_names[keycode]) - printf("0x%03x %s\n", scancode, format_keyname(key_names[keycode])); - else - printf("0x%03x 0x%03x\n", scancode, keycode); - } -fail: - return r; -} - -static void set_key(int fd, const char* scancode_str, const char* keyname) -{ - unsigned scancode; - char *endptr; - char t[105] = "KEY_UNKNOWN"; - const struct key *k; - - scancode = (unsigned) strtol(scancode_str, &endptr, 0); - if (*endptr != '\0') { - fprintf(stderr, "ERROR: Invalid scancode\n"); - exit(1); - } - - snprintf(t, sizeof(t), "KEY_%s", keyname); - - if (!(k = lookup_key(t, strlen(t)))) { - fprintf(stderr, "ERROR: Unknown key name '%s'\n", keyname); - exit(1); - } - - if (evdev_set_keycode(fd, scancode, k->id) < 0) - fprintf(stderr, "setting scancode 0x%2X to key code %i failed\n", - scancode, k->id); - else - printf("setting scancode 0x%2X to key code %i\n", - scancode, k->id); -} - -static int merge_table(int fd, FILE *f) { - int r = 0; - int line = 0; - - while (!feof(f)) { - char s[256], *p; - int scancode, new_keycode, old_keycode; - - if (!fgets(s, sizeof(s), f)) - break; - - line++; - p = s+strspn(s, "\t "); - if (*p == '#' || *p == '\n') - continue; - - if (sscanf(p, "%i %i", &scancode, &new_keycode) != 2) { - char t[105] = "KEY_UNKNOWN"; - const struct key *k; - - if (sscanf(p, "%i %100s", &scancode, t+4) != 2) { - fprintf(stderr, "WARNING: Parse failure at line %i, ignoring.\n", line); - r = -1; - continue; - } - - if (!(k = lookup_key(t, strlen(t)))) { - fprintf(stderr, "WARNING: Unknown key '%s' at line %i, ignoring.\n", t, line); - r = -1; - continue; - } - - new_keycode = k->id; - } - - - if ((old_keycode = evdev_get_keycode(fd, scancode, 0)) < 0) { - r = -1; - goto fail; - } - - if (evdev_set_keycode(fd, scancode, new_keycode) < 0) { - r = -1; - goto fail; - } - - if (new_keycode != old_keycode) - fprintf(stderr, "Remapped scancode 0x%02x to 0x%02x (prior: 0x%02x)\n", - scancode, new_keycode, old_keycode); - } -fail: - fclose(f); - return r; -} - - -/* read one event; return 1 if valid */ -static int read_event(int fd, struct input_event* ev) -{ - int ret; - ret = read(fd, ev, sizeof(struct input_event)); - - if (ret < 0) { - perror("read"); - return 0; - } - if (ret != sizeof(struct input_event)) { - fprintf(stderr, "did not get enough data for event struct, aborting\n"); - return 0; - } - - return 1; -} - -static void print_key(uint32_t scancode, uint16_t keycode, int has_scan, int has_key) -{ - const char *keyname; - - /* ignore key release events */ - if (has_key == 1) - return; - - if (has_key == 0 && has_scan != 0) { - fprintf(stderr, "got scan code event 0x%02X without a key code event\n", - scancode); - return; - } - - if (has_scan != 0) - printf("scan code: 0x%02X ", scancode); - else - printf("(no scan code received) "); - - keyname = key_names[keycode]; - if (keyname != NULL) - printf("key code: %s\n", format_keyname(keyname)); - else - printf("key code: %03X\n", keycode); -} - -static void interactive(int fd) -{ - struct input_event ev; - uint32_t last_scan = 0; - uint16_t last_key = 0; - int has_scan; /* boolean */ - int has_key; /* 0: none, 1: release, 2: press */ - - /* grab input device */ - ioctl(fd, EVIOCGRAB, 1); - puts("Press ESC to finish, or Control-C if this device is not your primary keyboard"); - - has_scan = has_key = 0; - while (read_event(fd, &ev)) { - /* Drivers usually send the scan code first, then the key code, - * then a SYN. Some drivers (like thinkpad_acpi) send the key - * code first, and some drivers might not send SYN events, so - * keep a robust state machine which can deal with any of those - */ - - if (ev.type == EV_MSC && ev.code == MSC_SCAN) { - if (has_scan) { - fputs("driver did not send SYN event in between key events; previous event:\n", - stderr); - print_key(last_scan, last_key, has_scan, has_key); - has_key = 0; - } - - last_scan = ev.value; - has_scan = 1; - /*printf("--- got scan %u; has scan %i key %i\n", last_scan, has_scan, has_key); */ - } - else if (ev.type == EV_KEY) { - if (has_key) { - fputs("driver did not send SYN event in between key events; previous event:\n", - stderr); - print_key(last_scan, last_key, has_scan, has_key); - has_scan = 0; - } - - last_key = ev.code; - has_key = 1 + ev.value; - /*printf("--- got key %hu; has scan %i key %i\n", last_key, has_scan, has_key);*/ - - /* Stop on ESC */ - if (ev.code == KEY_ESC && ev.value == 0) - break; - } - else if (ev.type == EV_SYN) { - /*printf("--- got SYN; has scan %i key %i\n", has_scan, has_key);*/ - print_key(last_scan, last_key, has_scan, has_key); - - has_scan = has_key = 0; - } - - } - - /* release input device */ - ioctl(fd, EVIOCGRAB, 0); -} - -static void help(int error) -{ - const char* h = "Usage: keymap []\n" - " keymap scancode keyname [...]\n" - " keymap -i \n"; - if (error) { - fputs(h, stderr); - exit(2); - } else { - fputs(h, stdout); - exit(0); - } -} - -int main(int argc, char **argv) -{ - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "interactive", no_argument, NULL, 'i' }, - {} - }; - int fd = -1; - int opt_interactive = 0; - int i; - - while (1) { - int option; - - option = getopt_long(argc, argv, "hi", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'h': - help(0); - - case 'i': - opt_interactive = 1; - break; - default: - return 1; - } - } - - if (argc < optind+1) - help (1); - - if ((fd = evdev_open(argv[optind])) < 0) - return 3; - - /* one argument (device): dump or interactive */ - if (argc == optind+1) { - if (opt_interactive) - interactive(fd); - else - dump_table(fd); - return 0; - } - - /* two arguments (device, mapfile): set map file */ - if (argc == optind+2) { - const char *filearg = argv[optind+1]; - if (strchr(filearg, '/')) { - /* Keymap file argument is a path */ - FILE *f = fopen(filearg, "r"); - if (f) - merge_table(fd, f); - else - perror(filearg); - } else { - /* Keymap file argument is a filename */ - /* Open override file if present, otherwise default file */ - char keymap_path[PATH_MAX]; - snprintf(keymap_path, sizeof(keymap_path), "%s%s", SYSCONFDIR "/udev/keymaps/", filearg); - FILE *f = fopen(keymap_path, "r"); - if (f) { - merge_table(fd, f); - } else { - snprintf(keymap_path, sizeof(keymap_path), "%s%s", PKGLIBEXECDIR "/keymaps/", filearg); - f = fopen(keymap_path, "r"); - if (f) - merge_table(fd, f); - else - perror(keymap_path); - } - } - return 0; - } - - /* more arguments (device, scancode/keyname pairs): set keys directly */ - if ((argc - optind - 1) % 2 == 0) { - for (i = optind+1; i < argc; i += 2) - set_key(fd, argv[i], argv[i+1]); - return 0; - } - - /* invalid number of arguments */ - help(1); - return 1; /* not reached */ -} diff --git a/src/udev/src/keymap/keymaps/acer b/src/udev/src/keymap/keymaps/acer deleted file mode 100644 index 4e7c297dea..0000000000 --- a/src/udev/src/keymap/keymaps/acer +++ /dev/null @@ -1,22 +0,0 @@ -0xA5 help # Fn+F1 -0xA6 setup # Fn+F2 Acer eSettings -0xA7 battery # Fn+F3 Power Management -0xA9 switchvideomode # Fn+F5 -0xB3 euro -0xB4 dollar -0xCE brightnessup # Fn+Right -0xD4 bluetooth # (toggle) off-to-on -0xD5 wlan # (toggle) on-to-off -0xD6 wlan # (toggle) off-to-on -0xD7 bluetooth # (toggle) on-to-off -0xD8 bluetooth # (toggle) off-to-on -0xD9 brightnessup # Fn+Right -0xEE brightnessup # Fn+Right -0xEF brightnessdown # Fn+Left -0xF1 f22 # Fn+F7 Touchpad toggle (off-to-on) -0xF2 f23 # Fn+F7 Touchpad toggle (on-to-off) -0xF3 prog2 # "P2" programmable button -0xF4 prog1 # "P1" programmable button -0xF5 presentation -0xF8 fn -0xF9 f23 # Launch NTI shadow diff --git a/src/udev/src/keymap/keymaps/acer-aspire_5720 b/src/udev/src/keymap/keymaps/acer-aspire_5720 deleted file mode 100644 index 1496d63a52..0000000000 --- a/src/udev/src/keymap/keymaps/acer-aspire_5720 +++ /dev/null @@ -1,4 +0,0 @@ -0x84 bluetooth # sent when bluetooth module missing, and key pressed -0x92 media # acer arcade -0xD4 bluetooth # bluetooth on -0xD9 bluetooth # bluetooth off diff --git a/src/udev/src/keymap/keymaps/acer-aspire_5920g b/src/udev/src/keymap/keymaps/acer-aspire_5920g deleted file mode 100644 index 633c4e854c..0000000000 --- a/src/udev/src/keymap/keymaps/acer-aspire_5920g +++ /dev/null @@ -1,5 +0,0 @@ -0x8A media -0x92 media -0xA6 setup -0xB2 www -0xD9 bluetooth # (toggle) on-to-off diff --git a/src/udev/src/keymap/keymaps/acer-aspire_6920 b/src/udev/src/keymap/keymaps/acer-aspire_6920 deleted file mode 100644 index 699c954b4e..0000000000 --- a/src/udev/src/keymap/keymaps/acer-aspire_6920 +++ /dev/null @@ -1,5 +0,0 @@ -0xD9 bluetooth # (toggle) on-to-off -0x92 media -0x9E back -0x83 rewind -0x89 fastforward diff --git a/src/udev/src/keymap/keymaps/acer-aspire_8930 b/src/udev/src/keymap/keymaps/acer-aspire_8930 deleted file mode 100644 index fb27bfb4f5..0000000000 --- a/src/udev/src/keymap/keymaps/acer-aspire_8930 +++ /dev/null @@ -1,5 +0,0 @@ -0xCA prog3 # key 'HOLD' on cine dash media console -0x83 rewind -0x89 fastforward -0x92 media # key 'ARCADE' on cine dash media console -0x9E back diff --git a/src/udev/src/keymap/keymaps/acer-travelmate_c300 b/src/udev/src/keymap/keymaps/acer-travelmate_c300 deleted file mode 100644 index bfef4cf868..0000000000 --- a/src/udev/src/keymap/keymaps/acer-travelmate_c300 +++ /dev/null @@ -1,5 +0,0 @@ -0x67 f24 # FIXME: rotate screen -0x68 up -0x69 down -0x6B fn -0x6C screenlock # FIXME: lock tablet device/buttons diff --git a/src/udev/src/keymap/keymaps/asus b/src/udev/src/keymap/keymaps/asus deleted file mode 100644 index 2a5995f982..0000000000 --- a/src/udev/src/keymap/keymaps/asus +++ /dev/null @@ -1,3 +0,0 @@ -0xED volumeup -0xEE volumedown -0xEF mute diff --git a/src/udev/src/keymap/keymaps/compaq-e_evo b/src/udev/src/keymap/keymaps/compaq-e_evo deleted file mode 100644 index 5fbc573aa4..0000000000 --- a/src/udev/src/keymap/keymaps/compaq-e_evo +++ /dev/null @@ -1,4 +0,0 @@ -0xA3 www # I key -0x9A search -0x9E email -0x9F homepage diff --git a/src/udev/src/keymap/keymaps/dell b/src/udev/src/keymap/keymaps/dell deleted file mode 100644 index 4f907b3eef..0000000000 --- a/src/udev/src/keymap/keymaps/dell +++ /dev/null @@ -1,29 +0,0 @@ -0x81 playpause # Play/Pause -0x82 stopcd # Stop -0x83 previoussong # Previous song -0x84 nextsong # Next song -0x85 brightnessdown # Fn+Down arrow Brightness Down -0x86 brightnessup # Fn+Up arrow Brightness Up -0x87 battery # Fn+F3 battery icon -0x88 unknown # Fn+F2 Turn On/Off Wireless - handled in hardware -0x89 ejectclosecd # Fn+F10 Eject CD -0x8A suspend # Fn+F1 hibernate -0x8B switchvideomode # Fn+F8 CRT/LCD (high keycode: "displaytoggle") -0x8C f23 # Fn+Right arrow Auto Brightness -0x8F switchvideomode # Fn+F7 aspect ratio -0x90 previoussong # Front panel previous song -0x91 prog1 # Wifi Catcher (DELL Specific) -0x92 media # MediaDirect button (house icon) -0x93 f23 # FIXME Fn+Left arrow Auto Brightness -0x95 camera # Shutter button Takes a picture if optional camera available -0x97 email # Tablet email button -0x98 f21 # FIXME: Tablet screen rotatation -0x99 nextsong # Front panel next song -0x9A setup # Tablet tools button -0x9B switchvideomode # Display Toggle button -0x9E f21 #touchpad toggle -0xA2 playpause # Front panel play/pause -0xA4 stopcd # Front panel stop -0xED media # MediaDirect button -0xD8 screenlock # FIXME: Tablet lock button -0xD9 f21 # touchpad toggle diff --git a/src/udev/src/keymap/keymaps/dell-latitude-xt2 b/src/udev/src/keymap/keymaps/dell-latitude-xt2 deleted file mode 100644 index 39872f559d..0000000000 --- a/src/udev/src/keymap/keymaps/dell-latitude-xt2 +++ /dev/null @@ -1,4 +0,0 @@ -0x9B up # tablet rocker up -0x9E enter # tablet rocker press -0x9F back # tablet back -0xA3 down # tablet rocker down diff --git a/src/udev/src/keymap/keymaps/everex-xt5000 b/src/udev/src/keymap/keymaps/everex-xt5000 deleted file mode 100644 index 4823a832f5..0000000000 --- a/src/udev/src/keymap/keymaps/everex-xt5000 +++ /dev/null @@ -1,7 +0,0 @@ -0x5C media -0x65 f21 # Fn+F5 Touchpad toggle -0x67 prog3 # Fan Speed Control button -0x6F brightnessup -0x7F brightnessdown -0xB2 www -0xEC mail diff --git a/src/udev/src/keymap/keymaps/fujitsu-amilo_li_2732 b/src/udev/src/keymap/keymaps/fujitsu-amilo_li_2732 deleted file mode 100644 index 9b8b36a170..0000000000 --- a/src/udev/src/keymap/keymaps/fujitsu-amilo_li_2732 +++ /dev/null @@ -1,3 +0,0 @@ -0xD9 brightnessdown # Fn+F8 brightness down -0xEF brightnessup # Fn+F9 brightness up -0xA9 switchvideomode # Fn+F10 Cycle between available video outputs diff --git a/src/udev/src/keymap/keymaps/fujitsu-amilo_pa_2548 b/src/udev/src/keymap/keymaps/fujitsu-amilo_pa_2548 deleted file mode 100644 index f7b0c52444..0000000000 --- a/src/udev/src/keymap/keymaps/fujitsu-amilo_pa_2548 +++ /dev/null @@ -1,3 +0,0 @@ -0xE0 volumedown -0xE1 volumeup -0xE5 prog1 diff --git a/src/udev/src/keymap/keymaps/fujitsu-amilo_pro_edition_v3505 b/src/udev/src/keymap/keymaps/fujitsu-amilo_pro_edition_v3505 deleted file mode 100644 index d2e38cbb23..0000000000 --- a/src/udev/src/keymap/keymaps/fujitsu-amilo_pro_edition_v3505 +++ /dev/null @@ -1,4 +0,0 @@ -0xA5 help # Fn-F1 -0xA9 switchvideomode # Fn-F3 -0xD9 brightnessdown # Fn-F8 -0xE0 brightnessup # Fn-F9 diff --git a/src/udev/src/keymap/keymaps/fujitsu-amilo_pro_v3205 b/src/udev/src/keymap/keymaps/fujitsu-amilo_pro_v3205 deleted file mode 100644 index 43e3199d59..0000000000 --- a/src/udev/src/keymap/keymaps/fujitsu-amilo_pro_v3205 +++ /dev/null @@ -1,2 +0,0 @@ -0xF4 f21 # FIXME: silent-mode decrease CPU/GPU clock -0xF7 switchvideomode # Fn+F3 diff --git a/src/udev/src/keymap/keymaps/fujitsu-amilo_si_1520 b/src/udev/src/keymap/keymaps/fujitsu-amilo_si_1520 deleted file mode 100644 index 1419bd9b5e..0000000000 --- a/src/udev/src/keymap/keymaps/fujitsu-amilo_si_1520 +++ /dev/null @@ -1,6 +0,0 @@ -0xE1 wlan -0xF3 wlan -0xEE brightnessdown -0xE0 brightnessup -0xE2 bluetooth -0xF7 video diff --git a/src/udev/src/keymap/keymaps/fujitsu-esprimo_mobile_v5 b/src/udev/src/keymap/keymaps/fujitsu-esprimo_mobile_v5 deleted file mode 100644 index d3d056b366..0000000000 --- a/src/udev/src/keymap/keymaps/fujitsu-esprimo_mobile_v5 +++ /dev/null @@ -1,4 +0,0 @@ -0xA9 switchvideomode -0xD9 brightnessdown -0xDF sleep -0xEF brightnessup diff --git a/src/udev/src/keymap/keymaps/fujitsu-esprimo_mobile_v6 b/src/udev/src/keymap/keymaps/fujitsu-esprimo_mobile_v6 deleted file mode 100644 index 52c70c50cb..0000000000 --- a/src/udev/src/keymap/keymaps/fujitsu-esprimo_mobile_v6 +++ /dev/null @@ -1,2 +0,0 @@ -0xCE brightnessup -0xEF brightnessdown diff --git a/src/udev/src/keymap/keymaps/genius-slimstar-320 b/src/udev/src/keymap/keymaps/genius-slimstar-320 deleted file mode 100644 index d0a3656dd8..0000000000 --- a/src/udev/src/keymap/keymaps/genius-slimstar-320 +++ /dev/null @@ -1,35 +0,0 @@ -# Genius SlimStar 320 -# -# Only buttons which are not properly mapped yet are configured below - -# "Scroll wheel", a circular up/down/left/right button. Aimed for scolling, -# but since there are no scrollleft/scrollright, let's map to back/forward. -0x900f0 scrollup -0x900f1 scrolldown -0x900f3 back -0x900f2 forward - -# Multimedia buttons, left side (from left to right) -# [W] -0x900f5 wordprocessor -# [Ex] -0x900f6 spreadsheet -# [P] -0x900f4 presentation -# Other five (calculator, playpause, stop, mute and eject) are OK - -# Right side, from left to right -# [e] -0xc0223 www -# "man" -0x900f7 chat -# "Y" -0x900fb prog1 -# [X] -0x900f8 close -# "picture" -0x900f9 graphicseditor -# "two windows" -0x900fd scale -# "lock" -0x900fc screenlock diff --git a/src/udev/src/keymap/keymaps/hewlett-packard b/src/udev/src/keymap/keymaps/hewlett-packard deleted file mode 100644 index 4461fa2ce5..0000000000 --- a/src/udev/src/keymap/keymaps/hewlett-packard +++ /dev/null @@ -1,12 +0,0 @@ -0x81 fn_esc -0x89 battery # FnF8 -0x8A screenlock # FnF6 -0x8B camera -0x8C media # music -0x8E dvd -0xB1 help -0xB3 f23 # FIXME: Auto brightness -0xD7 wlan -0x92 brightnessdown # FnF7 (FnF9 on 6730b) -0x97 brightnessup # FnF8 (FnF10 on 6730b) -0xEE switchvideomode # FnF4 diff --git a/src/udev/src/keymap/keymaps/hewlett-packard-2510p_2530p b/src/udev/src/keymap/keymaps/hewlett-packard-2510p_2530p deleted file mode 100644 index 41ad2e9b5a..0000000000 --- a/src/udev/src/keymap/keymaps/hewlett-packard-2510p_2530p +++ /dev/null @@ -1,2 +0,0 @@ -0xD8 f23 # touchpad off -0xD9 f22 # touchpad on diff --git a/src/udev/src/keymap/keymaps/hewlett-packard-compaq_elitebook b/src/udev/src/keymap/keymaps/hewlett-packard-compaq_elitebook deleted file mode 100644 index 42007c5483..0000000000 --- a/src/udev/src/keymap/keymaps/hewlett-packard-compaq_elitebook +++ /dev/null @@ -1,2 +0,0 @@ -0x88 presentation -0xD9 help # I key (high keycode: "info") diff --git a/src/udev/src/keymap/keymaps/hewlett-packard-pavilion b/src/udev/src/keymap/keymaps/hewlett-packard-pavilion deleted file mode 100644 index 3d3cefc8e6..0000000000 --- a/src/udev/src/keymap/keymaps/hewlett-packard-pavilion +++ /dev/null @@ -1,3 +0,0 @@ -0x88 media # FIXME: quick play -0xD8 f23 # touchpad off -0xD9 f22 # touchpad on diff --git a/src/udev/src/keymap/keymaps/hewlett-packard-presario-2100 b/src/udev/src/keymap/keymaps/hewlett-packard-presario-2100 deleted file mode 100644 index 1df39dcbd2..0000000000 --- a/src/udev/src/keymap/keymaps/hewlett-packard-presario-2100 +++ /dev/null @@ -1,3 +0,0 @@ -0xF0 help -0xF1 screenlock -0xF3 search diff --git a/src/udev/src/keymap/keymaps/hewlett-packard-tablet b/src/udev/src/keymap/keymaps/hewlett-packard-tablet deleted file mode 100644 index d19005ab90..0000000000 --- a/src/udev/src/keymap/keymaps/hewlett-packard-tablet +++ /dev/null @@ -1,6 +0,0 @@ -0x82 prog2 # Funny Key -0x83 prog1 # Q -0x84 tab -0x85 esc -0x86 pageup -0x87 pagedown diff --git a/src/udev/src/keymap/keymaps/hewlett-packard-tx2 b/src/udev/src/keymap/keymaps/hewlett-packard-tx2 deleted file mode 100644 index 36a690fcf6..0000000000 --- a/src/udev/src/keymap/keymaps/hewlett-packard-tx2 +++ /dev/null @@ -1,3 +0,0 @@ -0xC2 media -0xD8 f23 # Toggle touchpad button on tx2 (OFF) -0xD9 f22 # Toggle touchpad button on tx2 (ON) diff --git a/src/udev/src/keymap/keymaps/ibm-thinkpad-usb-keyboard-trackpoint b/src/udev/src/keymap/keymaps/ibm-thinkpad-usb-keyboard-trackpoint deleted file mode 100644 index 027e50bf88..0000000000 --- a/src/udev/src/keymap/keymaps/ibm-thinkpad-usb-keyboard-trackpoint +++ /dev/null @@ -1,7 +0,0 @@ -0x900f0 screenlock -0x900f1 wlan -0x900f2 switchvideomode -0x900f3 suspend -0x900f4 brightnessup -0x900f5 brightnessdown -0x900f8 zoom diff --git a/src/udev/src/keymap/keymaps/inventec-symphony_6.0_7.0 b/src/udev/src/keymap/keymaps/inventec-symphony_6.0_7.0 deleted file mode 100644 index 4a8b4ba5a7..0000000000 --- a/src/udev/src/keymap/keymaps/inventec-symphony_6.0_7.0 +++ /dev/null @@ -1,2 +0,0 @@ -0xF3 prog2 -0xF4 prog1 diff --git a/src/udev/src/keymap/keymaps/lenovo-3000 b/src/udev/src/keymap/keymaps/lenovo-3000 deleted file mode 100644 index 5bd165654a..0000000000 --- a/src/udev/src/keymap/keymaps/lenovo-3000 +++ /dev/null @@ -1,5 +0,0 @@ -0x8B switchvideomode # Fn+F7 video -0x96 wlan # Fn+F5 wireless -0x97 sleep # Fn+F4 suspend -0x98 suspend # Fn+F12 hibernate -0xB4 prog1 # Lenovo Care diff --git a/src/udev/src/keymap/keymaps/lenovo-ideapad b/src/udev/src/keymap/keymaps/lenovo-ideapad deleted file mode 100644 index fc339839f2..0000000000 --- a/src/udev/src/keymap/keymaps/lenovo-ideapad +++ /dev/null @@ -1,8 +0,0 @@ -# Key codes observed on S10-3, assumed valid on other IdeaPad models -0x81 rfkill # does nothing in BIOS -0x83 display_off # BIOS toggles screen state -0xB9 brightnessup # does nothing in BIOS -0xBA brightnessdown # does nothing in BIOS -0xF1 camera # BIOS toggles camera power -0xf2 f21 # touchpad toggle (key alternately emits f2 and f3) -0xf3 f21 diff --git a/src/udev/src/keymap/keymaps/lenovo-thinkpad-usb-keyboard-trackpoint b/src/udev/src/keymap/keymaps/lenovo-thinkpad-usb-keyboard-trackpoint deleted file mode 100644 index 47e8846a68..0000000000 --- a/src/udev/src/keymap/keymaps/lenovo-thinkpad-usb-keyboard-trackpoint +++ /dev/null @@ -1,13 +0,0 @@ -0x90012 screenlock # Fn+F2 -0x90013 battery # Fn+F3 -0x90014 wlan # Fn+F5 -0x90016 switchvideomode # Fn+F7 -0x90017 f21 # Fn+F8 touchpadtoggle -0x90019 suspend # Fn+F12 -0x9001A brightnessup # Fn+Home -0x9001B brightnessdown # Fn+End -0x9001D zoom # Fn+Space -0x90011 prog1 # Thinkvantage button - -0x90015 camera # Fn+F6 headset/camera VoIP key ?? -0x90010 micmute # Microphone mute button diff --git a/src/udev/src/keymap/keymaps/lenovo-thinkpad_x200_tablet b/src/udev/src/keymap/keymaps/lenovo-thinkpad_x200_tablet deleted file mode 100644 index 31ea3b2c70..0000000000 --- a/src/udev/src/keymap/keymaps/lenovo-thinkpad_x200_tablet +++ /dev/null @@ -1,6 +0,0 @@ -0x5D menu -0x63 fn -0x66 screenlock -0x67 cyclewindows # bezel circular arrow -0x68 setup # bezel setup / menu -0x6c direction # rotate screen diff --git a/src/udev/src/keymap/keymaps/lenovo-thinkpad_x6_tablet b/src/udev/src/keymap/keymaps/lenovo-thinkpad_x6_tablet deleted file mode 100644 index 6fd16b5662..0000000000 --- a/src/udev/src/keymap/keymaps/lenovo-thinkpad_x6_tablet +++ /dev/null @@ -1,8 +0,0 @@ -0x6C f21 # rotate -0x68 screenlock # screenlock -0x6B esc # escape -0x6D right # right on d-pad -0x6E left # left on d-pad -0x71 up # up on d-pad -0x6F down # down on d-pad -0x69 enter # enter on d-pad diff --git a/src/udev/src/keymap/keymaps/lg-x110 b/src/udev/src/keymap/keymaps/lg-x110 deleted file mode 100644 index ba08cba3fe..0000000000 --- a/src/udev/src/keymap/keymaps/lg-x110 +++ /dev/null @@ -1,12 +0,0 @@ -0xA0 mute # Fn-F9 -0xAE volumedown # Fn-Left -0xAF search # Fn-F3 -0xB0 volumeup # Fn-Right -0xB1 battery # Fn-F10 Info -0xB3 suspend # Fn-F12 -0xDF sleep # Fn-F4 -# 0xE2 bluetooth # satellite dish2 -0xE4 f21 # Fn-F5 Touchpad disable -0xF6 wlan # Fn-F6 -0xF7 reserved # brightnessdown # Fn-Down -0xF8 reserved # brightnessup # Fn-Up diff --git a/src/udev/src/keymap/keymaps/logitech-wave b/src/udev/src/keymap/keymaps/logitech-wave deleted file mode 100644 index caa5d5d310..0000000000 --- a/src/udev/src/keymap/keymaps/logitech-wave +++ /dev/null @@ -1,16 +0,0 @@ -0x9001C scale #expo -0x9001F zoomout #zoom out -0x90020 zoomin #zoom in -0x9003D prog1 #gadget -0x90005 camera #camera -0x90018 media #media center -0x90041 wordprocessor #fn+f1 (word) -0x90042 spreadsheet #fn+f2 (excel) -0x90043 calendar #fn+f3 (calendar) -0x90044 prog2 #fn+f4 (program a) -0x90045 prog3 #fn+f5 (program b) -0x90046 prog4 #fn+f6 (program c) -0x90048 messenger #fn+f8 (msn messenger) -0x9002D find #fn+f10 (search www) -0x9004B search #fn+f11 (search pc) -0x9004C ejectclosecd #fn+f12 (eject) diff --git a/src/udev/src/keymap/keymaps/logitech-wave-cordless b/src/udev/src/keymap/keymaps/logitech-wave-cordless deleted file mode 100644 index a10dad5e4d..0000000000 --- a/src/udev/src/keymap/keymaps/logitech-wave-cordless +++ /dev/null @@ -1,15 +0,0 @@ -0xD4 zoomin -0xCC zoomout -0xC0183 media -0xC1005 camera -0xC101F zoomout -0xC1020 zoomin -0xC1041 wordprocessor -0xC1042 spreadsheet -0xC1043 calendar -0xC1044 prog2 #fn+f4 (program a) -0xC1045 prog3 #fn+f5 (program b) -0xC1046 prog4 #fn+f6 (program c) -0xC1048 messenger -0xC104A find #fn+f10 (search www) -0xC104C ejectclosecd diff --git a/src/udev/src/keymap/keymaps/logitech-wave-pro-cordless b/src/udev/src/keymap/keymaps/logitech-wave-pro-cordless deleted file mode 100644 index e7aa02206c..0000000000 --- a/src/udev/src/keymap/keymaps/logitech-wave-pro-cordless +++ /dev/null @@ -1,12 +0,0 @@ -0xC01B6 camera -0xC0183 media -0xC0184 wordprocessor -0xC0186 spreadsheet -0xC018E calendar -0xC0223 homepage -0xC01BC messenger -0xC018A mail -0xC0221 search -0xC00B8 ejectcd -0xC022D zoomin -0xC022E zoomout diff --git a/src/udev/src/keymap/keymaps/maxdata-pro_7000 b/src/udev/src/keymap/keymaps/maxdata-pro_7000 deleted file mode 100644 index c0e4f77af4..0000000000 --- a/src/udev/src/keymap/keymaps/maxdata-pro_7000 +++ /dev/null @@ -1,9 +0,0 @@ -0x97 prog2 -0x9F prog1 -0xA0 mute # Fn-F5 -0x82 www -0xEC email -0xAE volumedown # Fn-Down -0xB0 volumeup # Fn-Up -0xDF suspend # Fn+F2 -0xF5 help diff --git a/src/udev/src/keymap/keymaps/medion-fid2060 b/src/udev/src/keymap/keymaps/medion-fid2060 deleted file mode 100644 index 5a76c76799..0000000000 --- a/src/udev/src/keymap/keymaps/medion-fid2060 +++ /dev/null @@ -1,2 +0,0 @@ -0x6B channeldown # Thottle Down -0x6D channelup # Thottle Up diff --git a/src/udev/src/keymap/keymaps/medionnb-a555 b/src/udev/src/keymap/keymaps/medionnb-a555 deleted file mode 100644 index c3b5dfa60b..0000000000 --- a/src/udev/src/keymap/keymaps/medionnb-a555 +++ /dev/null @@ -1,4 +0,0 @@ -0x63 www # N button -0x66 prog1 # link 1 button -0x67 email # envelope button -0x69 prog2 # link 2 button diff --git a/src/udev/src/keymap/keymaps/micro-star b/src/udev/src/keymap/keymaps/micro-star deleted file mode 100644 index 4a438698ed..0000000000 --- a/src/udev/src/keymap/keymaps/micro-star +++ /dev/null @@ -1,13 +0,0 @@ -0xA0 mute # Fn-F9 -0xAE volumedown # Fn-F7 -0xB0 volumeup # Fn-F8 -0xB2 www # e button -0xDF sleep # Fn-F12 -0xE2 bluetooth # satellite dish2 -0xE4 f21 # Fn-F3 Touchpad disable -0xEC email # envelope button -0xEE camera # Fn-F6 camera disable -0xF6 wlan # satellite dish1 -0xF7 brightnessdown # Fn-F4 -0xF8 brightnessup # Fn-F5 -0xF9 search diff --git a/src/udev/src/keymap/keymaps/module-asus-w3j b/src/udev/src/keymap/keymaps/module-asus-w3j deleted file mode 100644 index 773e0b3e82..0000000000 --- a/src/udev/src/keymap/keymaps/module-asus-w3j +++ /dev/null @@ -1,11 +0,0 @@ -0x41 nextsong -0x45 playpause -0x43 stopcd -0x40 previoussong -0x4C ejectclosecd -0x32 mute -0x31 volumedown -0x30 volumeup -0x5D wlan -0x7E bluetooth -0x8A media # high keycode: "tv" diff --git a/src/udev/src/keymap/keymaps/module-ibm b/src/udev/src/keymap/keymaps/module-ibm deleted file mode 100644 index a92dfa2506..0000000000 --- a/src/udev/src/keymap/keymaps/module-ibm +++ /dev/null @@ -1,16 +0,0 @@ -0x01 battery # Fn+F2 -0x02 screenlock # Fn+F3 -0x03 sleep # Fn+F4 -0x04 wlan # Fn+F5 -0x06 switchvideomode # Fn+F7 -0x07 zoom # Fn+F8 screen expand -0x08 f24 # Fn+F9 undock -0x0B suspend # Fn+F12 -0x0F brightnessup # Fn+Home -0x10 brightnessdown # Fn+End -0x11 kbdillumtoggle # Fn+PgUp - ThinkLight -0x13 zoom # Fn+Space -0x14 volumeup -0x15 volumedown -0x16 mute -0x17 prog1 # ThinkPad/ThinkVantage button (high keycode: "vendor") diff --git a/src/udev/src/keymap/keymaps/module-lenovo b/src/udev/src/keymap/keymaps/module-lenovo deleted file mode 100644 index 8e38883091..0000000000 --- a/src/udev/src/keymap/keymaps/module-lenovo +++ /dev/null @@ -1,17 +0,0 @@ -0x1 screenlock # Fn+F2 -0x2 battery # Fn+F3 -0x3 sleep # Fn+F4 -0x4 wlan # Fn+F5 -0x6 switchvideomode # Fn+F7 -0x7 f21 # Fn+F8 touchpadtoggle -0x8 f24 # Fn+F9 undock -0xB suspend # Fn+F12 -0xF brightnessup # Fn+Home -0x10 brightnessdown # Fn+End -0x11 kbdillumtoggle # Fn+PgUp - ThinkLight -0x13 zoom # Fn+Space -0x14 volumeup -0x15 volumedown -0x16 mute -0x17 prog1 # ThinkPad/ThinkVantage button (high keycode: "vendor") -0x1A micmute # Microphone mute diff --git a/src/udev/src/keymap/keymaps/module-sony b/src/udev/src/keymap/keymaps/module-sony deleted file mode 100644 index 7c000131d1..0000000000 --- a/src/udev/src/keymap/keymaps/module-sony +++ /dev/null @@ -1,8 +0,0 @@ -0x06 mute # Fn+F2 -0x07 volumedown # Fn+F3 -0x08 volumeup # Fn+F4 -0x09 brightnessdown # Fn+F5 -0x0A brightnessup # Fn+F6 -0x0B switchvideomode # Fn+F7 -0x0E zoom # Fn+F10 -0x10 suspend # Fn+F12 diff --git a/src/udev/src/keymap/keymaps/module-sony-old b/src/udev/src/keymap/keymaps/module-sony-old deleted file mode 100644 index 596a34258a..0000000000 --- a/src/udev/src/keymap/keymaps/module-sony-old +++ /dev/null @@ -1,2 +0,0 @@ -0x06 battery -0x07 mute diff --git a/src/udev/src/keymap/keymaps/module-sony-vgn b/src/udev/src/keymap/keymaps/module-sony-vgn deleted file mode 100644 index c8ba001516..0000000000 --- a/src/udev/src/keymap/keymaps/module-sony-vgn +++ /dev/null @@ -1,8 +0,0 @@ -0x00 brightnessdown # Fn+F5 -0x10 brightnessup # Fn+F6 -0x11 switchvideomode # Fn+F7 -0x12 zoomout -0x14 zoomin -0x15 suspend # Fn+F12 -0x17 prog1 -0x20 media diff --git a/src/udev/src/keymap/keymaps/olpc-xo b/src/udev/src/keymap/keymaps/olpc-xo deleted file mode 100644 index 34434a121d..0000000000 --- a/src/udev/src/keymap/keymaps/olpc-xo +++ /dev/null @@ -1,74 +0,0 @@ -0x59 fn -0x81 fn_esc -0xF9 camera -0xF8 sound # Fn-CAMERA = Mic - - -# Function key mappings, as per -# http://dev.laptop.org/ticket/10213#comment:20 -# -# Unmodified F1-F8 produce F1-F8, so no remap necessary. -# Unmodified F9-F12 control brightness and volume. -0x43 brightnessdown -0x44 brightnessup -0x57 volumedown -0x58 volumeup - -# fn-modified fkeys all produce the unmodified version of the key. -0xBB f1 -0xBC f2 -0xBD f3 -0xBE f4 -0xBF f5 -0xC0 f6 -0xC1 f7 -0xC2 f8 -0xC3 f9 -0xC4 f10 -0xD7 f11 -0xD8 f12 - - -# Using F13-F21 for the .5 F keys right now. -0xF7 f13 -0xF6 f14 -0xF5 f15 -0xF4 f16 -0xF3 f17 -0xF2 f18 -0xF1 f19 -0xF0 f20 -0xEF f21 - -0xEE chat -0xE4 chat # Just mapping Fn-Chat to Chat for now -0xDD menu # Frame -0xDA prog1 # Fn-Frame - -# The FN of some keys is other keys -0xD3 delete -0xD2 insert -0xC9 pageup -0xD1 pagedown -0xC7 home -0xCF end - -# Language key - don't ask what they are doing as KEY_HP -0x73 hp -0x7E hp - -0xDB leftmeta # left grab -0xDC rightmeta # right grab -0x85 rightmeta # Right grab releases on a different scancode -0xD6 kbdillumtoggle # Fn-space -0x69 switchvideomode # Brightness key - -# Game keys -0x65 kp8 # up -0x66 kp2 # down -0x67 kp4 # left -0x68 kp6 # right -0xE5 kp9 # pgup -0xE6 kp3 # pgdn -0xE7 kp7 # home -0xE8 kp1 # end diff --git a/src/udev/src/keymap/keymaps/onkyo b/src/udev/src/keymap/keymaps/onkyo deleted file mode 100644 index ee864ade4d..0000000000 --- a/src/udev/src/keymap/keymaps/onkyo +++ /dev/null @@ -1,14 +0,0 @@ -0xA0 mute # Fn+D -0xAE volumedown # Fn+F -0xB0 volumeup # Fn+G -0xDF sleep # Fn+W -0xE0 bluetooth # Fn+H -0xE2 cyclewindows # Fn+Esc -0xEE battery # Fn+Q -0xF0 media # Fn+R -0xF5 switchvideomode # Fn+E -0xF6 camera # Fn+T -0xF7 f21 # Fn+Y (touchpad toggle) -0xF8 brightnessup # Fn+S -0xF9 brightnessdown # Fn+A -0xFB wlan # Fn+J diff --git a/src/udev/src/keymap/keymaps/oqo-model2 b/src/udev/src/keymap/keymaps/oqo-model2 deleted file mode 100644 index b7f4851abe..0000000000 --- a/src/udev/src/keymap/keymaps/oqo-model2 +++ /dev/null @@ -1,5 +0,0 @@ -0x8E wlan -0xF0 switchvideomode -0xF1 mute -0xF2 volumedown -0xF3 volumeup diff --git a/src/udev/src/keymap/keymaps/samsung-90x3a b/src/udev/src/keymap/keymaps/samsung-90x3a deleted file mode 100644 index 8b65eb6d03..0000000000 --- a/src/udev/src/keymap/keymaps/samsung-90x3a +++ /dev/null @@ -1,5 +0,0 @@ -0x96 kbdillumup         # Fn+F8 keyboard backlit up -0x97 kbdillumdown       # Fn+F7 keyboard backlit down -0xD5 wlan               # Fn+F12 wifi on/off -0xCE prog1              # Fn+F1 performance mode -0x8D prog2              # Fn+F6 battery life extender diff --git a/src/udev/src/keymap/keymaps/samsung-other b/src/udev/src/keymap/keymaps/samsung-other deleted file mode 100644 index 3ac0c2f10c..0000000000 --- a/src/udev/src/keymap/keymaps/samsung-other +++ /dev/null @@ -1,14 +0,0 @@ -0x74 prog1 # User key -0x75 www -0x78 mail -0x82 switchvideomode # Fn+F4 CRT/LCD (high keycode: "displaytoggle") -0x83 battery # Fn+F2 -0x84 prog1 # Fn+F5 backlight on/off -0x86 wlan # Fn+F9 -0x88 brightnessup # Fn-Up -0x89 brightnessdown # Fn-Down -0xB1 prog2 # Fn+F7 run Samsung Magic Doctor (keypressed event is generated twice) -0xB3 prog3 # Fn+F8 switch power mode (battery/dynamic/performance) -0xB4 wlan # Fn+F9 (X60P) -0xF7 f22 # Fn+F10 Touchpad on -0xF9 f23 # Fn+F10 Touchpad off diff --git a/src/udev/src/keymap/keymaps/samsung-sq1us b/src/udev/src/keymap/keymaps/samsung-sq1us deleted file mode 100644 index ea2141ef84..0000000000 --- a/src/udev/src/keymap/keymaps/samsung-sq1us +++ /dev/null @@ -1,7 +0,0 @@ -0xD4 menu -0xD8 f1 -0xD9 f10 -0xD6 f3 -0xD7 f9 -0xE4 f5 -0xEE f11 diff --git a/src/udev/src/keymap/keymaps/samsung-sx20s b/src/udev/src/keymap/keymaps/samsung-sx20s deleted file mode 100644 index 9d954ee415..0000000000 --- a/src/udev/src/keymap/keymaps/samsung-sx20s +++ /dev/null @@ -1,4 +0,0 @@ -0x74 mute -0x75 mute -0x77 f22 # Touchpad on -0x79 f23 # Touchpad off diff --git a/src/udev/src/keymap/keymaps/toshiba-satellite_a100 b/src/udev/src/keymap/keymaps/toshiba-satellite_a100 deleted file mode 100644 index 22007be71b..0000000000 --- a/src/udev/src/keymap/keymaps/toshiba-satellite_a100 +++ /dev/null @@ -1,2 +0,0 @@ -0xA4 stopcd -0xB2 www diff --git a/src/udev/src/keymap/keymaps/toshiba-satellite_a110 b/src/udev/src/keymap/keymaps/toshiba-satellite_a110 deleted file mode 100644 index 1429409351..0000000000 --- a/src/udev/src/keymap/keymaps/toshiba-satellite_a110 +++ /dev/null @@ -1,10 +0,0 @@ -0x92 stop -0x93 www -0x94 media -0x9E f22 # Touchpad on -0x9F f23 # Touchpad off -0xB9 nextsong -0xD9 brightnessup -0xEE screenlock -0xF4 previoussong -0xF7 playpause diff --git a/src/udev/src/keymap/keymaps/toshiba-satellite_m30x b/src/udev/src/keymap/keymaps/toshiba-satellite_m30x deleted file mode 100644 index ae8e34941b..0000000000 --- a/src/udev/src/keymap/keymaps/toshiba-satellite_m30x +++ /dev/null @@ -1,6 +0,0 @@ -0xef brightnessdown -0xd9 brightnessup -0xee screenlock -0x93 media -0x9e f22 #touchpad_enable -0x9f f23 #touchpad_disable diff --git a/src/udev/src/keymap/keymaps/zepto-znote b/src/udev/src/keymap/keymaps/zepto-znote deleted file mode 100644 index cf72fda47b..0000000000 --- a/src/udev/src/keymap/keymaps/zepto-znote +++ /dev/null @@ -1,11 +0,0 @@ -0x93 switchvideomode # Fn+F3 Toggle Video Output -0x95 brightnessdown # Fn+F4 Brightness Down -0x91 brightnessup # Fn+F5 Brightness Up -0xA5 f23 # Fn+F6 Disable Touchpad -0xA6 f22 # Fn+F6 Enable Touchpad -0xA7 bluetooth # Fn+F10 Enable Bluetooth -0XA9 bluetooth # Fn+F10 Disable Bluetooth -0xF1 wlan # RF Switch Off -0xF2 wlan # RF Switch On -0xF4 prog1 # P1 Button -0xF3 prog2 # P2 Button diff --git a/src/udev/src/libudev-device-private.c b/src/udev/src/libudev-device-private.c deleted file mode 100644 index 13fdb8eb57..0000000000 --- a/src/udev/src/libudev-device-private.c +++ /dev/null @@ -1,185 +0,0 @@ -/* - * libudev - interface to udev device information - * - * Copyright (C) 2008-2010 Kay Sievers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libudev.h" -#include "libudev-private.h" - -static void udev_device_tag(struct udev_device *dev, const char *tag, bool add) -{ - const char *id; - struct udev *udev = udev_device_get_udev(dev); - char filename[UTIL_PATH_SIZE]; - - id = udev_device_get_id_filename(dev); - if (id == NULL) - return; - util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/tags/", tag, "/", id, NULL); - - if (add) { - int fd; - - util_create_path(udev, filename); - fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444); - if (fd >= 0) - close(fd); - } else { - unlink(filename); - } -} - -int udev_device_tag_index(struct udev_device *dev, struct udev_device *dev_old, bool add) -{ - struct udev_list_entry *list_entry; - bool found; - - if (add && dev_old != NULL) { - /* delete possible left-over tags */ - udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(dev_old)) { - const char *tag_old = udev_list_entry_get_name(list_entry); - struct udev_list_entry *list_entry_current; - - found = false; - udev_list_entry_foreach(list_entry_current, udev_device_get_tags_list_entry(dev)) { - const char *tag = udev_list_entry_get_name(list_entry_current); - - if (strcmp(tag, tag_old) == 0) { - found = true; - break; - } - } - if (!found) - udev_device_tag(dev_old, tag_old, false); - } - } - - udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(dev)) - udev_device_tag(dev, udev_list_entry_get_name(list_entry), add); - - return 0; -} - -static bool device_has_info(struct udev_device *udev_device) -{ - struct udev_list_entry *list_entry; - - if (udev_device_get_devlinks_list_entry(udev_device) != NULL) - return true; - if (udev_device_get_devlink_priority(udev_device) != 0) - return true; - udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev_device)) - if (udev_list_entry_get_num(list_entry)) - return true; - if (udev_device_get_tags_list_entry(udev_device) != NULL) - return true; - if (udev_device_get_watch_handle(udev_device) >= 0) - return true; - return false; -} - -int udev_device_update_db(struct udev_device *udev_device) -{ - bool has_info; - const char *id; - struct udev *udev = udev_device_get_udev(udev_device); - char filename[UTIL_PATH_SIZE]; - char filename_tmp[UTIL_PATH_SIZE]; - FILE *f; - - id = udev_device_get_id_filename(udev_device); - if (id == NULL) - return -1; - - has_info = device_has_info(udev_device); - util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/data/", id, NULL); - - /* do not store anything for otherwise empty devices */ - if (!has_info && - major(udev_device_get_devnum(udev_device)) == 0 && - udev_device_get_ifindex(udev_device) == 0) { - unlink(filename); - return 0; - } - - /* write a database file */ - util_strscpyl(filename_tmp, sizeof(filename_tmp), filename, ".tmp", NULL); - util_create_path(udev, filename_tmp); - f = fopen(filename_tmp, "we"); - if (f == NULL) { - err(udev, "unable to create temporary db file '%s': %m\n", filename_tmp); - return -1; - } - - /* - * set 'sticky' bit to indicate that we should not clean the - * database when we transition from initramfs to the real root - */ - if (udev_device_get_db_persist(udev_device)) - fchmod(fileno(f), 01644); - - if (has_info) { - struct udev_list_entry *list_entry; - - if (major(udev_device_get_devnum(udev_device)) > 0) { - size_t devlen = strlen(udev_get_dev_path(udev))+1; - - udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(udev_device)) - fprintf(f, "S:%s\n", &udev_list_entry_get_name(list_entry)[devlen]); - if (udev_device_get_devlink_priority(udev_device) != 0) - fprintf(f, "L:%i\n", udev_device_get_devlink_priority(udev_device)); - if (udev_device_get_watch_handle(udev_device) >= 0) - fprintf(f, "W:%i\n", udev_device_get_watch_handle(udev_device)); - } - - if (udev_device_get_usec_initialized(udev_device) > 0) - fprintf(f, "I:%llu\n", udev_device_get_usec_initialized(udev_device)); - - udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev_device)) { - if (!udev_list_entry_get_num(list_entry)) - continue; - fprintf(f, "E:%s=%s\n", - udev_list_entry_get_name(list_entry), - udev_list_entry_get_value(list_entry)); - } - - udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(udev_device)) - fprintf(f, "G:%s\n", udev_list_entry_get_name(list_entry)); - } - - fclose(f); - rename(filename_tmp, filename); - info(udev, "created %s file '%s' for '%s'\n", has_info ? "db" : "empty", - filename, udev_device_get_devpath(udev_device)); - return 0; -} - -int udev_device_delete_db(struct udev_device *udev_device) -{ - const char *id; - struct udev *udev = udev_device_get_udev(udev_device); - char filename[UTIL_PATH_SIZE]; - - id = udev_device_get_id_filename(udev_device); - if (id == NULL) - return -1; - util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/data/", id, NULL); - unlink(filename); - return 0; -} diff --git a/src/udev/src/libudev-device.c b/src/udev/src/libudev-device.c deleted file mode 100644 index 10f28b8cd5..0000000000 --- a/src/udev/src/libudev-device.c +++ /dev/null @@ -1,1744 +0,0 @@ -/* - * libudev - interface to udev device information - * - * Copyright (C) 2008-2010 Kay Sievers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libudev.h" -#include "libudev-private.h" - -/** - * SECTION:libudev-device - * @short_description: kernel sys devices - * - * Representation of kernel sys devices. Devices are uniquely identified - * by their syspath, every device has exactly one path in the kernel sys - * filesystem. Devices usually belong to a kernel subsystem, and and have - * a unique name inside that subsystem. - */ - -/** - * udev_device: - * - * Opaque object representing one kernel sys device. - */ -struct udev_device { - struct udev *udev; - struct udev_device *parent_device; - char *syspath; - const char *devpath; - char *sysname; - const char *sysnum; - char *devnode; - mode_t devnode_mode; - char *subsystem; - char *devtype; - char *driver; - char *action; - char *devpath_old; - char *id_filename; - char **envp; - char *monitor_buf; - size_t monitor_buf_len; - struct udev_list devlinks_list; - struct udev_list properties_list; - struct udev_list sysattr_value_list; - struct udev_list sysattr_list; - struct udev_list tags_list; - unsigned long long int seqnum; - unsigned long long int usec_initialized; - int devlink_priority; - int refcount; - dev_t devnum; - int ifindex; - int watch_handle; - int maj, min; - bool parent_set; - bool subsystem_set; - bool devtype_set; - bool devlinks_uptodate; - bool envp_uptodate; - bool tags_uptodate; - bool driver_set; - bool info_loaded; - bool db_loaded; - bool uevent_loaded; - bool is_initialized; - bool sysattr_list_read; - bool db_persist; -}; - -/** - * udev_device_get_seqnum: - * @udev_device: udev device - * - * This is only valid if the device was received through a monitor. Devices read from - * sys do not have a sequence number. - * - * Returns: the kernel event sequence number, or 0 if there is no sequence number available. - **/ -UDEV_EXPORT unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device) -{ - if (udev_device == NULL) - return 0; - return udev_device->seqnum; -} - -static int udev_device_set_seqnum(struct udev_device *udev_device, unsigned long long int seqnum) -{ - char num[32]; - - udev_device->seqnum = seqnum; - snprintf(num, sizeof(num), "%llu", seqnum); - udev_device_add_property(udev_device, "SEQNUM", num); - return 0; -} - -int udev_device_get_ifindex(struct udev_device *udev_device) -{ - if (!udev_device->info_loaded) - udev_device_read_uevent_file(udev_device); - return udev_device->ifindex; -} - -static int udev_device_set_ifindex(struct udev_device *udev_device, int ifindex) -{ - char num[32]; - - udev_device->ifindex = ifindex; - snprintf(num, sizeof(num), "%u", ifindex); - udev_device_add_property(udev_device, "IFINDEX", num); - return 0; -} - -/** - * udev_device_get_devnum: - * @udev_device: udev device - * - * Returns: the device major/minor number. - **/ -UDEV_EXPORT dev_t udev_device_get_devnum(struct udev_device *udev_device) -{ - if (udev_device == NULL) - return makedev(0, 0); - if (!udev_device->info_loaded) - udev_device_read_uevent_file(udev_device); - return udev_device->devnum; -} - -static int udev_device_set_devnum(struct udev_device *udev_device, dev_t devnum) -{ - char num[32]; - - udev_device->devnum = devnum; - - snprintf(num, sizeof(num), "%u", major(devnum)); - udev_device_add_property(udev_device, "MAJOR", num); - snprintf(num, sizeof(num), "%u", minor(devnum)); - udev_device_add_property(udev_device, "MINOR", num); - return 0; -} - -const char *udev_device_get_devpath_old(struct udev_device *udev_device) -{ - return udev_device->devpath_old; -} - -static int udev_device_set_devpath_old(struct udev_device *udev_device, const char *devpath_old) -{ - const char *pos; - - free(udev_device->devpath_old); - udev_device->devpath_old = strdup(devpath_old); - if (udev_device->devpath_old == NULL) - return -ENOMEM; - udev_device_add_property(udev_device, "DEVPATH_OLD", udev_device->devpath_old); - - pos = strrchr(udev_device->devpath_old, '/'); - if (pos == NULL) - return -EINVAL; - return 0; -} - -/** - * udev_device_get_driver: - * @udev_device: udev device - * - * Returns: the driver string, or #NULL if there is no driver attached. - **/ -UDEV_EXPORT const char *udev_device_get_driver(struct udev_device *udev_device) -{ - char driver[UTIL_NAME_SIZE]; - - if (udev_device == NULL) - return NULL; - if (!udev_device->driver_set) { - udev_device->driver_set = true; - if (util_get_sys_core_link_value(udev_device->udev, "driver", udev_device->syspath, driver, sizeof(driver)) > 0) - udev_device->driver = strdup(driver); - } - return udev_device->driver; -} - -static int udev_device_set_driver(struct udev_device *udev_device, const char *driver) -{ - free(udev_device->driver); - udev_device->driver = strdup(driver); - if (udev_device->driver == NULL) - return -ENOMEM; - udev_device->driver_set = true; - udev_device_add_property(udev_device, "DRIVER", udev_device->driver); - return 0; -} - -/** - * udev_device_get_devtype: - * @udev_device: udev device - * - * Retrieve the devtype string of the udev device. - * - * Returns: the devtype name of the udev device, or #NULL if it can not be determined - **/ -UDEV_EXPORT const char *udev_device_get_devtype(struct udev_device *udev_device) -{ - if (udev_device == NULL) - return NULL; - if (!udev_device->devtype_set) { - udev_device->devtype_set = true; - udev_device_read_uevent_file(udev_device); - } - return udev_device->devtype; -} - -static int udev_device_set_devtype(struct udev_device *udev_device, const char *devtype) -{ - free(udev_device->devtype); - udev_device->devtype = strdup(devtype); - if (udev_device->devtype == NULL) - return -ENOMEM; - udev_device->devtype_set = true; - udev_device_add_property(udev_device, "DEVTYPE", udev_device->devtype); - return 0; -} - -static int udev_device_set_subsystem(struct udev_device *udev_device, const char *subsystem) -{ - free(udev_device->subsystem); - udev_device->subsystem = strdup(subsystem); - if (udev_device->subsystem == NULL) - return -ENOMEM; - udev_device->subsystem_set = true; - udev_device_add_property(udev_device, "SUBSYSTEM", udev_device->subsystem); - return 0; -} - -/** - * udev_device_get_subsystem: - * @udev_device: udev device - * - * Retrieve the subsystem string of the udev device. The string does not - * contain any "/". - * - * Returns: the subsystem name of the udev device, or #NULL if it can not be determined - **/ -UDEV_EXPORT const char *udev_device_get_subsystem(struct udev_device *udev_device) -{ - char subsystem[UTIL_NAME_SIZE]; - - if (udev_device == NULL) - return NULL; - if (!udev_device->subsystem_set) { - udev_device->subsystem_set = true; - /* read "subsystem" link */ - if (util_get_sys_core_link_value(udev_device->udev, "subsystem", udev_device->syspath, subsystem, sizeof(subsystem)) > 0) { - udev_device_set_subsystem(udev_device, subsystem); - return udev_device->subsystem; - } - /* implicit names */ - if (strncmp(udev_device->devpath, "/module/", 8) == 0) { - udev_device_set_subsystem(udev_device, "module"); - return udev_device->subsystem; - } - if (strstr(udev_device->devpath, "/drivers/") != NULL) { - udev_device_set_subsystem(udev_device, "drivers"); - return udev_device->subsystem; - } - if (strncmp(udev_device->devpath, "/subsystem/", 11) == 0 || - strncmp(udev_device->devpath, "/class/", 7) == 0 || - strncmp(udev_device->devpath, "/bus/", 5) == 0) { - udev_device_set_subsystem(udev_device, "subsystem"); - return udev_device->subsystem; - } - } - return udev_device->subsystem; -} - -mode_t udev_device_get_devnode_mode(struct udev_device *udev_device) -{ - if (!udev_device->info_loaded) - udev_device_read_uevent_file(udev_device); - return udev_device->devnode_mode; -} - -static int udev_device_set_devnode_mode(struct udev_device *udev_device, mode_t mode) -{ - char num[32]; - - udev_device->devnode_mode = mode; - snprintf(num, sizeof(num), "%#o", mode); - udev_device_add_property(udev_device, "DEVMODE", num); - return 0; -} - -struct udev_list_entry *udev_device_add_property(struct udev_device *udev_device, const char *key, const char *value) -{ - udev_device->envp_uptodate = false; - if (value == NULL) { - struct udev_list_entry *list_entry; - - list_entry = udev_device_get_properties_list_entry(udev_device); - list_entry = udev_list_entry_get_by_name(list_entry, key); - if (list_entry != NULL) - udev_list_entry_delete(list_entry); - return NULL; - } - return udev_list_entry_add(&udev_device->properties_list, key, value); -} - -static struct udev_list_entry *udev_device_add_property_from_string(struct udev_device *udev_device, const char *property) -{ - char name[UTIL_LINE_SIZE]; - char *val; - - util_strscpy(name, sizeof(name), property); - val = strchr(name, '='); - if (val == NULL) - return NULL; - val[0] = '\0'; - val = &val[1]; - if (val[0] == '\0') - val = NULL; - return udev_device_add_property(udev_device, name, val); -} - -/* - * parse property string, and if needed, update internal values accordingly - * - * udev_device_add_property_from_string_parse_finish() needs to be - * called after adding properties, and its return value checked - * - * udev_device_set_info_loaded() needs to be set, to avoid trying - * to use a device without a DEVPATH set - */ -void udev_device_add_property_from_string_parse(struct udev_device *udev_device, const char *property) -{ - if (strncmp(property, "DEVPATH=", 8) == 0) { - char path[UTIL_PATH_SIZE]; - - util_strscpyl(path, sizeof(path), udev_get_sys_path(udev_device->udev), &property[8], NULL); - udev_device_set_syspath(udev_device, path); - } else if (strncmp(property, "SUBSYSTEM=", 10) == 0) { - udev_device_set_subsystem(udev_device, &property[10]); - } else if (strncmp(property, "DEVTYPE=", 8) == 0) { - udev_device_set_devtype(udev_device, &property[8]); - } else if (strncmp(property, "DEVNAME=", 8) == 0) { - udev_device_set_devnode(udev_device, &property[8]); - } else if (strncmp(property, "DEVLINKS=", 9) == 0) { - char devlinks[UTIL_PATH_SIZE]; - char *slink; - char *next; - - util_strscpy(devlinks, sizeof(devlinks), &property[9]); - slink = devlinks; - next = strchr(slink, ' '); - while (next != NULL) { - next[0] = '\0'; - udev_device_add_devlink(udev_device, slink, 0); - slink = &next[1]; - next = strchr(slink, ' '); - } - if (slink[0] != '\0') - udev_device_add_devlink(udev_device, slink, 0); - } else if (strncmp(property, "TAGS=", 5) == 0) { - char tags[UTIL_PATH_SIZE]; - char *next; - - util_strscpy(tags, sizeof(tags), &property[5]); - next = strchr(tags, ':'); - if (next != NULL) { - next++; - while (next[0] != '\0') { - char *tag; - - tag = next; - next = strchr(tag, ':'); - if (next == NULL) - break; - next[0] = '\0'; - next++; - udev_device_add_tag(udev_device, tag); - } - } - } else if (strncmp(property, "USEC_INITIALIZED=", 19) == 0) { - udev_device_set_usec_initialized(udev_device, strtoull(&property[19], NULL, 10)); - } else if (strncmp(property, "DRIVER=", 7) == 0) { - udev_device_set_driver(udev_device, &property[7]); - } else if (strncmp(property, "ACTION=", 7) == 0) { - udev_device_set_action(udev_device, &property[7]); - } else if (strncmp(property, "MAJOR=", 6) == 0) { - udev_device->maj = strtoull(&property[6], NULL, 10); - } else if (strncmp(property, "MINOR=", 6) == 0) { - udev_device->min = strtoull(&property[6], NULL, 10); - } else if (strncmp(property, "DEVPATH_OLD=", 12) == 0) { - udev_device_set_devpath_old(udev_device, &property[12]); - } else if (strncmp(property, "SEQNUM=", 7) == 0) { - udev_device_set_seqnum(udev_device, strtoull(&property[7], NULL, 10)); - } else if (strncmp(property, "IFINDEX=", 8) == 0) { - udev_device_set_ifindex(udev_device, strtoull(&property[8], NULL, 10)); - } else if (strncmp(property, "DEVMODE=", 8) == 0) { - udev_device_set_devnode_mode(udev_device, strtoul(&property[8], NULL, 8)); - } else { - udev_device_add_property_from_string(udev_device, property); - } -} - -int udev_device_add_property_from_string_parse_finish(struct udev_device *udev_device) -{ - if (udev_device->maj > 0) - udev_device_set_devnum(udev_device, makedev(udev_device->maj, udev_device->min)); - udev_device->maj = 0; - udev_device->min = 0; - - if (udev_device->devpath == NULL || udev_device->subsystem == NULL) - return -EINVAL; - return 0; -} - -/** - * udev_device_get_property_value: - * @udev_device: udev device - * @key: property name - * - * Returns: the value of a device property, or #NULL if there is no such property. - **/ -UDEV_EXPORT const char *udev_device_get_property_value(struct udev_device *udev_device, const char *key) -{ - struct udev_list_entry *list_entry; - - if (udev_device == NULL) - return NULL; - if (key == NULL) - return NULL; - - list_entry = udev_device_get_properties_list_entry(udev_device); - list_entry = udev_list_entry_get_by_name(list_entry, key); - return udev_list_entry_get_value(list_entry); -} - -int udev_device_read_db(struct udev_device *udev_device, const char *dbfile) -{ - char filename[UTIL_PATH_SIZE]; - char line[UTIL_LINE_SIZE]; - FILE *f; - - /* providing a database file will always force-load it */ - if (dbfile == NULL) { - const char *id; - - if (udev_device->db_loaded) - return 0; - udev_device->db_loaded = true; - - id = udev_device_get_id_filename(udev_device); - if (id == NULL) - return -1; - util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_device->udev), "/data/", id, NULL); - dbfile = filename; - } - - f = fopen(dbfile, "re"); - if (f == NULL) { - info(udev_device->udev, "no db file to read %s: %m\n", dbfile); - return -1; - } - udev_device->is_initialized = true; - - while (fgets(line, sizeof(line), f)) { - ssize_t len; - const char *val; - struct udev_list_entry *entry; - - len = strlen(line); - if (len < 4) - break; - line[len-1] = '\0'; - val = &line[2]; - switch(line[0]) { - case 'S': - util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_device->udev), "/", val, NULL); - udev_device_add_devlink(udev_device, filename, 0); - break; - case 'L': - udev_device_set_devlink_priority(udev_device, atoi(val)); - break; - case 'E': - entry = udev_device_add_property_from_string(udev_device, val); - udev_list_entry_set_num(entry, true); - break; - case 'G': - udev_device_add_tag(udev_device, val); - break; - case 'W': - udev_device_set_watch_handle(udev_device, atoi(val)); - break; - case 'I': - udev_device_set_usec_initialized(udev_device, strtoull(val, NULL, 10)); - break; - } - } - fclose(f); - - info(udev_device->udev, "device %p filled with db file data\n", udev_device); - return 0; -} - -int udev_device_read_uevent_file(struct udev_device *udev_device) -{ - char filename[UTIL_PATH_SIZE]; - FILE *f; - char line[UTIL_LINE_SIZE]; - int maj = 0; - int min = 0; - - if (udev_device->uevent_loaded) - return 0; - - util_strscpyl(filename, sizeof(filename), udev_device->syspath, "/uevent", NULL); - f = fopen(filename, "re"); - if (f == NULL) - return -1; - udev_device->uevent_loaded = true; - - while (fgets(line, sizeof(line), f)) { - char *pos; - - pos = strchr(line, '\n'); - if (pos == NULL) - continue; - pos[0] = '\0'; - - if (strncmp(line, "DEVTYPE=", 8) == 0) { - udev_device_set_devtype(udev_device, &line[8]); - continue; - } - if (strncmp(line, "IFINDEX=", 8) == 0) { - udev_device_set_ifindex(udev_device, strtoull(&line[8], NULL, 10)); - continue; - } - if (strncmp(line, "DEVNAME=", 8) == 0) { - udev_device_set_devnode(udev_device, &line[8]); - continue; - } - - if (strncmp(line, "MAJOR=", 6) == 0) - maj = strtoull(&line[6], NULL, 10); - else if (strncmp(line, "MINOR=", 6) == 0) - min = strtoull(&line[6], NULL, 10); - else if (strncmp(line, "DEVMODE=", 8) == 0) - udev_device->devnode_mode = strtoul(&line[8], NULL, 8); - - udev_device_add_property_from_string(udev_device, line); - } - - udev_device->devnum = makedev(maj, min); - fclose(f); - return 0; -} - -void udev_device_set_info_loaded(struct udev_device *device) -{ - device->info_loaded = true; -} - -struct udev_device *udev_device_new(struct udev *udev) -{ - struct udev_device *udev_device; - struct udev_list_entry *list_entry; - - if (udev == NULL) - return NULL; - - udev_device = calloc(1, sizeof(struct udev_device)); - if (udev_device == NULL) - return NULL; - udev_device->refcount = 1; - udev_device->udev = udev; - udev_list_init(udev, &udev_device->devlinks_list, true); - udev_list_init(udev, &udev_device->properties_list, true); - udev_list_init(udev, &udev_device->sysattr_value_list, true); - udev_list_init(udev, &udev_device->sysattr_list, false); - udev_list_init(udev, &udev_device->tags_list, true); - udev_device->watch_handle = -1; - /* copy global properties */ - udev_list_entry_foreach(list_entry, udev_get_properties_list_entry(udev)) - udev_device_add_property(udev_device, - udev_list_entry_get_name(list_entry), - udev_list_entry_get_value(list_entry)); - dbg(udev_device->udev, "udev_device: %p created\n", udev_device); - return udev_device; -} - -/** - * udev_device_new_from_syspath: - * @udev: udev library context - * @syspath: sys device path including sys directory - * - * Create new udev device, and fill in information from the sys - * device and the udev database entry. The syspath is the absolute - * path to the device, including the sys mount point. - * - * The initial refcount is 1, and needs to be decremented to - * release the resources of the udev device. - * - * Returns: a new udev device, or #NULL, if it does not exist - **/ -UDEV_EXPORT struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath) -{ - size_t len; - const char *subdir; - char path[UTIL_PATH_SIZE]; - char *pos; - struct stat statbuf; - struct udev_device *udev_device; - - if (udev == NULL) - return NULL; - if (syspath == NULL) - return NULL; - - /* path starts in sys */ - len = strlen(udev_get_sys_path(udev)); - if (strncmp(syspath, udev_get_sys_path(udev), len) != 0) { - info(udev, "not in sys :%s\n", syspath); - return NULL; - } - - /* path is not a root directory */ - subdir = &syspath[len+1]; - pos = strrchr(subdir, '/'); - if (pos == NULL || pos[1] == '\0' || pos < &subdir[2]) { - dbg(udev, "not a subdir :%s\n", syspath); - return NULL; - } - - /* resolve possible symlink to real path */ - util_strscpy(path, sizeof(path), syspath); - util_resolve_sys_link(udev, path, sizeof(path)); - - if (strncmp(&path[len], "/devices/", 9) == 0) { - char file[UTIL_PATH_SIZE]; - - /* all "devices" require a "uevent" file */ - util_strscpyl(file, sizeof(file), path, "/uevent", NULL); - if (stat(file, &statbuf) != 0) { - dbg(udev, "not a device: %s\n", syspath); - return NULL; - } - } else { - /* everything else just needs to be a directory */ - if (stat(path, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) { - dbg(udev, "directory not found: %s\n", syspath); - return NULL; - } - } - - udev_device = udev_device_new(udev); - if (udev_device == NULL) - return NULL; - - udev_device_set_syspath(udev_device, path); - info(udev, "device %p has devpath '%s'\n", udev_device, udev_device_get_devpath(udev_device)); - - return udev_device; -} - -/** - * udev_device_new_from_devnum: - * @udev: udev library context - * @type: char or block device - * @devnum: device major/minor number - * - * Create new udev device, and fill in information from the sys - * device and the udev database entry. The device is looked-up - * by its major/minor number and type. Character and block device - * numbers are not unique across the two types. - * - * The initial refcount is 1, and needs to be decremented to - * release the resources of the udev device. - * - * Returns: a new udev device, or #NULL, if it does not exist - **/ -UDEV_EXPORT struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum) -{ - char path[UTIL_PATH_SIZE]; - const char *type_str; - - if (type == 'b') - type_str = "block"; - else if (type == 'c') - type_str = "char"; - else - return NULL; - - /* use /sys/dev/{block,char}/: link */ - snprintf(path, sizeof(path), "%s/dev/%s/%u:%u", - udev_get_sys_path(udev), type_str, major(devnum), minor(devnum)); - return udev_device_new_from_syspath(udev, path); -} - -struct udev_device *udev_device_new_from_id_filename(struct udev *udev, char *id) -{ - char type; - int maj, min; - char subsys[UTIL_PATH_SIZE]; - char *sysname; - - switch(id[0]) { - case 'b': - case 'c': - if (sscanf(id, "%c%i:%i", &type, &maj, &min) != 3) - return NULL; - return udev_device_new_from_devnum(udev, type, makedev(maj, min)); - case 'n': { - int sk; - struct ifreq ifr; - struct udev_device *dev; - int ifindex; - - ifindex = strtoul(&id[1], NULL, 10); - if (ifindex <= 0) - return NULL; - - sk = socket(PF_INET, SOCK_DGRAM, 0); - if (sk < 0) - return NULL; - memset(&ifr, 0x00, sizeof(struct ifreq)); - ifr.ifr_ifindex = ifindex; - if (ioctl(sk, SIOCGIFNAME, &ifr) != 0) { - close(sk); - return NULL; - } - close(sk); - - dev = udev_device_new_from_subsystem_sysname(udev, "net", ifr.ifr_name); - if (dev == NULL) - return NULL; - if (udev_device_get_ifindex(dev) == ifindex) - return dev; - udev_device_unref(dev); - return NULL; - } - case '+': - util_strscpy(subsys, sizeof(subsys), &id[1]); - sysname = strchr(subsys, ':'); - if (sysname == NULL) - return NULL; - sysname[0] = '\0'; - sysname = &sysname[1]; - return udev_device_new_from_subsystem_sysname(udev, subsys, sysname); - default: - return NULL; - } -} - -/** - * udev_device_new_from_subsystem_sysname: - * @udev: udev library context - * @subsystem: the subsystem of the device - * @sysname: the name of the device - * - * Create new udev device, and fill in information from the sys device - * and the udev database entry. The device is looked up by the subsystem - * and name string of the device, like "mem" / "zero", or "block" / "sda". - * - * The initial refcount is 1, and needs to be decremented to - * release the resources of the udev device. - * - * Returns: a new udev device, or #NULL, if it does not exist - **/ -UDEV_EXPORT struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname) -{ - char path_full[UTIL_PATH_SIZE]; - char *path; - size_t l; - struct stat statbuf; - - path = path_full; - l = util_strpcpyl(&path, sizeof(path_full), udev_get_sys_path(udev), NULL); - - if (strcmp(subsystem, "subsystem") == 0) { - util_strscpyl(path, l, "/subsystem/", sysname, NULL); - if (stat(path_full, &statbuf) == 0) - goto found; - - util_strscpyl(path, l, "/bus/", sysname, NULL); - if (stat(path_full, &statbuf) == 0) - goto found; - - util_strscpyl(path, l, "/class/", sysname, NULL); - if (stat(path_full, &statbuf) == 0) - goto found; - goto out; - } - - if (strcmp(subsystem, "module") == 0) { - util_strscpyl(path, l, "/module/", sysname, NULL); - if (stat(path_full, &statbuf) == 0) - goto found; - goto out; - } - - if (strcmp(subsystem, "drivers") == 0) { - char subsys[UTIL_NAME_SIZE]; - char *driver; - - util_strscpy(subsys, sizeof(subsys), sysname); - driver = strchr(subsys, ':'); - if (driver != NULL) { - driver[0] = '\0'; - driver = &driver[1]; - - util_strscpyl(path, l, "/subsystem/", subsys, "/drivers/", driver, NULL); - if (stat(path_full, &statbuf) == 0) - goto found; - - util_strscpyl(path, l, "/bus/", subsys, "/drivers/", driver, NULL); - if (stat(path_full, &statbuf) == 0) - goto found; - } - goto out; - } - - util_strscpyl(path, l, "/subsystem/", subsystem, "/devices/", sysname, NULL); - if (stat(path_full, &statbuf) == 0) - goto found; - - util_strscpyl(path, l, "/bus/", subsystem, "/devices/", sysname, NULL); - if (stat(path_full, &statbuf) == 0) - goto found; - - util_strscpyl(path, l, "/class/", subsystem, "/", sysname, NULL); - if (stat(path_full, &statbuf) == 0) - goto found; -out: - return NULL; -found: - return udev_device_new_from_syspath(udev, path_full); -} - -/** - * udev_device_new_from_environment - * @udev: udev library context - * - * Create new udev device, and fill in information from the - * current process environment. This only works reliable if - * the process is called from a udev rule. It is usually used - * for tools executed from IMPORT= rules. - * - * The initial refcount is 1, and needs to be decremented to - * release the resources of the udev device. - * - * Returns: a new udev device, or #NULL, if it does not exist - **/ -UDEV_EXPORT struct udev_device *udev_device_new_from_environment(struct udev *udev) -{ - int i; - struct udev_device *udev_device; - - udev_device = udev_device_new(udev); - if (udev_device == NULL) - return NULL; - udev_device_set_info_loaded(udev_device); - - for (i = 0; environ[i] != NULL; i++) - udev_device_add_property_from_string_parse(udev_device, environ[i]); - - if (udev_device_add_property_from_string_parse_finish(udev_device) < 0) { - info(udev, "missing values, invalid device\n"); - udev_device_unref(udev_device); - udev_device = NULL; - } - - return udev_device; -} - -static struct udev_device *device_new_from_parent(struct udev_device *udev_device) -{ - struct udev_device *udev_device_parent = NULL; - char path[UTIL_PATH_SIZE]; - const char *subdir; - - util_strscpy(path, sizeof(path), udev_device->syspath); - subdir = &path[strlen(udev_get_sys_path(udev_device->udev))+1]; - for (;;) { - char *pos; - - pos = strrchr(subdir, '/'); - if (pos == NULL || pos < &subdir[2]) - break; - pos[0] = '\0'; - udev_device_parent = udev_device_new_from_syspath(udev_device->udev, path); - if (udev_device_parent != NULL) - return udev_device_parent; - } - return NULL; -} - -/** - * udev_device_get_parent: - * @udev_device: the device to start searching from - * - * Find the next parent device, and fill in information from the sys - * device and the udev database entry. - * - * The returned the device is not referenced. It is attached to the - * child device, and will be cleaned up when the child device - * is cleaned up. - * - * It is not necessarily just the upper level directory, empty or not - * recognized sys directories are ignored. - * - * It can be called as many times as needed, without caring about - * references. - * - * Returns: a new udev device, or #NULL, if it no parent exist. - **/ -UDEV_EXPORT struct udev_device *udev_device_get_parent(struct udev_device *udev_device) -{ - if (udev_device == NULL) - return NULL; - if (!udev_device->parent_set) { - udev_device->parent_set = true; - udev_device->parent_device = device_new_from_parent(udev_device); - } - if (udev_device->parent_device != NULL) - dbg(udev_device->udev, "returning existing parent %p\n", udev_device->parent_device); - return udev_device->parent_device; -} - -/** - * udev_device_get_parent_with_subsystem_devtype: - * @udev_device: udev device to start searching from - * @subsystem: the subsystem of the device - * @devtype: the type (DEVTYPE) of the device - * - * Find the next parent device, with a matching subsystem and devtype - * value, and fill in information from the sys device and the udev - * database entry. - * - * If devtype is #NULL, only subsystem is checked, and any devtype will - * match. - * - * The returned the device is not referenced. It is attached to the - * child device, and will be cleaned up when the child device - * is cleaned up. - * - * It can be called as many times as needed, without caring about - * references. - * - * Returns: a new udev device, or #NULL if no matching parent exists. - **/ -UDEV_EXPORT struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_device *udev_device, const char *subsystem, const char *devtype) -{ - struct udev_device *parent; - - if (subsystem == NULL) - return NULL; - - parent = udev_device_get_parent(udev_device); - while (parent != NULL) { - const char *parent_subsystem; - const char *parent_devtype; - - parent_subsystem = udev_device_get_subsystem(parent); - if (parent_subsystem != NULL && strcmp(parent_subsystem, subsystem) == 0) { - if (devtype == NULL) - break; - parent_devtype = udev_device_get_devtype(parent); - if (parent_devtype != NULL && strcmp(parent_devtype, devtype) == 0) - break; - } - parent = udev_device_get_parent(parent); - } - return parent; -} - -/** - * udev_device_get_udev: - * @udev_device: udev device - * - * Retrieve the udev library context the device was created with. - * - * Returns: the udev library context - **/ -UDEV_EXPORT struct udev *udev_device_get_udev(struct udev_device *udev_device) -{ - if (udev_device == NULL) - return NULL; - return udev_device->udev; -} - -/** - * udev_device_ref: - * @udev_device: udev device - * - * Take a reference of a udev device. - * - * Returns: the passed udev device - **/ -UDEV_EXPORT struct udev_device *udev_device_ref(struct udev_device *udev_device) -{ - if (udev_device == NULL) - return NULL; - udev_device->refcount++; - return udev_device; -} - -/** - * udev_device_unref: - * @udev_device: udev device - * - * Drop a reference of a udev device. If the refcount reaches zero, - * the resources of the device will be released. - * - **/ -UDEV_EXPORT void udev_device_unref(struct udev_device *udev_device) -{ - if (udev_device == NULL) - return; - udev_device->refcount--; - if (udev_device->refcount > 0) - return; - if (udev_device->parent_device != NULL) - udev_device_unref(udev_device->parent_device); - free(udev_device->syspath); - free(udev_device->sysname); - free(udev_device->devnode); - free(udev_device->subsystem); - free(udev_device->devtype); - udev_list_cleanup(&udev_device->devlinks_list); - udev_list_cleanup(&udev_device->properties_list); - udev_list_cleanup(&udev_device->sysattr_value_list); - udev_list_cleanup(&udev_device->sysattr_list); - udev_list_cleanup(&udev_device->tags_list); - free(udev_device->action); - free(udev_device->driver); - free(udev_device->devpath_old); - free(udev_device->id_filename); - free(udev_device->envp); - free(udev_device->monitor_buf); - dbg(udev_device->udev, "udev_device: %p released\n", udev_device); - free(udev_device); -} - -/** - * udev_device_get_devpath: - * @udev_device: udev device - * - * Retrieve the kernel devpath value of the udev device. The path - * does not contain the sys mount point, and starts with a '/'. - * - * Returns: the devpath of the udev device - **/ -UDEV_EXPORT const char *udev_device_get_devpath(struct udev_device *udev_device) -{ - if (udev_device == NULL) - return NULL; - return udev_device->devpath; -} - -/** - * udev_device_get_syspath: - * @udev_device: udev device - * - * Retrieve the sys path of the udev device. The path is an - * absolute path and starts with the sys mount point. - * - * Returns: the sys path of the udev device - **/ -UDEV_EXPORT const char *udev_device_get_syspath(struct udev_device *udev_device) -{ - if (udev_device == NULL) - return NULL; - return udev_device->syspath; -} - -/** - * udev_device_get_sysname: - * @udev_device: udev device - * - * Returns: the sys name of the device device - **/ -UDEV_EXPORT const char *udev_device_get_sysname(struct udev_device *udev_device) -{ - if (udev_device == NULL) - return NULL; - return udev_device->sysname; -} - -/** - * udev_device_get_sysnum: - * @udev_device: udev device - * - * Returns: the trailing number of of the device name - **/ -UDEV_EXPORT const char *udev_device_get_sysnum(struct udev_device *udev_device) -{ - if (udev_device == NULL) - return NULL; - return udev_device->sysnum; -} - -/** - * udev_device_get_devnode: - * @udev_device: udev device - * - * Retrieve the device node file name belonging to the udev device. - * The path is an absolute path, and starts with the device directory. - * - * Returns: the device node file name of the udev device, or #NULL if no device node exists - **/ -UDEV_EXPORT const char *udev_device_get_devnode(struct udev_device *udev_device) -{ - if (udev_device == NULL) - return NULL; - if (udev_device->devnode != NULL) - return udev_device->devnode; - if (!udev_device->info_loaded) - udev_device_read_uevent_file(udev_device); - return udev_device->devnode; -} - -/** - * udev_device_get_devlinks_list_entry: - * @udev_device: udev device - * - * Retrieve the list of device links pointing to the device file of - * the udev device. The next list entry can be retrieved with - * udev_list_entry_next(), which returns #NULL if no more entries exist. - * The devlink path can be retrieved from the list entry by - * udev_list_entry_get_name(). The path is an absolute path, and starts with - * the device directory. - * - * Returns: the first entry of the device node link list - **/ -UDEV_EXPORT struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev_device *udev_device) -{ - if (udev_device == NULL) - return NULL; - if (!udev_device->info_loaded) - udev_device_read_db(udev_device, NULL); - return udev_list_get_entry(&udev_device->devlinks_list); -} - -void udev_device_cleanup_devlinks_list(struct udev_device *udev_device) -{ - udev_device->devlinks_uptodate = false; - udev_list_cleanup(&udev_device->devlinks_list); -} - -/** - * udev_device_get_properties_list_entry: - * @udev_device: udev device - * - * Retrieve the list of key/value device properties of the udev - * device. The next list entry can be retrieved with udev_list_entry_next(), - * which returns #NULL if no more entries exist. The property name - * can be retrieved from the list entry by udev_list_get_name(), - * the property value by udev_list_get_value(). - * - * Returns: the first entry of the property list - **/ -UDEV_EXPORT struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device *udev_device) -{ - if (udev_device == NULL) - return NULL; - if (!udev_device->info_loaded) { - udev_device_read_uevent_file(udev_device); - udev_device_read_db(udev_device, NULL); - } - if (!udev_device->devlinks_uptodate) { - char symlinks[UTIL_PATH_SIZE]; - struct udev_list_entry *list_entry; - - udev_device->devlinks_uptodate = true; - list_entry = udev_device_get_devlinks_list_entry(udev_device); - if (list_entry != NULL) { - char *s; - size_t l; - - s = symlinks; - l = util_strpcpyl(&s, sizeof(symlinks), udev_list_entry_get_name(list_entry), NULL); - udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry)) - l = util_strpcpyl(&s, l, " ", udev_list_entry_get_name(list_entry), NULL); - udev_device_add_property(udev_device, "DEVLINKS", symlinks); - } - } - if (!udev_device->tags_uptodate) { - udev_device->tags_uptodate = true; - if (udev_device_get_tags_list_entry(udev_device) != NULL) { - char tags[UTIL_PATH_SIZE]; - struct udev_list_entry *list_entry; - char *s; - size_t l; - - s = tags; - l = util_strpcpyl(&s, sizeof(tags), ":", NULL); - udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(udev_device)) - l = util_strpcpyl(&s, l, udev_list_entry_get_name(list_entry), ":", NULL); - udev_device_add_property(udev_device, "TAGS", tags); - } - } - return udev_list_get_entry(&udev_device->properties_list); -} - -/** - * udev_device_get_action: - * @udev_device: udev device - * - * This is only valid if the device was received through a monitor. Devices read from - * sys do not have an action string. Usual actions are: add, remove, change, online, - * offline. - * - * Returns: the kernel action value, or #NULL if there is no action value available. - **/ -UDEV_EXPORT const char *udev_device_get_action(struct udev_device *udev_device) -{ - if (udev_device == NULL) - return NULL; - return udev_device->action; -} - -/** - * udev_device_get_usec_since_initialized: - * @udev_device: udev device - * - * Return the number of microseconds passed since udev set up the - * device for the first time. - * - * This is only implemented for devices with need to store properties - * in the udev database. All other devices return 0 here. - * - * Returns: the number of microseconds since the device was first seen. - **/ -UDEV_EXPORT unsigned long long int udev_device_get_usec_since_initialized(struct udev_device *udev_device) -{ - unsigned long long now; - - if (udev_device == NULL) - return 0; - if (!udev_device->info_loaded) - udev_device_read_db(udev_device, NULL); - if (udev_device->usec_initialized == 0) - return 0; - now = now_usec(); - if (now == 0) - return 0; - return now - udev_device->usec_initialized; -} - -unsigned long long udev_device_get_usec_initialized(struct udev_device *udev_device) -{ - return udev_device->usec_initialized; -} - -void udev_device_set_usec_initialized(struct udev_device *udev_device, unsigned long long usec_initialized) -{ - char num[32]; - - udev_device->usec_initialized = usec_initialized; - snprintf(num, sizeof(num), "%llu", usec_initialized); - udev_device_add_property(udev_device, "USEC_INITIALIZED", num); -} - -/** - * udev_device_get_sysattr_value: - * @udev_device: udev device - * @sysattr: attribute name - * - * The retrieved value is cached in the device. Repeated calls will return the same - * value and not open the attribute again. - * - * Returns: the content of a sys attribute file, or #NULL if there is no sys attribute value. - **/ -UDEV_EXPORT const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr) -{ - struct udev_list_entry *list_entry; - char path[UTIL_PATH_SIZE]; - char value[4096]; - struct stat statbuf; - int fd; - ssize_t size; - const char *val = NULL; - - if (udev_device == NULL) - return NULL; - if (sysattr == NULL) - return NULL; - - /* look for possibly already cached result */ - list_entry = udev_list_get_entry(&udev_device->sysattr_value_list); - list_entry = udev_list_entry_get_by_name(list_entry, sysattr); - if (list_entry != NULL) { - dbg(udev_device->udev, "got '%s' (%s) from cache\n", - sysattr, udev_list_entry_get_value(list_entry)); - return udev_list_entry_get_value(list_entry); - } - - util_strscpyl(path, sizeof(path), udev_device_get_syspath(udev_device), "/", sysattr, NULL); - if (lstat(path, &statbuf) != 0) { - dbg(udev_device->udev, "no attribute '%s', keep negative entry\n", path); - udev_list_entry_add(&udev_device->sysattr_value_list, sysattr, NULL); - goto out; - } - - if (S_ISLNK(statbuf.st_mode)) { - struct udev_device *dev; - - /* - * Some core links return only the last element of the target path, - * these are just values, the paths should not be exposed. - */ - if (strcmp(sysattr, "driver") == 0 || - strcmp(sysattr, "subsystem") == 0 || - strcmp(sysattr, "module") == 0) { - if (util_get_sys_core_link_value(udev_device->udev, sysattr, - udev_device->syspath, value, sizeof(value)) < 0) - return NULL; - dbg(udev_device->udev, "cache '%s' with link value '%s'\n", sysattr, value); - list_entry = udev_list_entry_add(&udev_device->sysattr_value_list, sysattr, value); - val = udev_list_entry_get_value(list_entry); - goto out; - } - - /* resolve link to a device and return its syspath */ - util_strscpyl(path, sizeof(path), udev_device->syspath, "/", sysattr, NULL); - dev = udev_device_new_from_syspath(udev_device->udev, path); - if (dev != NULL) { - list_entry = udev_list_entry_add(&udev_device->sysattr_value_list, sysattr, - udev_device_get_syspath(dev)); - val = udev_list_entry_get_value(list_entry); - udev_device_unref(dev); - } - - goto out; - } - - /* skip directories */ - if (S_ISDIR(statbuf.st_mode)) - goto out; - - /* skip non-readable files */ - if ((statbuf.st_mode & S_IRUSR) == 0) - goto out; - - /* read attribute value */ - fd = open(path, O_RDONLY|O_CLOEXEC); - if (fd < 0) { - dbg(udev_device->udev, "attribute '%s' can not be opened\n", path); - goto out; - } - size = read(fd, value, sizeof(value)); - close(fd); - if (size < 0) - goto out; - if (size == sizeof(value)) - goto out; - - /* got a valid value, store it in cache and return it */ - value[size] = '\0'; - util_remove_trailing_chars(value, '\n'); - dbg(udev_device->udev, "'%s' has attribute value '%s'\n", path, value); - list_entry = udev_list_entry_add(&udev_device->sysattr_value_list, sysattr, value); - val = udev_list_entry_get_value(list_entry); -out: - return val; -} - -static int udev_device_sysattr_list_read(struct udev_device *udev_device) -{ - struct dirent *dent; - DIR *dir; - int num = 0; - - if (udev_device == NULL) - return -1; - if (udev_device->sysattr_list_read) - return 0; - - dir = opendir(udev_device_get_syspath(udev_device)); - if (!dir) { - dbg(udev_device->udev, "sysfs dir '%s' can not be opened\n", - udev_device_get_syspath(udev_device)); - return -1; - } - - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - char path[UTIL_PATH_SIZE]; - struct stat statbuf; - - /* only handle symlinks and regular files */ - if (dent->d_type != DT_LNK && dent->d_type != DT_REG) - continue; - - util_strscpyl(path, sizeof(path), udev_device_get_syspath(udev_device), "/", dent->d_name, NULL); - if (lstat(path, &statbuf) != 0) - continue; - if ((statbuf.st_mode & S_IRUSR) == 0) - continue; - - udev_list_entry_add(&udev_device->sysattr_list, dent->d_name, NULL); - num++; - } - - closedir(dir); - dbg(udev_device->udev, "found %d sysattrs for '%s'\n", num, udev_device_get_syspath(udev_device)); - udev_device->sysattr_list_read = true; - - return num; -} - -/** - * udev_device_get_sysattr_list_entry: - * @udev_device: udev device - * - * Retrieve the list of available sysattrs, with value being empty; - * This just return all available sysfs attributes for a particular - * device without reading their values. - * - * Returns: the first entry of the property list - **/ -UDEV_EXPORT struct udev_list_entry *udev_device_get_sysattr_list_entry(struct udev_device *udev_device) -{ - if (!udev_device->sysattr_list_read) { - int ret; - ret = udev_device_sysattr_list_read(udev_device); - if (0 > ret) - return NULL; - } - - return udev_list_get_entry(&udev_device->sysattr_list); -} - -int udev_device_set_syspath(struct udev_device *udev_device, const char *syspath) -{ - const char *pos; - size_t len; - - free(udev_device->syspath); - udev_device->syspath = strdup(syspath); - if (udev_device->syspath == NULL) - return -ENOMEM; - udev_device->devpath = &udev_device->syspath[strlen(udev_get_sys_path(udev_device->udev))]; - udev_device_add_property(udev_device, "DEVPATH", udev_device->devpath); - - pos = strrchr(udev_device->syspath, '/'); - if (pos == NULL) - return -EINVAL; - udev_device->sysname = strdup(&pos[1]); - if (udev_device->sysname == NULL) - return -ENOMEM; - - /* some devices have '!' in their name, change that to '/' */ - len = 0; - while (udev_device->sysname[len] != '\0') { - if (udev_device->sysname[len] == '!') - udev_device->sysname[len] = '/'; - len++; - } - - /* trailing number */ - while (len > 0 && isdigit(udev_device->sysname[--len])) - udev_device->sysnum = &udev_device->sysname[len]; - - /* sysname is completely numeric */ - if (len == 0) - udev_device->sysnum = NULL; - - return 0; -} - -int udev_device_set_devnode(struct udev_device *udev_device, const char *devnode) -{ - free(udev_device->devnode); - if (devnode[0] != '/') { - if (asprintf(&udev_device->devnode, "%s/%s", udev_get_dev_path(udev_device->udev), devnode) < 0) - udev_device->devnode = NULL; - } else { - udev_device->devnode = strdup(devnode); - } - if (udev_device->devnode == NULL) - return -ENOMEM; - udev_device_add_property(udev_device, "DEVNAME", udev_device->devnode); - return 0; -} - -int udev_device_add_devlink(struct udev_device *udev_device, const char *devlink, int unique) -{ - struct udev_list_entry *list_entry; - - udev_device->devlinks_uptodate = false; - list_entry = udev_list_entry_add(&udev_device->devlinks_list, devlink, NULL); - if (list_entry == NULL) - return -ENOMEM; - if (unique) - udev_list_entry_set_num(list_entry, true); - return 0; -} - -const char *udev_device_get_id_filename(struct udev_device *udev_device) -{ - if (udev_device->id_filename == NULL) { - if (udev_device_get_subsystem(udev_device) == NULL) - return NULL; - - if (major(udev_device_get_devnum(udev_device)) > 0) { - /* use dev_t -- b259:131072, c254:0 */ - if (asprintf(&udev_device->id_filename, "%c%u:%u", - strcmp(udev_device_get_subsystem(udev_device), "block") == 0 ? 'b' : 'c', - major(udev_device_get_devnum(udev_device)), - minor(udev_device_get_devnum(udev_device))) < 0) - udev_device->id_filename = NULL; - } else if (udev_device_get_ifindex(udev_device) > 0) { - /* use netdev ifindex -- n3 */ - if (asprintf(&udev_device->id_filename, "n%u", udev_device_get_ifindex(udev_device)) < 0) - udev_device->id_filename = NULL; - } else { - /* - * use $subsys:$syname -- pci:0000:00:1f.2 - * sysname() has '!' translated, get it from devpath - */ - const char *sysname; - sysname = strrchr(udev_device->devpath, '/'); - if (sysname == NULL) - return NULL; - sysname = &sysname[1]; - if (asprintf(&udev_device->id_filename, "+%s:%s", udev_device_get_subsystem(udev_device), sysname) < 0) - udev_device->id_filename = NULL; - } - } - return udev_device->id_filename; -} - -/** - * udev_device_get_is_initialized: - * @udev_device: udev device - * - * Check if udev has already handled the device and has set up - * device node permissions and context, or has renamed a network - * device. - * - * This is only implemented for devices with a device node - * or network interfaces. All other devices return 1 here. - * - * Returns: 1 if the device is set up. 0 otherwise. - **/ -UDEV_EXPORT int udev_device_get_is_initialized(struct udev_device *udev_device) -{ - if (!udev_device->info_loaded) - udev_device_read_db(udev_device, NULL); - return udev_device->is_initialized; -} - -void udev_device_set_is_initialized(struct udev_device *udev_device) -{ - udev_device->is_initialized = true; -} - -int udev_device_add_tag(struct udev_device *udev_device, const char *tag) -{ - if (strchr(tag, ':') != NULL || strchr(tag, ' ') != NULL) - return -EINVAL; - udev_device->tags_uptodate = false; - if (udev_list_entry_add(&udev_device->tags_list, tag, NULL) != NULL) - return 0; - return -ENOMEM; -} - -void udev_device_cleanup_tags_list(struct udev_device *udev_device) -{ - udev_device->tags_uptodate = false; - udev_list_cleanup(&udev_device->tags_list); -} - -/** - * udev_device_get_tags_list_entry: - * @udev_device: udev device - * - * Retrieve the list of tags attached to the udev device. The next - * list entry can be retrieved with udev_list_entry_next(), - * which returns #NULL if no more entries exist. The tag string - * can be retrieved from the list entry by udev_list_get_name(). - * - * Returns: the first entry of the tag list - **/ -UDEV_EXPORT struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_device *udev_device) -{ - if (udev_device == NULL) - return NULL; - if (!udev_device->info_loaded) - udev_device_read_db(udev_device, NULL); - return udev_list_get_entry(&udev_device->tags_list); -} - -UDEV_EXPORT int udev_device_has_tag(struct udev_device *udev_device, const char *tag) -{ - struct udev_list_entry *list_entry; - - if (udev_device == NULL) - return false; - if (!udev_device->info_loaded) - udev_device_read_db(udev_device, NULL); - list_entry = udev_device_get_tags_list_entry(udev_device); - if (udev_list_entry_get_by_name(list_entry, tag) != NULL) - return true; - return false; -} - -#define ENVP_SIZE 128 -#define MONITOR_BUF_SIZE 4096 -static int update_envp_monitor_buf(struct udev_device *udev_device) -{ - struct udev_list_entry *list_entry; - char *s; - size_t l; - unsigned int i; - - /* monitor buffer of property strings */ - free(udev_device->monitor_buf); - udev_device->monitor_buf_len = 0; - udev_device->monitor_buf = malloc(MONITOR_BUF_SIZE); - if (udev_device->monitor_buf == NULL) - return -ENOMEM; - - /* envp array, strings will point into monitor buffer */ - if (udev_device->envp == NULL) - udev_device->envp = malloc(sizeof(char *) * ENVP_SIZE); - if (udev_device->envp == NULL) - return -ENOMEM; - - i = 0; - s = udev_device->monitor_buf; - l = MONITOR_BUF_SIZE; - udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev_device)) { - const char *key; - - key = udev_list_entry_get_name(list_entry); - /* skip private variables */ - if (key[0] == '.') - continue; - - /* add string to envp array */ - udev_device->envp[i++] = s; - if (i+1 >= ENVP_SIZE) - return -EINVAL; - - /* add property string to monitor buffer */ - l = util_strpcpyl(&s, l, key, "=", udev_list_entry_get_value(list_entry), NULL); - if (l == 0) - return -EINVAL; - /* advance past the trailing '\0' that util_strpcpyl() guarantees */ - s++; - l--; - } - udev_device->envp[i] = NULL; - udev_device->monitor_buf_len = s - udev_device->monitor_buf; - udev_device->envp_uptodate = true; - dbg(udev_device->udev, "filled envp/monitor buffer, %u properties, %zu bytes\n", - i, udev_device->monitor_buf_len); - return 0; -} - -char **udev_device_get_properties_envp(struct udev_device *udev_device) -{ - if (!udev_device->envp_uptodate) - if (update_envp_monitor_buf(udev_device) != 0) - return NULL; - return udev_device->envp; -} - -ssize_t udev_device_get_properties_monitor_buf(struct udev_device *udev_device, const char **buf) -{ - if (!udev_device->envp_uptodate) - if (update_envp_monitor_buf(udev_device) != 0) - return -EINVAL; - *buf = udev_device->monitor_buf; - return udev_device->monitor_buf_len; -} - -int udev_device_set_action(struct udev_device *udev_device, const char *action) -{ - free(udev_device->action); - udev_device->action = strdup(action); - if (udev_device->action == NULL) - return -ENOMEM; - udev_device_add_property(udev_device, "ACTION", udev_device->action); - return 0; -} - -int udev_device_get_devlink_priority(struct udev_device *udev_device) -{ - if (!udev_device->info_loaded) - udev_device_read_db(udev_device, NULL); - return udev_device->devlink_priority; -} - -int udev_device_set_devlink_priority(struct udev_device *udev_device, int prio) -{ - udev_device->devlink_priority = prio; - return 0; -} - -int udev_device_get_watch_handle(struct udev_device *udev_device) -{ - if (!udev_device->info_loaded) - udev_device_read_db(udev_device, NULL); - return udev_device->watch_handle; -} - -int udev_device_set_watch_handle(struct udev_device *udev_device, int handle) -{ - udev_device->watch_handle = handle; - return 0; -} - -bool udev_device_get_db_persist(struct udev_device *udev_device) -{ - return udev_device->db_persist; -} - -void udev_device_set_db_persist(struct udev_device *udev_device) -{ - udev_device->db_persist = true; -} diff --git a/src/udev/src/libudev-enumerate.c b/src/udev/src/libudev-enumerate.c deleted file mode 100644 index 034d96feba..0000000000 --- a/src/udev/src/libudev-enumerate.c +++ /dev/null @@ -1,947 +0,0 @@ -/* - * libudev - interface to udev device information - * - * Copyright (C) 2008-2010 Kay Sievers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libudev.h" -#include "libudev-private.h" - -/** - * SECTION:libudev-enumerate - * @short_description: lookup and sort sys devices - * - * Lookup devices in the sys filesystem, filter devices by properties, - * and return a sorted list of devices. - */ - -struct syspath { - char *syspath; - size_t len; -}; - -/** - * udev_enumerate: - * - * Opaque object representing one device lookup/sort context. - */ -struct udev_enumerate { - struct udev *udev; - int refcount; - struct udev_list sysattr_match_list; - struct udev_list sysattr_nomatch_list; - struct udev_list subsystem_match_list; - struct udev_list subsystem_nomatch_list; - struct udev_list sysname_match_list; - struct udev_list properties_match_list; - struct udev_list tags_match_list; - struct udev_device *parent_match; - struct udev_list devices_list; - struct syspath *devices; - unsigned int devices_cur; - unsigned int devices_max; - bool devices_uptodate:1; - bool match_is_initialized; -}; - -/** - * udev_enumerate_new: - * @udev: udev library context - * - * Returns: an enumeration context - **/ -UDEV_EXPORT struct udev_enumerate *udev_enumerate_new(struct udev *udev) -{ - struct udev_enumerate *udev_enumerate; - - udev_enumerate = calloc(1, sizeof(struct udev_enumerate)); - if (udev_enumerate == NULL) - return NULL; - udev_enumerate->refcount = 1; - udev_enumerate->udev = udev; - udev_list_init(udev, &udev_enumerate->sysattr_match_list, false); - udev_list_init(udev, &udev_enumerate->sysattr_nomatch_list, false); - udev_list_init(udev, &udev_enumerate->subsystem_match_list, true); - udev_list_init(udev, &udev_enumerate->subsystem_nomatch_list, true); - udev_list_init(udev, &udev_enumerate->sysname_match_list, true); - udev_list_init(udev, &udev_enumerate->properties_match_list, false); - udev_list_init(udev, &udev_enumerate->tags_match_list, true); - udev_list_init(udev, &udev_enumerate->devices_list, false); - return udev_enumerate; -} - -/** - * udev_enumerate_ref: - * @udev_enumerate: context - * - * Take a reference of a enumeration context. - * - * Returns: the passed enumeration context - **/ -UDEV_EXPORT struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate) -{ - if (udev_enumerate == NULL) - return NULL; - udev_enumerate->refcount++; - return udev_enumerate; -} - -/** - * udev_enumerate_unref: - * @udev_enumerate: context - * - * Drop a reference of an enumeration context. If the refcount reaches zero, - * all resources of the enumeration context will be released. - **/ -UDEV_EXPORT void udev_enumerate_unref(struct udev_enumerate *udev_enumerate) -{ - unsigned int i; - - if (udev_enumerate == NULL) - return; - udev_enumerate->refcount--; - if (udev_enumerate->refcount > 0) - return; - udev_list_cleanup(&udev_enumerate->sysattr_match_list); - udev_list_cleanup(&udev_enumerate->sysattr_nomatch_list); - udev_list_cleanup(&udev_enumerate->subsystem_match_list); - udev_list_cleanup(&udev_enumerate->subsystem_nomatch_list); - udev_list_cleanup(&udev_enumerate->sysname_match_list); - udev_list_cleanup(&udev_enumerate->properties_match_list); - udev_list_cleanup(&udev_enumerate->tags_match_list); - udev_device_unref(udev_enumerate->parent_match); - udev_list_cleanup(&udev_enumerate->devices_list); - for (i = 0; i < udev_enumerate->devices_cur; i++) - free(udev_enumerate->devices[i].syspath); - free(udev_enumerate->devices); - free(udev_enumerate); -} - -/** - * udev_enumerate_get_udev: - * @udev_enumerate: context - * - * Returns: the udev library context. - */ -UDEV_EXPORT struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate) -{ - if (udev_enumerate == NULL) - return NULL; - return udev_enumerate->udev; -} - -static int syspath_add(struct udev_enumerate *udev_enumerate, const char *syspath) -{ - char *path; - struct syspath *entry; - - /* double array size if needed */ - if (udev_enumerate->devices_cur >= udev_enumerate->devices_max) { - struct syspath *buf; - unsigned int add; - - add = udev_enumerate->devices_max; - if (add < 1024) - add = 1024; - buf = realloc(udev_enumerate->devices, (udev_enumerate->devices_max + add) * sizeof(struct syspath)); - if (buf == NULL) - return -ENOMEM; - udev_enumerate->devices = buf; - udev_enumerate->devices_max += add; - } - - path = strdup(syspath); - if (path == NULL) - return -ENOMEM; - entry = &udev_enumerate->devices[udev_enumerate->devices_cur]; - entry->syspath = path; - entry->len = strlen(path); - udev_enumerate->devices_cur++; - udev_enumerate->devices_uptodate = false; - return 0; -} - -static int syspath_cmp(const void *p1, const void *p2) -{ - const struct syspath *path1 = p1; - const struct syspath *path2 = p2; - size_t len; - int ret; - - len = MIN(path1->len, path2->len); - ret = memcmp(path1->syspath, path2->syspath, len); - if (ret == 0) { - if (path1->len < path2->len) - ret = -1; - else if (path1->len > path2->len) - ret = 1; - } - return ret; -} - -/* For devices that should be moved to the absolute end of the list */ -static bool devices_delay_end(struct udev *udev, const char *syspath) -{ - static const char *delay_device_list[] = { - "/block/md", - "/block/dm-", - NULL - }; - size_t len; - int i; - - len = strlen(udev_get_sys_path(udev)); - for (i = 0; delay_device_list[i] != NULL; i++) { - if (strstr(&syspath[len], delay_device_list[i]) != NULL) { - dbg(udev, "delaying: %s\n", syspath); - return true; - } - } - return false; -} - -/* For devices that should just be moved a little bit later, just - * before the point where some common path prefix changes. Returns the - * number of characters that make up that common prefix */ -static size_t devices_delay_later(struct udev *udev, const char *syspath) -{ - const char *c; - - /* For sound cards the control device must be enumerated last - * to make sure it's the final device node that gets ACLs - * applied. Applications rely on this fact and use ACL changes - * on the control node as an indicator that the ACL change of - * the entire sound card completed. The kernel makes this - * guarantee when creating those devices, and hence we should - * too when enumerating them. */ - - if ((c = strstr(syspath, "/sound/card"))) { - c += 11; - c += strcspn(c, "/"); - - if (strncmp(c, "/controlC", 9) == 0) - return c - syspath + 1; - } - - return 0; -} - -/** - * udev_enumerate_get_list_entry: - * @udev_enumerate: context - * - * Returns: the first entry of the sorted list of device paths. - */ -UDEV_EXPORT struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate) -{ - if (udev_enumerate == NULL) - return NULL; - if (!udev_enumerate->devices_uptodate) { - unsigned int i; - unsigned int max; - struct syspath *prev = NULL, *move_later = NULL; - size_t move_later_prefix = 0; - - udev_list_cleanup(&udev_enumerate->devices_list); - qsort(udev_enumerate->devices, udev_enumerate->devices_cur, sizeof(struct syspath), syspath_cmp); - - max = udev_enumerate->devices_cur; - for (i = 0; i < max; i++) { - struct syspath *entry = &udev_enumerate->devices[i]; - - /* skip duplicated entries */ - if (prev != NULL && - entry->len == prev->len && - memcmp(entry->syspath, prev->syspath, entry->len) == 0) - continue; - prev = entry; - - /* skip to be delayed devices, and add them to the end of the list */ - if (devices_delay_end(udev_enumerate->udev, entry->syspath)) { - syspath_add(udev_enumerate, entry->syspath); - /* need to update prev here for the case realloc() gives a different address */ - prev = &udev_enumerate->devices[i]; - continue; - } - - /* skip to be delayed devices, and move the to - * the point where the prefix changes. We can - * only move one item at a time. */ - if (!move_later) { - move_later_prefix = devices_delay_later(udev_enumerate->udev, entry->syspath); - - if (move_later_prefix > 0) { - move_later = entry; - continue; - } - } - - if (move_later && - strncmp(entry->syspath, move_later->syspath, move_later_prefix) != 0) { - - udev_list_entry_add(&udev_enumerate->devices_list, move_later->syspath, NULL); - move_later = NULL; - } - - udev_list_entry_add(&udev_enumerate->devices_list, entry->syspath, NULL); - } - - if (move_later) - udev_list_entry_add(&udev_enumerate->devices_list, move_later->syspath, NULL); - - /* add and cleanup delayed devices from end of list */ - for (i = max; i < udev_enumerate->devices_cur; i++) { - struct syspath *entry = &udev_enumerate->devices[i]; - - udev_list_entry_add(&udev_enumerate->devices_list, entry->syspath, NULL); - free(entry->syspath); - } - udev_enumerate->devices_cur = max; - - udev_enumerate->devices_uptodate = true; - } - return udev_list_get_entry(&udev_enumerate->devices_list); -} - -/** - * udev_enumerate_add_match_subsystem: - * @udev_enumerate: context - * @subsystem: filter for a subsystem of the device to include in the list - * - * Returns: 0 on success, otherwise a negative error value. - */ -UDEV_EXPORT int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) -{ - if (udev_enumerate == NULL) - return -EINVAL; - if (subsystem == NULL) - return 0; - if (udev_list_entry_add(&udev_enumerate->subsystem_match_list, subsystem, NULL) == NULL) - return -ENOMEM; - return 0; -} - -/** - * udev_enumerate_add_nomatch_subsystem: - * @udev_enumerate: context - * @subsystem: filter for a subsystem of the device to exclude from the list - * - * Returns: 0 on success, otherwise a negative error value. - */ -UDEV_EXPORT int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) -{ - if (udev_enumerate == NULL) - return -EINVAL; - if (subsystem == NULL) - return 0; - if (udev_list_entry_add(&udev_enumerate->subsystem_nomatch_list, subsystem, NULL) == NULL) - return -ENOMEM; - return 0; -} - -/** - * udev_enumerate_add_match_sysattr: - * @udev_enumerate: context - * @sysattr: filter for a sys attribute at the device to include in the list - * @value: optional value of the sys attribute - * - * Returns: 0 on success, otherwise a negative error value. - */ -UDEV_EXPORT int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value) -{ - if (udev_enumerate == NULL) - return -EINVAL; - if (sysattr == NULL) - return 0; - if (udev_list_entry_add(&udev_enumerate->sysattr_match_list, sysattr, value) == NULL) - return -ENOMEM; - return 0; -} - -/** - * udev_enumerate_add_nomatch_sysattr: - * @udev_enumerate: context - * @sysattr: filter for a sys attribute at the device to exclude from the list - * @value: optional value of the sys attribute - * - * Returns: 0 on success, otherwise a negative error value. - */ -UDEV_EXPORT int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value) -{ - if (udev_enumerate == NULL) - return -EINVAL; - if (sysattr == NULL) - return 0; - if (udev_list_entry_add(&udev_enumerate->sysattr_nomatch_list, sysattr, value) == NULL) - return -ENOMEM; - return 0; -} - -static int match_sysattr_value(struct udev_device *dev, const char *sysattr, const char *match_val) -{ - const char *val = NULL; - bool match = false; - - val = udev_device_get_sysattr_value(dev, sysattr); - if (val == NULL) - goto exit; - if (match_val == NULL) { - match = true; - goto exit; - } - if (fnmatch(match_val, val, 0) == 0) { - match = true; - goto exit; - } -exit: - return match; -} - -/** - * udev_enumerate_add_match_property: - * @udev_enumerate: context - * @property: filter for a property of the device to include in the list - * @value: value of the property - * - * Returns: 0 on success, otherwise a negative error value. - */ -UDEV_EXPORT int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value) -{ - if (udev_enumerate == NULL) - return -EINVAL; - if (property == NULL) - return 0; - if (udev_list_entry_add(&udev_enumerate->properties_match_list, property, value) == NULL) - return -ENOMEM; - return 0; -} - -/** - * udev_enumerate_add_match_tag: - * @udev_enumerate: context - * @tag: filter for a tag of the device to include in the list - * - * Returns: 0 on success, otherwise a negative error value. - */ -UDEV_EXPORT int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag) -{ - if (udev_enumerate == NULL) - return -EINVAL; - if (tag == NULL) - return 0; - if (udev_list_entry_add(&udev_enumerate->tags_match_list, tag, NULL) == NULL) - return -ENOMEM; - return 0; -} - -/** - * udev_enumerate_add_match_parent: - * @udev_enumerate: context - * @parent: parent device where to start searching - * - * Return the devices on the subtree of one given device. The parent - * itself is included in the list. - * - * A reference for the device is held until the udev_enumerate context - * is cleaned up. - * - * Returns: 0 on success, otherwise a negative error value. - */ -UDEV_EXPORT int udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *parent) -{ - if (udev_enumerate == NULL) - return -EINVAL; - if (parent == NULL) - return 0; - if (udev_enumerate->parent_match != NULL) - udev_device_unref(udev_enumerate->parent_match); - udev_enumerate->parent_match = udev_device_ref(parent); - return 0; -} - -/** - * udev_enumerate_add_match_is_initialized: - * @udev_enumerate: context - * - * Match only devices which udev has set up already. This makes - * sure, that the device node permissions and context are properly set - * and that network devices are fully renamed. - * - * Usually, devices which are found in the kernel but not already - * handled by udev, have still pending events. Services should subscribe - * to monitor events and wait for these devices to become ready, instead - * of using uninitialized devices. - * - * For now, this will not affect devices which do not have a device node - * and are not network interfaces. - * - * Returns: 0 on success, otherwise a negative error value. - */ -UDEV_EXPORT int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate) -{ - if (udev_enumerate == NULL) - return -EINVAL; - udev_enumerate->match_is_initialized = true; - return 0; -} - -/** - * udev_enumerate_add_match_sysname: - * @udev_enumerate: context - * @sysname: filter for the name of the device to include in the list - * - * Returns: 0 on success, otherwise a negative error value. - */ -UDEV_EXPORT int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname) -{ - if (udev_enumerate == NULL) - return -EINVAL; - if (sysname == NULL) - return 0; - if (udev_list_entry_add(&udev_enumerate->sysname_match_list, sysname, NULL) == NULL) - return -ENOMEM; - return 0; -} - -static bool match_sysattr(struct udev_enumerate *udev_enumerate, struct udev_device *dev) -{ - struct udev_list_entry *list_entry; - - /* skip list */ - udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->sysattr_nomatch_list)) { - if (match_sysattr_value(dev, udev_list_entry_get_name(list_entry), - udev_list_entry_get_value(list_entry))) - return false; - } - /* include list */ - if (udev_list_get_entry(&udev_enumerate->sysattr_match_list) != NULL) { - udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->sysattr_match_list)) { - /* anything that does not match, will make it FALSE */ - if (!match_sysattr_value(dev, udev_list_entry_get_name(list_entry), - udev_list_entry_get_value(list_entry))) - return false; - } - return true; - } - return true; -} - -static bool match_property(struct udev_enumerate *udev_enumerate, struct udev_device *dev) -{ - struct udev_list_entry *list_entry; - bool match = false; - - /* no match always matches */ - if (udev_list_get_entry(&udev_enumerate->properties_match_list) == NULL) - return true; - - /* loop over matches */ - udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->properties_match_list)) { - const char *match_key = udev_list_entry_get_name(list_entry); - const char *match_value = udev_list_entry_get_value(list_entry); - struct udev_list_entry *property_entry; - - /* loop over device properties */ - udev_list_entry_foreach(property_entry, udev_device_get_properties_list_entry(dev)) { - const char *dev_key = udev_list_entry_get_name(property_entry); - const char *dev_value = udev_list_entry_get_value(property_entry); - - if (fnmatch(match_key, dev_key, 0) != 0) - continue; - if (match_value == NULL && dev_value == NULL) { - match = true; - goto out; - } - if (match_value == NULL || dev_value == NULL) - continue; - if (fnmatch(match_value, dev_value, 0) == 0) { - match = true; - goto out; - } - } - } -out: - return match; -} - -static bool match_tag(struct udev_enumerate *udev_enumerate, struct udev_device *dev) -{ - struct udev_list_entry *list_entry; - - /* no match always matches */ - if (udev_list_get_entry(&udev_enumerate->tags_match_list) == NULL) - return true; - - /* loop over matches */ - udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->tags_match_list)) - if (!udev_device_has_tag(dev, udev_list_entry_get_name(list_entry))) - return false; - - return true; -} - -static bool match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *dev) -{ - const char *parent; - - if (udev_enumerate->parent_match == NULL) - return true; - - parent = udev_device_get_devpath(udev_enumerate->parent_match); - return strncmp(parent, udev_device_get_devpath(dev), strlen(parent)) == 0; -} - -static bool match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname) -{ - struct udev_list_entry *list_entry; - - if (udev_list_get_entry(&udev_enumerate->sysname_match_list) == NULL) - return true; - - udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->sysname_match_list)) { - if (fnmatch(udev_list_entry_get_name(list_entry), sysname, 0) != 0) - continue; - return true; - } - return false; -} - -static int scan_dir_and_add_devices(struct udev_enumerate *udev_enumerate, - const char *basedir, const char *subdir1, const char *subdir2) -{ - struct udev *udev = udev_enumerate_get_udev(udev_enumerate); - char path[UTIL_PATH_SIZE]; - size_t l; - char *s; - DIR *dir; - struct dirent *dent; - - s = path; - l = util_strpcpyl(&s, sizeof(path), udev_get_sys_path(udev), "/", basedir, NULL); - if (subdir1 != NULL) - l = util_strpcpyl(&s, l, "/", subdir1, NULL); - if (subdir2 != NULL) - util_strpcpyl(&s, l, "/", subdir2, NULL); - dir = opendir(path); - if (dir == NULL) - return -ENOENT; - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - char syspath[UTIL_PATH_SIZE]; - struct udev_device *dev; - - if (dent->d_name[0] == '.') - continue; - - if (!match_sysname(udev_enumerate, dent->d_name)) - continue; - - util_strscpyl(syspath, sizeof(syspath), path, "/", dent->d_name, NULL); - dev = udev_device_new_from_syspath(udev_enumerate->udev, syspath); - if (dev == NULL) - continue; - - if (udev_enumerate->match_is_initialized) { - /* - * All devices with a device node or network interfaces - * possibly need udev to adjust the device node permission - * or context, or rename the interface before it can be - * reliably used from other processes. - * - * For now, we can only check these types of devices, we - * might not store a database, and have no way to find out - * for all other types of devices. - */ - if (!udev_device_get_is_initialized(dev) && - (major(udev_device_get_devnum(dev)) > 0 || udev_device_get_ifindex(dev) > 0)) - goto nomatch; - } - if (!match_parent(udev_enumerate, dev)) - goto nomatch; - if (!match_tag(udev_enumerate, dev)) - goto nomatch; - if (!match_property(udev_enumerate, dev)) - goto nomatch; - if (!match_sysattr(udev_enumerate, dev)) - goto nomatch; - - syspath_add(udev_enumerate, udev_device_get_syspath(dev)); -nomatch: - udev_device_unref(dev); - } - closedir(dir); - return 0; -} - -static bool match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) -{ - struct udev_list_entry *list_entry; - - udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->subsystem_nomatch_list)) { - if (fnmatch(udev_list_entry_get_name(list_entry), subsystem, 0) == 0) - return false; - } - if (udev_list_get_entry(&udev_enumerate->subsystem_match_list) != NULL) { - udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->subsystem_match_list)) { - if (fnmatch(udev_list_entry_get_name(list_entry), subsystem, 0) == 0) - return true; - } - return false; - } - return true; -} - -static int scan_dir(struct udev_enumerate *udev_enumerate, const char *basedir, const char *subdir, const char *subsystem) -{ - struct udev *udev = udev_enumerate_get_udev(udev_enumerate); - - char path[UTIL_PATH_SIZE]; - DIR *dir; - struct dirent *dent; - - util_strscpyl(path, sizeof(path), udev_get_sys_path(udev), "/", basedir, NULL); - dir = opendir(path); - if (dir == NULL) - return -1; - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - if (dent->d_name[0] == '.') - continue; - if (!match_subsystem(udev_enumerate, subsystem != NULL ? subsystem : dent->d_name)) - continue; - scan_dir_and_add_devices(udev_enumerate, basedir, dent->d_name, subdir); - } - closedir(dir); - return 0; -} - -/** - * udev_enumerate_add_syspath: - * @udev_enumerate: context - * @syspath: path of a device - * - * Add a device to the list of devices, to retrieve it back sorted in dependency order. - * - * Returns: 0 on success, otherwise a negative error value. - */ -UDEV_EXPORT int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath) -{ - struct udev_device *udev_device; - - if (udev_enumerate == NULL) - return -EINVAL; - if (syspath == NULL) - return 0; - /* resolve to real syspath */ - udev_device = udev_device_new_from_syspath(udev_enumerate->udev, syspath); - if (udev_device == NULL) - return -EINVAL; - syspath_add(udev_enumerate, udev_device_get_syspath(udev_device)); - udev_device_unref(udev_device); - return 0; -} - -static int scan_devices_tags(struct udev_enumerate *udev_enumerate) -{ - struct udev *udev = udev_enumerate_get_udev(udev_enumerate); - struct udev_list_entry *list_entry; - - /* scan only tagged devices, use tags reverse-index, instead of searching all devices in /sys */ - udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->tags_match_list)) { - DIR *dir; - struct dirent *dent; - char path[UTIL_PATH_SIZE]; - - util_strscpyl(path, sizeof(path), udev_get_run_path(udev), "/tags/", - udev_list_entry_get_name(list_entry), NULL); - dir = opendir(path); - if (dir == NULL) - continue; - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - struct udev_device *dev; - - if (dent->d_name[0] == '.') - continue; - - dev = udev_device_new_from_id_filename(udev_enumerate->udev, dent->d_name); - if (dev == NULL) - continue; - - if (!match_subsystem(udev_enumerate, udev_device_get_subsystem(dev))) - goto nomatch; - if (!match_sysname(udev_enumerate, udev_device_get_sysname(dev))) - goto nomatch; - if (!match_parent(udev_enumerate, dev)) - goto nomatch; - if (!match_property(udev_enumerate, dev)) - goto nomatch; - if (!match_sysattr(udev_enumerate, dev)) - goto nomatch; - - syspath_add(udev_enumerate, udev_device_get_syspath(dev)); -nomatch: - udev_device_unref(dev); - } - closedir(dir); - } - return 0; -} - -static int parent_add_child(struct udev_enumerate *enumerate, const char *path) -{ - struct udev_device *dev; - - dev = udev_device_new_from_syspath(enumerate->udev, path); - if (dev == NULL) - return -ENODEV; - - if (!match_subsystem(enumerate, udev_device_get_subsystem(dev))) - return 0; - if (!match_sysname(enumerate, udev_device_get_sysname(dev))) - return 0; - if (!match_property(enumerate, dev)) - return 0; - if (!match_sysattr(enumerate, dev)) - return 0; - - syspath_add(enumerate, udev_device_get_syspath(dev)); - udev_device_unref(dev); - return 1; -} - -static int parent_crawl_children(struct udev_enumerate *enumerate, const char *path, int maxdepth) -{ - DIR *d; - struct dirent *dent; - - d = opendir(path); - if (d == NULL) - return -errno; - - for (dent = readdir(d); dent != NULL; dent = readdir(d)) { - char *child; - - if (dent->d_name[0] == '.') - continue; - if (dent->d_type != DT_DIR) - continue; - if (asprintf(&child, "%s/%s", path, dent->d_name) < 0) - continue; - parent_add_child(enumerate, child); - if (maxdepth > 0) - parent_crawl_children(enumerate, child, maxdepth-1); - free(child); - } - - closedir(d); - return 0; -} - -static int scan_devices_children(struct udev_enumerate *enumerate) -{ - const char *path; - - path = udev_device_get_syspath(enumerate->parent_match); - parent_add_child(enumerate, path); - return parent_crawl_children(enumerate, path, 256); -} - -static int scan_devices_all(struct udev_enumerate *udev_enumerate) -{ - struct udev *udev = udev_enumerate_get_udev(udev_enumerate); - char base[UTIL_PATH_SIZE]; - struct stat statbuf; - - util_strscpyl(base, sizeof(base), udev_get_sys_path(udev), "/subsystem", NULL); - if (stat(base, &statbuf) == 0) { - /* we have /subsystem/, forget all the old stuff */ - dbg(udev, "searching '/subsystem/*/devices/*' dir\n"); - scan_dir(udev_enumerate, "subsystem", "devices", NULL); - } else { - dbg(udev, "searching '/bus/*/devices/*' dir\n"); - scan_dir(udev_enumerate, "bus", "devices", NULL); - dbg(udev, "searching '/class/*' dir\n"); - scan_dir(udev_enumerate, "class", NULL, NULL); - } - return 0; -} - -/** - * udev_enumerate_scan_devices: - * @udev_enumerate: udev enumeration context - * - * Returns: 0 on success, otherwise a negative error value. - **/ -UDEV_EXPORT int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate) -{ - if (udev_enumerate == NULL) - return -EINVAL; - - /* efficiently lookup tags only, we maintain a reverse-index */ - if (udev_list_get_entry(&udev_enumerate->tags_match_list) != NULL) - return scan_devices_tags(udev_enumerate); - - /* walk the subtree of one parent device only */ - if (udev_enumerate->parent_match != NULL) - return scan_devices_children(udev_enumerate); - - /* scan devices of all subsystems */ - return scan_devices_all(udev_enumerate); -} - -/** - * udev_enumerate_scan_subsystems: - * @udev_enumerate: udev enumeration context - * - * Returns: 0 on success, otherwise a negative error value. - **/ -UDEV_EXPORT int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate) -{ - struct udev *udev = udev_enumerate_get_udev(udev_enumerate); - char base[UTIL_PATH_SIZE]; - struct stat statbuf; - const char *subsysdir; - - if (udev_enumerate == NULL) - return -EINVAL; - - /* all kernel modules */ - if (match_subsystem(udev_enumerate, "module")) { - dbg(udev, "searching 'modules/*' dir\n"); - scan_dir_and_add_devices(udev_enumerate, "module", NULL, NULL); - } - - util_strscpyl(base, sizeof(base), udev_get_sys_path(udev), "/subsystem", NULL); - if (stat(base, &statbuf) == 0) - subsysdir = "subsystem"; - else - subsysdir = "bus"; - - /* all subsystems (only buses support coldplug) */ - if (match_subsystem(udev_enumerate, "subsystem")) { - dbg(udev, "searching '%s/*' dir\n", subsysdir); - scan_dir_and_add_devices(udev_enumerate, subsysdir, NULL, NULL); - } - - /* all subsystem drivers */ - if (match_subsystem(udev_enumerate, "drivers")) { - dbg(udev, "searching '%s/*/drivers/*' dir\n", subsysdir); - scan_dir(udev_enumerate, subsysdir, "drivers", "drivers"); - } - return 0; -} diff --git a/src/udev/src/libudev-list.c b/src/udev/src/libudev-list.c deleted file mode 100644 index 4bdef35ae8..0000000000 --- a/src/udev/src/libudev-list.c +++ /dev/null @@ -1,344 +0,0 @@ -/* - * libudev - interface to udev device information - * - * Copyright (C) 2008 Kay Sievers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include - -#include "libudev.h" -#include "libudev-private.h" - -/** - * SECTION:libudev-list - * @short_description: list operation - * - * Libudev list operations. - */ - -/** - * udev_list_entry: - * - * Opaque object representing one entry in a list. An entry contains - * contains a name, and optionally a value. - */ -struct udev_list_entry { - struct udev_list_node node; - struct udev_list *list; - char *name; - char *value; - int num; -}; - -/* the list's head points to itself if empty */ -void udev_list_node_init(struct udev_list_node *list) -{ - list->next = list; - list->prev = list; -} - -int udev_list_node_is_empty(struct udev_list_node *list) -{ - return list->next == list; -} - -static void udev_list_node_insert_between(struct udev_list_node *new, - struct udev_list_node *prev, - struct udev_list_node *next) -{ - next->prev = new; - new->next = next; - new->prev = prev; - prev->next = new; -} - -void udev_list_node_append(struct udev_list_node *new, struct udev_list_node *list) -{ - udev_list_node_insert_between(new, list->prev, list); -} - -void udev_list_node_remove(struct udev_list_node *entry) -{ - struct udev_list_node *prev = entry->prev; - struct udev_list_node *next = entry->next; - - next->prev = prev; - prev->next = next; - - entry->prev = NULL; - entry->next = NULL; -} - -/* return list entry which embeds this node */ -static struct udev_list_entry *list_node_to_entry(struct udev_list_node *node) -{ - char *list; - - list = (char *)node; - list -= offsetof(struct udev_list_entry, node); - return (struct udev_list_entry *)list; -} - -void udev_list_init(struct udev *udev, struct udev_list *list, bool unique) -{ - memset(list, 0x00, sizeof(struct udev_list)); - list->udev = udev; - list->unique = unique; - udev_list_node_init(&list->node); -} - -/* insert entry into a list as the last element */ -void udev_list_entry_append(struct udev_list_entry *new, struct udev_list *list) -{ - /* inserting before the list head make the node the last node in the list */ - udev_list_node_insert_between(&new->node, list->node.prev, &list->node); - new->list = list; -} - -/* insert entry into a list, before a given existing entry */ -void udev_list_entry_insert_before(struct udev_list_entry *new, struct udev_list_entry *entry) -{ - udev_list_node_insert_between(&new->node, entry->node.prev, &entry->node); - new->list = entry->list; -} - -/* binary search in sorted array */ -static int list_search(struct udev_list *list, const char *name) -{ - unsigned int first, last; - - first = 0; - last = list->entries_cur; - while (first < last) { - unsigned int i; - int cmp; - - i = (first + last)/2; - cmp = strcmp(name, list->entries[i]->name); - if (cmp < 0) - last = i; - else if (cmp > 0) - first = i+1; - else - return i; - } - - /* not found, return negative insertion-index+1 */ - return -(first+1); -} - -struct udev_list_entry *udev_list_entry_add(struct udev_list *list, const char *name, const char *value) -{ - struct udev_list_entry *entry; - int i = 0; - - if (list->unique) { - /* lookup existing name or insertion-index */ - i = list_search(list, name); - if (i >= 0) { - entry = list->entries[i]; - - dbg(list->udev, "'%s' is already in the list\n", name); - free(entry->value); - if (value == NULL) { - entry->value = NULL; - dbg(list->udev, "'%s' value unset\n", name); - return entry; - } - entry->value = strdup(value); - if (entry->value == NULL) - return NULL; - dbg(list->udev, "'%s' value replaced with '%s'\n", name, value); - return entry; - } - } - - /* add new name */ - entry = calloc(1, sizeof(struct udev_list_entry)); - if (entry == NULL) - return NULL; - entry->name = strdup(name); - if (entry->name == NULL) { - free(entry); - return NULL; - } - if (value != NULL) { - entry->value = strdup(value); - if (entry->value == NULL) { - free(entry->name); - free(entry); - return NULL; - } - } - - if (list->unique) { - /* allocate or enlarge sorted array if needed */ - if (list->entries_cur >= list->entries_max) { - unsigned int add; - - add = list->entries_max; - if (add < 1) - add = 64; - list->entries = realloc(list->entries, (list->entries_max + add) * sizeof(struct udev_list_entry *)); - if (list->entries == NULL) { - free(entry->name); - free(entry->value); - return NULL; - } - list->entries_max += add; - } - - /* the negative i returned the insertion index */ - i = (-i)-1; - - /* insert into sorted list */ - if ((unsigned int)i < list->entries_cur) - udev_list_entry_insert_before(entry, list->entries[i]); - else - udev_list_entry_append(entry, list); - - /* insert into sorted array */ - memmove(&list->entries[i+1], &list->entries[i], - (list->entries_cur - i) * sizeof(struct udev_list_entry *)); - list->entries[i] = entry; - list->entries_cur++; - } else { - udev_list_entry_append(entry, list); - } - - dbg(list->udev, "'%s=%s' added\n", entry->name, entry->value); - return entry; -} - -void udev_list_entry_delete(struct udev_list_entry *entry) -{ - if (entry->list->entries != NULL) { - int i; - struct udev_list *list = entry->list; - - /* remove entry from sorted array */ - i = list_search(list, entry->name); - if (i >= 0) { - memmove(&list->entries[i], &list->entries[i+1], - ((list->entries_cur-1) - i) * sizeof(struct udev_list_entry *)); - list->entries_cur--; - } - } - - udev_list_node_remove(&entry->node); - free(entry->name); - free(entry->value); - free(entry); -} - -void udev_list_cleanup(struct udev_list *list) -{ - struct udev_list_entry *entry_loop; - struct udev_list_entry *entry_tmp; - - free(list->entries); - list->entries = NULL; - list->entries_cur = 0; - list->entries_max = 0; - udev_list_entry_foreach_safe(entry_loop, entry_tmp, udev_list_get_entry(list)) - udev_list_entry_delete(entry_loop); -} - -struct udev_list_entry *udev_list_get_entry(struct udev_list *list) -{ - if (udev_list_node_is_empty(&list->node)) - return NULL; - return list_node_to_entry(list->node.next); -} - -/** - * udev_list_entry_get_next: - * @list_entry: current entry - * - * Returns: the next entry from the list, #NULL is no more entries are found. - */ -UDEV_EXPORT struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry) -{ - struct udev_list_node *next; - - if (list_entry == NULL) - return NULL; - next = list_entry->node.next; - /* empty list or no more entries */ - if (next == &list_entry->list->node) - return NULL; - return list_node_to_entry(next); -} - -/** - * udev_list_entry_get_by_name: - * @list_entry: current entry - * @name: name string to match - * - * Returns: the entry where @name matched, #NULL if no matching entry is found. - */ -UDEV_EXPORT struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name) -{ - int i; - - if (list_entry == NULL) - return NULL; - - if (!list_entry->list->unique) - return NULL; - - i = list_search(list_entry->list, name); - if (i < 0) - return NULL; - return list_entry->list->entries[i]; -} - -/** - * udev_list_entry_get_name: - * @list_entry: current entry - * - * Returns: the name string of this entry. - */ -UDEV_EXPORT const char *udev_list_entry_get_name(struct udev_list_entry *list_entry) -{ - if (list_entry == NULL) - return NULL; - return list_entry->name; -} - -/** - * udev_list_entry_get_value: - * @list_entry: current entry - * - * Returns: the value string of this entry. - */ -UDEV_EXPORT const char *udev_list_entry_get_value(struct udev_list_entry *list_entry) -{ - if (list_entry == NULL) - return NULL; - return list_entry->value; -} - -int udev_list_entry_get_num(struct udev_list_entry *list_entry) -{ - if (list_entry == NULL) - return -EINVAL; - return list_entry->num; -} - -void udev_list_entry_set_num(struct udev_list_entry *list_entry, int num) -{ - if (list_entry == NULL) - return; - list_entry->num = num; -} diff --git a/src/udev/src/libudev-monitor.c b/src/udev/src/libudev-monitor.c deleted file mode 100644 index 77dc55572f..0000000000 --- a/src/udev/src/libudev-monitor.c +++ /dev/null @@ -1,874 +0,0 @@ -/* - * libudev - interface to udev device information - * - * Copyright (C) 2008-2010 Kay Sievers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libudev.h" -#include "libudev-private.h" - -/** - * SECTION:libudev-monitor - * @short_description: device event source - * - * Connects to a device event source. - */ - -/** - * udev_monitor: - * - * Opaque object handling an event source. - */ -struct udev_monitor { - struct udev *udev; - int refcount; - int sock; - struct sockaddr_nl snl; - struct sockaddr_nl snl_trusted_sender; - struct sockaddr_nl snl_destination; - struct sockaddr_un sun; - socklen_t addrlen; - struct udev_list filter_subsystem_list; - struct udev_list filter_tag_list; - bool bound; -}; - -enum udev_monitor_netlink_group { - UDEV_MONITOR_NONE, - UDEV_MONITOR_KERNEL, - UDEV_MONITOR_UDEV, -}; - -#define UDEV_MONITOR_MAGIC 0xfeedcafe -struct udev_monitor_netlink_header { - /* "libudev" prefix to distinguish libudev and kernel messages */ - char prefix[8]; - /* - * magic to protect against daemon <-> library message format mismatch - * used in the kernel from socket filter rules; needs to be stored in network order - */ - unsigned int magic; - /* total length of header structure known to the sender */ - unsigned int header_size; - /* properties string buffer */ - unsigned int properties_off; - unsigned int properties_len; - /* - * hashes of primary device properties strings, to let libudev subscribers - * use in-kernel socket filters; values need to be stored in network order - */ - unsigned int filter_subsystem_hash; - unsigned int filter_devtype_hash; - unsigned int filter_tag_bloom_hi; - unsigned int filter_tag_bloom_lo; -}; - -static struct udev_monitor *udev_monitor_new(struct udev *udev) -{ - struct udev_monitor *udev_monitor; - - udev_monitor = calloc(1, sizeof(struct udev_monitor)); - if (udev_monitor == NULL) - return NULL; - udev_monitor->refcount = 1; - udev_monitor->udev = udev; - udev_list_init(udev, &udev_monitor->filter_subsystem_list, false); - udev_list_init(udev, &udev_monitor->filter_tag_list, true); - return udev_monitor; -} - -/** - * udev_monitor_new_from_socket: - * @udev: udev library context - * @socket_path: unix socket path - * - * This function should not be used in any new application. The - * kernel's netlink socket multiplexes messages to all interested - * clients. Creating custom sockets from udev to applications - * should be avoided. - * - * Create a new udev monitor and connect to a specified socket. The - * path to a socket either points to an existing socket file, or if - * the socket path starts with a '@' character, an abstract namespace - * socket will be used. - * - * A socket file will not be created. If it does not already exist, - * it will fall-back and connect to an abstract namespace socket with - * the given path. The permissions adjustment of a socket file, as - * well as the later cleanup, needs to be done by the caller. - * - * The initial refcount is 1, and needs to be decremented to - * release the resources of the udev monitor. - * - * Returns: a new udev monitor, or #NULL, in case of an error - **/ -UDEV_EXPORT struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char *socket_path) -{ - struct udev_monitor *udev_monitor; - struct stat statbuf; - - if (udev == NULL) - return NULL; - if (socket_path == NULL) - return NULL; - udev_monitor = udev_monitor_new(udev); - if (udev_monitor == NULL) - return NULL; - - udev_monitor->sun.sun_family = AF_LOCAL; - if (socket_path[0] == '@') { - /* translate leading '@' to abstract namespace */ - util_strscpy(udev_monitor->sun.sun_path, sizeof(udev_monitor->sun.sun_path), socket_path); - udev_monitor->sun.sun_path[0] = '\0'; - udev_monitor->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(socket_path); - } else if (stat(socket_path, &statbuf) == 0 && S_ISSOCK(statbuf.st_mode)) { - /* existing socket file */ - util_strscpy(udev_monitor->sun.sun_path, sizeof(udev_monitor->sun.sun_path), socket_path); - udev_monitor->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(socket_path); - } else { - /* no socket file, assume abstract namespace socket */ - util_strscpy(&udev_monitor->sun.sun_path[1], sizeof(udev_monitor->sun.sun_path)-1, socket_path); - udev_monitor->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(socket_path)+1; - } - udev_monitor->sock = socket(AF_LOCAL, SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); - if (udev_monitor->sock == -1) { - err(udev, "error getting socket: %m\n"); - free(udev_monitor); - return NULL; - } - - dbg(udev, "monitor %p created with '%s'\n", udev_monitor, socket_path); - return udev_monitor; -} - -struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const char *name, int fd) -{ - struct udev_monitor *udev_monitor; - unsigned int group; - - if (udev == NULL) - return NULL; - - if (name == NULL) - group = UDEV_MONITOR_NONE; - else if (strcmp(name, "udev") == 0) - group = UDEV_MONITOR_UDEV; - else if (strcmp(name, "kernel") == 0) - group = UDEV_MONITOR_KERNEL; - else - return NULL; - - udev_monitor = udev_monitor_new(udev); - if (udev_monitor == NULL) - return NULL; - - if (fd < 0) { - udev_monitor->sock = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_KOBJECT_UEVENT); - if (udev_monitor->sock == -1) { - err(udev, "error getting socket: %m\n"); - free(udev_monitor); - return NULL; - } - } else { - udev_monitor->bound = true; - udev_monitor->sock = fd; - } - - udev_monitor->snl.nl_family = AF_NETLINK; - udev_monitor->snl.nl_groups = group; - - /* default destination for sending */ - udev_monitor->snl_destination.nl_family = AF_NETLINK; - udev_monitor->snl_destination.nl_groups = UDEV_MONITOR_UDEV; - - dbg(udev, "monitor %p created with NETLINK_KOBJECT_UEVENT (%u)\n", udev_monitor, group); - return udev_monitor; -} - -/** - * udev_monitor_new_from_netlink: - * @udev: udev library context - * @name: name of event source - * - * Create new udev monitor and connect to a specified event - * source. Valid sources identifiers are "udev" and "kernel". - * - * Applications should usually not connect directly to the - * "kernel" events, because the devices might not be useable - * at that time, before udev has configured them, and created - * device nodes. Accessing devices at the same time as udev, - * might result in unpredictable behavior. The "udev" events - * are sent out after udev has finished its event processing, - * all rules have been processed, and needed device nodes are - * created. - * - * The initial refcount is 1, and needs to be decremented to - * release the resources of the udev monitor. - * - * Returns: a new udev monitor, or #NULL, in case of an error - **/ -UDEV_EXPORT struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name) -{ - return udev_monitor_new_from_netlink_fd(udev, name, -1); -} - -static inline void bpf_stmt(struct sock_filter *inss, unsigned int *i, - unsigned short code, unsigned int data) -{ - struct sock_filter *ins = &inss[*i]; - - ins->code = code; - ins->k = data; - (*i)++; -} - -static inline void bpf_jmp(struct sock_filter *inss, unsigned int *i, - unsigned short code, unsigned int data, - unsigned short jt, unsigned short jf) -{ - struct sock_filter *ins = &inss[*i]; - - ins->code = code; - ins->jt = jt; - ins->jf = jf; - ins->k = data; - (*i)++; -} - -/** - * udev_monitor_filter_update: - * @udev_monitor: monitor - * - * Update the installed socket filter. This is only needed, - * if the filter was removed or changed. - * - * Returns: 0 on success, otherwise a negative error value. - */ -UDEV_EXPORT int udev_monitor_filter_update(struct udev_monitor *udev_monitor) -{ - struct sock_filter ins[512]; - struct sock_fprog filter; - unsigned int i; - struct udev_list_entry *list_entry; - int err; - - if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) == NULL && - udev_list_get_entry(&udev_monitor->filter_tag_list) == NULL) - return 0; - - memset(ins, 0x00, sizeof(ins)); - i = 0; - - /* load magic in A */ - bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, magic)); - /* jump if magic matches */ - bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, UDEV_MONITOR_MAGIC, 1, 0); - /* wrong magic, pass packet */ - bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff); - - if (udev_list_get_entry(&udev_monitor->filter_tag_list) != NULL) { - int tag_matches; - - /* count tag matches, to calculate end of tag match block */ - tag_matches = 0; - udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_tag_list)) - tag_matches++; - - /* add all tags matches */ - udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_tag_list)) { - uint64_t tag_bloom_bits = util_string_bloom64(udev_list_entry_get_name(list_entry)); - uint32_t tag_bloom_hi = tag_bloom_bits >> 32; - uint32_t tag_bloom_lo = tag_bloom_bits & 0xffffffff; - - /* load device bloom bits in A */ - bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_tag_bloom_hi)); - /* clear bits (tag bits & bloom bits) */ - bpf_stmt(ins, &i, BPF_ALU|BPF_AND|BPF_K, tag_bloom_hi); - /* jump to next tag if it does not match */ - bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, tag_bloom_hi, 0, 3); - - /* load device bloom bits in A */ - bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_tag_bloom_lo)); - /* clear bits (tag bits & bloom bits) */ - bpf_stmt(ins, &i, BPF_ALU|BPF_AND|BPF_K, tag_bloom_lo); - /* jump behind end of tag match block if tag matches */ - tag_matches--; - bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, tag_bloom_lo, 1 + (tag_matches * 6), 0); - } - - /* nothing matched, drop packet */ - bpf_stmt(ins, &i, BPF_RET|BPF_K, 0); - } - - /* add all subsystem matches */ - if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) != NULL) { - udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_subsystem_list)) { - unsigned int hash = util_string_hash32(udev_list_entry_get_name(list_entry)); - - /* load device subsystem value in A */ - bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_subsystem_hash)); - if (udev_list_entry_get_value(list_entry) == NULL) { - /* jump if subsystem does not match */ - bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1); - } else { - /* jump if subsystem does not match */ - bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 3); - - /* load device devtype value in A */ - bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_devtype_hash)); - /* jump if value does not match */ - hash = util_string_hash32(udev_list_entry_get_value(list_entry)); - bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1); - } - - /* matched, pass packet */ - bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff); - - if (i+1 >= ARRAY_SIZE(ins)) - return -1; - } - - /* nothing matched, drop packet */ - bpf_stmt(ins, &i, BPF_RET|BPF_K, 0); - } - - /* matched, pass packet */ - bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff); - - /* install filter */ - memset(&filter, 0x00, sizeof(filter)); - filter.len = i; - filter.filter = ins; - err = setsockopt(udev_monitor->sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)); - return err; -} - -int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct udev_monitor *sender) -{ - udev_monitor->snl_trusted_sender.nl_pid = sender->snl.nl_pid; - return 0; -} -/** - * udev_monitor_enable_receiving: - * @udev_monitor: the monitor which should receive events - * - * Binds the @udev_monitor socket to the event source. - * - * Returns: 0 on success, otherwise a negative error value. - */ -UDEV_EXPORT int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) -{ - int err = 0; - const int on = 1; - - if (udev_monitor->sun.sun_family != 0) { - if (!udev_monitor->bound) { - err = bind(udev_monitor->sock, - (struct sockaddr *)&udev_monitor->sun, udev_monitor->addrlen); - if (err == 0) - udev_monitor->bound = true; - } - } else if (udev_monitor->snl.nl_family != 0) { - udev_monitor_filter_update(udev_monitor); - if (!udev_monitor->bound) { - err = bind(udev_monitor->sock, - (struct sockaddr *)&udev_monitor->snl, sizeof(struct sockaddr_nl)); - if (err == 0) - udev_monitor->bound = true; - } - if (err == 0) { - struct sockaddr_nl snl; - socklen_t addrlen; - - /* - * get the address the kernel has assigned us - * it is usually, but not necessarily the pid - */ - addrlen = sizeof(struct sockaddr_nl); - err = getsockname(udev_monitor->sock, (struct sockaddr *)&snl, &addrlen); - if (err == 0) - udev_monitor->snl.nl_pid = snl.nl_pid; - } - } else { - return -EINVAL; - } - - if (err < 0) { - err(udev_monitor->udev, "bind failed: %m\n"); - return err; - } - - /* enable receiving of sender credentials */ - setsockopt(udev_monitor->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); - return 0; -} - -/** - * udev_monitor_set_receive_buffer_size: - * @udev_monitor: the monitor which should receive events - * @size: the size in bytes - * - * Set the size of the kernel socket buffer. This call needs the - * appropriate privileges to succeed. - * - * Returns: 0 on success, otherwise -1 on error. - */ -UDEV_EXPORT int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size) -{ - if (udev_monitor == NULL) - return -1; - return setsockopt(udev_monitor->sock, SOL_SOCKET, SO_RCVBUFFORCE, &size, sizeof(size)); -} - -int udev_monitor_disconnect(struct udev_monitor *udev_monitor) -{ - int err; - - err = close(udev_monitor->sock); - udev_monitor->sock = -1; - return err; -} - -/** - * udev_monitor_ref: - * @udev_monitor: udev monitor - * - * Take a reference of a udev monitor. - * - * Returns: the passed udev monitor - **/ -UDEV_EXPORT struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor) -{ - if (udev_monitor == NULL) - return NULL; - udev_monitor->refcount++; - return udev_monitor; -} - -/** - * udev_monitor_unref: - * @udev_monitor: udev monitor - * - * Drop a reference of a udev monitor. If the refcount reaches zero, - * the bound socket will be closed, and the resources of the monitor - * will be released. - * - **/ -UDEV_EXPORT void udev_monitor_unref(struct udev_monitor *udev_monitor) -{ - if (udev_monitor == NULL) - return; - udev_monitor->refcount--; - if (udev_monitor->refcount > 0) - return; - if (udev_monitor->sock >= 0) - close(udev_monitor->sock); - udev_list_cleanup(&udev_monitor->filter_subsystem_list); - udev_list_cleanup(&udev_monitor->filter_tag_list); - dbg(udev_monitor->udev, "monitor %p released\n", udev_monitor); - free(udev_monitor); -} - -/** - * udev_monitor_get_udev: - * @udev_monitor: udev monitor - * - * Retrieve the udev library context the monitor was created with. - * - * Returns: the udev library context - **/ -UDEV_EXPORT struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor) -{ - if (udev_monitor == NULL) - return NULL; - return udev_monitor->udev; -} - -/** - * udev_monitor_get_fd: - * @udev_monitor: udev monitor - * - * Retrieve the socket file descriptor associated with the monitor. - * - * Returns: the socket file descriptor - **/ -UDEV_EXPORT int udev_monitor_get_fd(struct udev_monitor *udev_monitor) -{ - if (udev_monitor == NULL) - return -1; - return udev_monitor->sock; -} - -static int passes_filter(struct udev_monitor *udev_monitor, struct udev_device *udev_device) -{ - struct udev_list_entry *list_entry; - - if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) == NULL) - goto tag; - udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_subsystem_list)) { - const char *subsys = udev_list_entry_get_name(list_entry); - const char *dsubsys = udev_device_get_subsystem(udev_device); - const char *devtype; - const char *ddevtype; - - if (strcmp(dsubsys, subsys) != 0) - continue; - - devtype = udev_list_entry_get_value(list_entry); - if (devtype == NULL) - goto tag; - ddevtype = udev_device_get_devtype(udev_device); - if (ddevtype == NULL) - continue; - if (strcmp(ddevtype, devtype) == 0) - goto tag; - } - return 0; - -tag: - if (udev_list_get_entry(&udev_monitor->filter_tag_list) == NULL) - return 1; - udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_tag_list)) { - const char *tag = udev_list_entry_get_name(list_entry); - - if (udev_device_has_tag(udev_device, tag)) - return 1; - } - return 0; -} - -/** - * udev_monitor_receive_device: - * @udev_monitor: udev monitor - * - * Receive data from the udev monitor socket, allocate a new udev - * device, fill in the received data, and return the device. - * - * Only socket connections with uid=0 are accepted. - * - * The initial refcount is 1, and needs to be decremented to - * release the resources of the udev device. - * - * Returns: a new udev device, or #NULL, in case of an error - **/ -UDEV_EXPORT struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor) -{ - struct udev_device *udev_device; - struct msghdr smsg; - struct iovec iov; - char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; - struct cmsghdr *cmsg; - struct sockaddr_nl snl; - struct ucred *cred; - char buf[8192]; - ssize_t buflen; - ssize_t bufpos; - struct udev_monitor_netlink_header *nlh; - -retry: - if (udev_monitor == NULL) - return NULL; - iov.iov_base = &buf; - iov.iov_len = sizeof(buf); - memset (&smsg, 0x00, sizeof(struct msghdr)); - smsg.msg_iov = &iov; - smsg.msg_iovlen = 1; - smsg.msg_control = cred_msg; - smsg.msg_controllen = sizeof(cred_msg); - - if (udev_monitor->snl.nl_family != 0) { - smsg.msg_name = &snl; - smsg.msg_namelen = sizeof(snl); - } - - buflen = recvmsg(udev_monitor->sock, &smsg, 0); - if (buflen < 0) { - if (errno != EINTR) - info(udev_monitor->udev, "unable to receive message\n"); - return NULL; - } - - if (buflen < 32 || (size_t)buflen >= sizeof(buf)) { - info(udev_monitor->udev, "invalid message length\n"); - return NULL; - } - - if (udev_monitor->snl.nl_family != 0) { - if (snl.nl_groups == 0) { - /* unicast message, check if we trust the sender */ - if (udev_monitor->snl_trusted_sender.nl_pid == 0 || - snl.nl_pid != udev_monitor->snl_trusted_sender.nl_pid) { - info(udev_monitor->udev, "unicast netlink message ignored\n"); - return NULL; - } - } else if (snl.nl_groups == UDEV_MONITOR_KERNEL) { - if (snl.nl_pid > 0) { - info(udev_monitor->udev, "multicast kernel netlink message from pid %d ignored\n", - snl.nl_pid); - return NULL; - } - } - } - - cmsg = CMSG_FIRSTHDR(&smsg); - if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { - info(udev_monitor->udev, "no sender credentials received, message ignored\n"); - return NULL; - } - - cred = (struct ucred *)CMSG_DATA(cmsg); - if (cred->uid != 0) { - info(udev_monitor->udev, "sender uid=%d, message ignored\n", cred->uid); - return NULL; - } - - if (memcmp(buf, "libudev", 8) == 0) { - /* udev message needs proper version magic */ - nlh = (struct udev_monitor_netlink_header *) buf; - if (nlh->magic != htonl(UDEV_MONITOR_MAGIC)) { - err(udev_monitor->udev, "unrecognized message signature (%x != %x)\n", - nlh->magic, htonl(UDEV_MONITOR_MAGIC)); - return NULL; - } - if (nlh->properties_off+32 > buflen) - return NULL; - bufpos = nlh->properties_off; - } else { - /* kernel message with header */ - bufpos = strlen(buf) + 1; - if ((size_t)bufpos < sizeof("a@/d") || bufpos >= buflen) { - info(udev_monitor->udev, "invalid message length\n"); - return NULL; - } - - /* check message header */ - if (strstr(buf, "@/") == NULL) { - info(udev_monitor->udev, "unrecognized message header\n"); - return NULL; - } - } - - udev_device = udev_device_new(udev_monitor->udev); - if (udev_device == NULL) - return NULL; - udev_device_set_info_loaded(udev_device); - - while (bufpos < buflen) { - char *key; - size_t keylen; - - key = &buf[bufpos]; - keylen = strlen(key); - if (keylen == 0) - break; - bufpos += keylen + 1; - udev_device_add_property_from_string_parse(udev_device, key); - } - - if (udev_device_add_property_from_string_parse_finish(udev_device) < 0) { - info(udev_monitor->udev, "missing values, invalid device\n"); - udev_device_unref(udev_device); - return NULL; - } - - /* skip device, if it does not pass the current filter */ - if (!passes_filter(udev_monitor, udev_device)) { - struct pollfd pfd[1]; - int rc; - - udev_device_unref(udev_device); - - /* if something is queued, get next device */ - pfd[0].fd = udev_monitor->sock; - pfd[0].events = POLLIN; - rc = poll(pfd, 1, 0); - if (rc > 0) - goto retry; - return NULL; - } - - return udev_device; -} - -int udev_monitor_send_device(struct udev_monitor *udev_monitor, - struct udev_monitor *destination, struct udev_device *udev_device) -{ - const char *buf; - ssize_t blen; - ssize_t count; - - blen = udev_device_get_properties_monitor_buf(udev_device, &buf); - if (blen < 32) - return -EINVAL; - - if (udev_monitor->sun.sun_family != 0) { - struct msghdr smsg; - struct iovec iov[2]; - const char *action; - char header[2048]; - char *s; - - /* header @ */ - action = udev_device_get_action(udev_device); - if (action == NULL) - return -EINVAL; - s = header; - if (util_strpcpyl(&s, sizeof(header), action, "@", udev_device_get_devpath(udev_device), NULL) == 0) - return -EINVAL; - iov[0].iov_base = header; - iov[0].iov_len = (s - header)+1; - - /* add properties list */ - iov[1].iov_base = (char *)buf; - iov[1].iov_len = blen; - - memset(&smsg, 0x00, sizeof(struct msghdr)); - smsg.msg_iov = iov; - smsg.msg_iovlen = 2; - smsg.msg_name = &udev_monitor->sun; - smsg.msg_namelen = udev_monitor->addrlen; - count = sendmsg(udev_monitor->sock, &smsg, 0); - info(udev_monitor->udev, "passed %zi bytes to socket monitor %p\n", count, udev_monitor); - return count; - } - - if (udev_monitor->snl.nl_family != 0) { - struct msghdr smsg; - struct iovec iov[2]; - const char *val; - struct udev_monitor_netlink_header nlh; - struct udev_list_entry *list_entry; - uint64_t tag_bloom_bits; - - /* add versioned header */ - memset(&nlh, 0x00, sizeof(struct udev_monitor_netlink_header)); - memcpy(nlh.prefix, "libudev", 8); - nlh.magic = htonl(UDEV_MONITOR_MAGIC); - nlh.header_size = sizeof(struct udev_monitor_netlink_header); - val = udev_device_get_subsystem(udev_device); - nlh.filter_subsystem_hash = htonl(util_string_hash32(val)); - val = udev_device_get_devtype(udev_device); - if (val != NULL) - nlh.filter_devtype_hash = htonl(util_string_hash32(val)); - iov[0].iov_base = &nlh; - iov[0].iov_len = sizeof(struct udev_monitor_netlink_header); - - /* add tag bloom filter */ - tag_bloom_bits = 0; - udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(udev_device)) - tag_bloom_bits |= util_string_bloom64(udev_list_entry_get_name(list_entry)); - if (tag_bloom_bits > 0) { - nlh.filter_tag_bloom_hi = htonl(tag_bloom_bits >> 32); - nlh.filter_tag_bloom_lo = htonl(tag_bloom_bits & 0xffffffff); - } - - /* add properties list */ - nlh.properties_off = iov[0].iov_len; - nlh.properties_len = blen; - iov[1].iov_base = (char *)buf; - iov[1].iov_len = blen; - - memset(&smsg, 0x00, sizeof(struct msghdr)); - smsg.msg_iov = iov; - smsg.msg_iovlen = 2; - /* - * Use custom address for target, or the default one. - * - * If we send to a multicast group, we will get - * ECONNREFUSED, which is expected. - */ - if (destination != NULL) - smsg.msg_name = &destination->snl; - else - smsg.msg_name = &udev_monitor->snl_destination; - smsg.msg_namelen = sizeof(struct sockaddr_nl); - count = sendmsg(udev_monitor->sock, &smsg, 0); - info(udev_monitor->udev, "passed %zi bytes to netlink monitor %p\n", count, udev_monitor); - return count; - } - - return -EINVAL; -} - -/** - * udev_monitor_filter_add_match_subsystem_devtype: - * @udev_monitor: the monitor - * @subsystem: the subsystem value to match the incoming devices against - * @devtype: the devtype value to match the incoming devices against - * - * This filter is efficiently executed inside the kernel, and libudev subscribers - * will usually not be woken up for devices which do not match. - * - * The filter must be installed before the monitor is switched to listening mode. - * - * Returns: 0 on success, otherwise a negative error value. - */ -UDEV_EXPORT int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype) -{ - if (udev_monitor == NULL) - return -EINVAL; - if (subsystem == NULL) - return -EINVAL; - if (udev_list_entry_add(&udev_monitor->filter_subsystem_list, subsystem, devtype) == NULL) - return -ENOMEM; - return 0; -} - -/** - * udev_monitor_filter_add_match_tag: - * @udev_monitor: the monitor - * @tag: the name of a tag - * - * This filter is efficiently executed inside the kernel, and libudev subscribers - * will usually not be woken up for devices which do not match. - * - * The filter must be installed before the monitor is switched to listening mode. - * - * Returns: 0 on success, otherwise a negative error value. - */ -UDEV_EXPORT int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag) -{ - if (udev_monitor == NULL) - return -EINVAL; - if (tag == NULL) - return -EINVAL; - if (udev_list_entry_add(&udev_monitor->filter_tag_list, tag, NULL) == NULL) - return -ENOMEM; - return 0; -} - -/** - * udev_monitor_filter_remove: - * @udev_monitor: monitor - * - * Remove all filters from monitor. - * - * Returns: 0 on success, otherwise a negative error value. - */ -UDEV_EXPORT int udev_monitor_filter_remove(struct udev_monitor *udev_monitor) -{ - static struct sock_fprog filter = { 0, NULL }; - - udev_list_cleanup(&udev_monitor->filter_subsystem_list); - return setsockopt(udev_monitor->sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)); -} diff --git a/src/udev/src/libudev-private.h b/src/udev/src/libudev-private.h deleted file mode 100644 index 5f5c64a63d..0000000000 --- a/src/udev/src/libudev-private.h +++ /dev/null @@ -1,213 +0,0 @@ -/* - * libudev - interface to udev device information - * - * Copyright (C) 2008-2010 Kay Sievers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - */ - -#ifndef _LIBUDEV_PRIVATE_H_ -#define _LIBUDEV_PRIVATE_H_ - -#include -#include -#include -#include -#include "libudev.h" - -#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) -#define READ_END 0 -#define WRITE_END 1 - -static inline void __attribute__((always_inline, format(printf, 2, 3))) -udev_log_null(struct udev *udev, const char *format, ...) {} - -#define udev_log_cond(udev, prio, arg...) \ - do { \ - if (udev_get_log_priority(udev) >= prio) \ - udev_log(udev, prio, __FILE__, __LINE__, __FUNCTION__, ## arg); \ - } while (0) - -#ifdef ENABLE_LOGGING -# ifdef ENABLE_DEBUG -# define dbg(udev, arg...) udev_log_cond(udev, LOG_DEBUG, ## arg) -# else -# define dbg(udev, arg...) udev_log_null(udev, ## arg) -# endif -# define info(udev, arg...) udev_log_cond(udev, LOG_INFO, ## arg) -# define err(udev, arg...) udev_log_cond(udev, LOG_ERR, ## arg) -#else -# define dbg(udev, arg...) udev_log_null(udev, ## arg) -# define info(udev, arg...) udev_log_null(udev, ## arg) -# define err(udev, arg...) udev_log_null(udev, ## arg) -#endif - -#define UDEV_EXPORT __attribute__ ((visibility("default"))) - -static inline void udev_log_init(const char *program_name) -{ - openlog(program_name, LOG_PID | LOG_CONS, LOG_DAEMON); -} - -static inline void udev_log_close(void) -{ - closelog(); -} - -/* libudev.c */ -void udev_log(struct udev *udev, - int priority, const char *file, int line, const char *fn, - const char *format, ...) - __attribute__((format(printf, 6, 7))); -int udev_get_rules_path(struct udev *udev, char **path[], unsigned long long *ts_usec[]); -struct udev_list_entry *udev_add_property(struct udev *udev, const char *key, const char *value); -struct udev_list_entry *udev_get_properties_list_entry(struct udev *udev); - -/* libudev-device.c */ -struct udev_device *udev_device_new(struct udev *udev); -struct udev_device *udev_device_new_from_id_filename(struct udev *udev, char *id); -mode_t udev_device_get_devnode_mode(struct udev_device *udev_device); -int udev_device_set_syspath(struct udev_device *udev_device, const char *syspath); -int udev_device_set_devnode(struct udev_device *udev_device, const char *devnode); -int udev_device_add_devlink(struct udev_device *udev_device, const char *devlink, int unique); -void udev_device_cleanup_devlinks_list(struct udev_device *udev_device); -struct udev_list_entry *udev_device_add_property(struct udev_device *udev_device, const char *key, const char *value); -void udev_device_add_property_from_string_parse(struct udev_device *udev_device, const char *property); -int udev_device_add_property_from_string_parse_finish(struct udev_device *udev_device); -char **udev_device_get_properties_envp(struct udev_device *udev_device); -ssize_t udev_device_get_properties_monitor_buf(struct udev_device *udev_device, const char **buf); -int udev_device_read_db(struct udev_device *udev_device, const char *dbfile); -int udev_device_read_uevent_file(struct udev_device *udev_device); -int udev_device_set_action(struct udev_device *udev_device, const char *action); -const char *udev_device_get_devpath_old(struct udev_device *udev_device); -const char *udev_device_get_id_filename(struct udev_device *udev_device); -void udev_device_set_is_initialized(struct udev_device *udev_device); -int udev_device_add_tag(struct udev_device *udev_device, const char *tag); -void udev_device_cleanup_tags_list(struct udev_device *udev_device); -unsigned long long udev_device_get_usec_initialized(struct udev_device *udev_device); -void udev_device_set_usec_initialized(struct udev_device *udev_device, unsigned long long usec_initialized); -int udev_device_get_devlink_priority(struct udev_device *udev_device); -int udev_device_set_devlink_priority(struct udev_device *udev_device, int prio); -int udev_device_get_watch_handle(struct udev_device *udev_device); -int udev_device_set_watch_handle(struct udev_device *udev_device, int handle); -int udev_device_get_ifindex(struct udev_device *udev_device); -void udev_device_set_info_loaded(struct udev_device *device); -bool udev_device_get_db_persist(struct udev_device *udev_device); -void udev_device_set_db_persist(struct udev_device *udev_device); - -/* libudev-device-private.c */ -int udev_device_update_db(struct udev_device *udev_device); -int udev_device_delete_db(struct udev_device *udev_device); -int udev_device_tag_index(struct udev_device *dev, struct udev_device *dev_old, bool add); - -/* libudev-monitor.c - netlink/unix socket communication */ -int udev_monitor_disconnect(struct udev_monitor *udev_monitor); -int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct udev_monitor *sender); -int udev_monitor_send_device(struct udev_monitor *udev_monitor, - struct udev_monitor *destination, struct udev_device *udev_device); -struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const char *name, int fd); - -/* libudev-list.c */ -struct udev_list_node { - struct udev_list_node *next, *prev; -}; -struct udev_list { - struct udev *udev; - struct udev_list_node node; - struct udev_list_entry **entries; - unsigned int entries_cur; - unsigned int entries_max; - bool unique; -}; -#define UDEV_LIST(list) struct udev_list_node list = { &(list), &(list) } -void udev_list_node_init(struct udev_list_node *list); -int udev_list_node_is_empty(struct udev_list_node *list); -void udev_list_node_append(struct udev_list_node *new, struct udev_list_node *list); -void udev_list_node_remove(struct udev_list_node *entry); -#define udev_list_node_foreach(node, list) \ - for (node = (list)->next; \ - node != list; \ - node = (node)->next) -#define udev_list_node_foreach_safe(node, tmp, list) \ - for (node = (list)->next, tmp = (node)->next; \ - node != list; \ - node = tmp, tmp = (tmp)->next) -void udev_list_init(struct udev *udev, struct udev_list *list, bool unique); -void udev_list_cleanup(struct udev_list *list); -struct udev_list_entry *udev_list_get_entry(struct udev_list *list); -struct udev_list_entry *udev_list_entry_add(struct udev_list *list, const char *name, const char *value); -void udev_list_entry_delete(struct udev_list_entry *entry); -void udev_list_entry_insert_before(struct udev_list_entry *new, struct udev_list_entry *entry); -void udev_list_entry_append(struct udev_list_entry *new, struct udev_list *list); -int udev_list_entry_get_num(struct udev_list_entry *list_entry); -void udev_list_entry_set_num(struct udev_list_entry *list_entry, int num); -#define udev_list_entry_foreach_safe(entry, tmp, first) \ - for (entry = first, tmp = udev_list_entry_get_next(entry); \ - entry != NULL; \ - entry = tmp, tmp = udev_list_entry_get_next(tmp)) - -/* libudev-queue.c */ -unsigned long long int udev_get_kernel_seqnum(struct udev *udev); -int udev_queue_read_seqnum(FILE *queue_file, unsigned long long int *seqnum); -ssize_t udev_queue_read_devpath(FILE *queue_file, char *devpath, size_t size); -ssize_t udev_queue_skip_devpath(FILE *queue_file); - -/* libudev-queue-private.c */ -struct udev_queue_export *udev_queue_export_new(struct udev *udev); -struct udev_queue_export *udev_queue_export_unref(struct udev_queue_export *udev_queue_export); -void udev_queue_export_cleanup(struct udev_queue_export *udev_queue_export); -int udev_queue_export_device_queued(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device); -int udev_queue_export_device_finished(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device); - -/* libudev-util.c */ -#define UTIL_PATH_SIZE 1024 -#define UTIL_NAME_SIZE 512 -#define UTIL_LINE_SIZE 16384 -#define UDEV_ALLOWED_CHARS_INPUT "/ $%?," -ssize_t util_get_sys_core_link_value(struct udev *udev, const char *slink, const char *syspath, char *value, size_t size); -int util_resolve_sys_link(struct udev *udev, char *syspath, size_t size); -int util_log_priority(const char *priority); -size_t util_path_encode(const char *src, char *dest, size_t size); -size_t util_path_decode(char *s); -void util_remove_trailing_chars(char *path, char c); -size_t util_strpcpy(char **dest, size_t size, const char *src); -size_t util_strpcpyl(char **dest, size_t size, const char *src, ...) __attribute__((sentinel)); -size_t util_strscpy(char *dest, size_t size, const char *src); -size_t util_strscpyl(char *dest, size_t size, const char *src, ...) __attribute__((sentinel)); -int util_replace_whitespace(const char *str, char *to, size_t len); -int util_replace_chars(char *str, const char *white); -unsigned int util_string_hash32(const char *key); -uint64_t util_string_bloom64(const char *str); - -/* libudev-util-private.c */ -int util_create_path(struct udev *udev, const char *path); -int util_create_path_selinux(struct udev *udev, const char *path); -int util_delete_path(struct udev *udev, const char *path); -uid_t util_lookup_user(struct udev *udev, const char *user); -gid_t util_lookup_group(struct udev *udev, const char *group); -int util_resolve_subsys_kernel(struct udev *udev, const char *string, - char *result, size_t maxsize, int read_value); -unsigned long long ts_usec(const struct timespec *ts); -unsigned long long now_usec(void); - -/* libudev-selinux-private.c */ -#ifndef WITH_SELINUX -static inline void udev_selinux_init(struct udev *udev) {} -static inline void udev_selinux_exit(struct udev *udev) {} -static inline void udev_selinux_lsetfilecon(struct udev *udev, const char *file, unsigned int mode) {} -static inline void udev_selinux_setfscreatecon(struct udev *udev, const char *file, unsigned int mode) {} -static inline void udev_selinux_setfscreateconat(struct udev *udev, int dfd, const char *file, unsigned int mode) {} -static inline void udev_selinux_resetfscreatecon(struct udev *udev) {} -#else -void udev_selinux_init(struct udev *udev); -void udev_selinux_exit(struct udev *udev); -void udev_selinux_lsetfilecon(struct udev *udev, const char *file, unsigned int mode); -void udev_selinux_setfscreatecon(struct udev *udev, const char *file, unsigned int mode); -void udev_selinux_setfscreateconat(struct udev *udev, int dfd, const char *file, unsigned int mode); -void udev_selinux_resetfscreatecon(struct udev *udev); -#endif - -#endif diff --git a/src/udev/src/libudev-queue-private.c b/src/udev/src/libudev-queue-private.c deleted file mode 100644 index 71771950aa..0000000000 --- a/src/udev/src/libudev-queue-private.c +++ /dev/null @@ -1,412 +0,0 @@ -/* - * libudev - interface to udev device information - * - * Copyright (C) 2008 Kay Sievers - * Copyright (C) 2009 Alan Jenkins - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - */ - -/* - * DISCLAIMER - The file format mentioned here is private to udev/libudev, - * and may be changed without notice. - * - * The udev event queue is exported as a binary log file. - * Each log record consists of a sequence number followed by the device path. - * - * When a new event is queued, its details are appended to the log. - * When the event finishes, a second record is appended to the log - * with the same sequence number but a devpath len of 0. - * - * Example: - * { 0x0000000000000001 } - * { 0x0000000000000001, 0x0019, "/devices/virtual/mem/null" }, - * { 0x0000000000000002, 0x001b, "/devices/virtual/mem/random" }, - * { 0x0000000000000001, 0x0000 }, - * { 0x0000000000000003, 0x0019, "/devices/virtual/mem/zero" }, - * - * Events 2 and 3 are still queued, but event 1 has finished. - * - * The queue does not grow indefinitely. It is periodically re-created - * to remove finished events. Atomic rename() makes this transparent to readers. - * - * The queue file starts with a single sequence number which specifies the - * minimum sequence number in the log that follows. Any events prior to this - * sequence number have already finished. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libudev.h" -#include "libudev-private.h" - -static int rebuild_queue_file(struct udev_queue_export *udev_queue_export); - -struct udev_queue_export { - struct udev *udev; - int queued_count; /* number of unfinished events exported in queue file */ - FILE *queue_file; - unsigned long long int seqnum_max; /* earliest sequence number in queue file */ - unsigned long long int seqnum_min; /* latest sequence number in queue file */ - int waste_bytes; /* queue file bytes wasted on finished events */ -}; - -struct udev_queue_export *udev_queue_export_new(struct udev *udev) -{ - struct udev_queue_export *udev_queue_export; - unsigned long long int initial_seqnum; - - if (udev == NULL) - return NULL; - - udev_queue_export = calloc(1, sizeof(struct udev_queue_export)); - if (udev_queue_export == NULL) - return NULL; - udev_queue_export->udev = udev; - - initial_seqnum = udev_get_kernel_seqnum(udev); - udev_queue_export->seqnum_min = initial_seqnum; - udev_queue_export->seqnum_max = initial_seqnum; - - udev_queue_export_cleanup(udev_queue_export); - if (rebuild_queue_file(udev_queue_export) != 0) { - free(udev_queue_export); - return NULL; - } - - return udev_queue_export; -} - -struct udev_queue_export *udev_queue_export_unref(struct udev_queue_export *udev_queue_export) -{ - if (udev_queue_export == NULL) - return NULL; - if (udev_queue_export->queue_file != NULL) - fclose(udev_queue_export->queue_file); - free(udev_queue_export); - return NULL; -} - -void udev_queue_export_cleanup(struct udev_queue_export *udev_queue_export) -{ - char filename[UTIL_PATH_SIZE]; - - if (udev_queue_export == NULL) - return; - util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_queue_export->udev), "/queue.tmp", NULL); - unlink(filename); - util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_queue_export->udev), "/queue.bin", NULL); - unlink(filename); -} - -static int skip_to(FILE *file, long offset) -{ - long old_offset; - - /* fseek may drop buffered data, avoid it for small seeks */ - old_offset = ftell(file); - if (offset > old_offset && offset - old_offset <= BUFSIZ) { - size_t skip_bytes = offset - old_offset; - char buf[skip_bytes]; - - if (fread(buf, skip_bytes, 1, file) != skip_bytes) - return -1; - } - - return fseek(file, offset, SEEK_SET); -} - -struct queue_devpaths { - unsigned int devpaths_first; /* index of first queued event */ - unsigned int devpaths_size; - long devpaths[]; /* seqnum -> offset of devpath in queue file (or 0) */ -}; - -/* - * Returns a table mapping seqnum to devpath file offset for currently queued events. - * devpaths[i] represents the event with seqnum = i + udev_queue_export->seqnum_min. - */ -static struct queue_devpaths *build_index(struct udev_queue_export *udev_queue_export) -{ - struct queue_devpaths *devpaths; - unsigned long long int range; - long devpath_offset; - ssize_t devpath_len; - unsigned long long int seqnum; - unsigned long long int n; - unsigned int i; - - /* seek to the first event in the file */ - rewind(udev_queue_export->queue_file); - udev_queue_read_seqnum(udev_queue_export->queue_file, &seqnum); - - /* allocate the table */ - range = udev_queue_export->seqnum_min - udev_queue_export->seqnum_max; - if (range - 1 > INT_MAX) { - err(udev_queue_export->udev, "queue file overflow\n"); - return NULL; - } - devpaths = calloc(1, sizeof(struct queue_devpaths) + (range + 1) * sizeof(long)); - if (devpaths == NULL) - return NULL; - devpaths->devpaths_size = range + 1; - - /* read all records and populate the table */ - for (;;) { - if (udev_queue_read_seqnum(udev_queue_export->queue_file, &seqnum) < 0) - break; - n = seqnum - udev_queue_export->seqnum_max; - if (n >= devpaths->devpaths_size) - goto read_error; - - devpath_offset = ftell(udev_queue_export->queue_file); - devpath_len = udev_queue_skip_devpath(udev_queue_export->queue_file); - if (devpath_len < 0) - goto read_error; - - if (devpath_len > 0) - devpaths->devpaths[n] = devpath_offset; - else - devpaths->devpaths[n] = 0; - } - - /* find first queued event */ - for (i = 0; i < devpaths->devpaths_size; i++) { - if (devpaths->devpaths[i] != 0) - break; - } - devpaths->devpaths_first = i; - - return devpaths; - -read_error: - err(udev_queue_export->udev, "queue file corrupted\n"); - free(devpaths); - return NULL; -} - -static int rebuild_queue_file(struct udev_queue_export *udev_queue_export) -{ - unsigned long long int seqnum; - struct queue_devpaths *devpaths = NULL; - char filename[UTIL_PATH_SIZE]; - char filename_tmp[UTIL_PATH_SIZE]; - FILE *new_queue_file = NULL; - unsigned int i; - - /* read old queue file */ - if (udev_queue_export->queue_file != NULL) { - dbg(udev_queue_export->udev, "compacting queue file, freeing %d bytes\n", - udev_queue_export->waste_bytes); - - devpaths = build_index(udev_queue_export); - if (devpaths != NULL) - udev_queue_export->seqnum_max += devpaths->devpaths_first; - } - if (devpaths == NULL) { - dbg(udev_queue_export->udev, "creating empty queue file\n"); - udev_queue_export->queued_count = 0; - udev_queue_export->seqnum_max = udev_queue_export->seqnum_min; - } - - /* create new queue file */ - util_strscpyl(filename_tmp, sizeof(filename_tmp), udev_get_run_path(udev_queue_export->udev), "/queue.tmp", NULL); - new_queue_file = fopen(filename_tmp, "w+"); - if (new_queue_file == NULL) - goto error; - seqnum = udev_queue_export->seqnum_max; - fwrite(&seqnum, 1, sizeof(unsigned long long int), new_queue_file); - - /* copy unfinished events only to the new file */ - if (devpaths != NULL) { - for (i = devpaths->devpaths_first; i < devpaths->devpaths_size; i++) { - char devpath[UTIL_PATH_SIZE]; - int err; - unsigned short devpath_len; - - if (devpaths->devpaths[i] != 0) - { - skip_to(udev_queue_export->queue_file, devpaths->devpaths[i]); - err = udev_queue_read_devpath(udev_queue_export->queue_file, devpath, sizeof(devpath)); - devpath_len = err; - - fwrite(&seqnum, sizeof(unsigned long long int), 1, new_queue_file); - fwrite(&devpath_len, sizeof(unsigned short), 1, new_queue_file); - fwrite(devpath, 1, devpath_len, new_queue_file); - } - seqnum++; - } - free(devpaths); - devpaths = NULL; - } - fflush(new_queue_file); - if (ferror(new_queue_file)) - goto error; - - /* rename the new file on top of the old one */ - util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_queue_export->udev), "/queue.bin", NULL); - if (rename(filename_tmp, filename) != 0) - goto error; - - if (udev_queue_export->queue_file != NULL) - fclose(udev_queue_export->queue_file); - udev_queue_export->queue_file = new_queue_file; - udev_queue_export->waste_bytes = 0; - - return 0; - -error: - err(udev_queue_export->udev, "failed to create queue file: %m\n"); - udev_queue_export_cleanup(udev_queue_export); - - if (udev_queue_export->queue_file != NULL) { - fclose(udev_queue_export->queue_file); - udev_queue_export->queue_file = NULL; - } - if (new_queue_file != NULL) - fclose(new_queue_file); - - if (devpaths != NULL) - free(devpaths); - udev_queue_export->queued_count = 0; - udev_queue_export->waste_bytes = 0; - udev_queue_export->seqnum_max = udev_queue_export->seqnum_min; - - return -1; -} - -static int write_queue_record(struct udev_queue_export *udev_queue_export, - unsigned long long int seqnum, const char *devpath, size_t devpath_len) -{ - unsigned short len; - - if (udev_queue_export->queue_file == NULL) { - dbg(udev_queue_export->udev, "can't record event: queue file not available\n"); - return -1; - } - - if (fwrite(&seqnum, sizeof(unsigned long long int), 1, udev_queue_export->queue_file) != 1) - goto write_error; - - len = (devpath_len < USHRT_MAX) ? devpath_len : USHRT_MAX; - if (fwrite(&len, sizeof(unsigned short), 1, udev_queue_export->queue_file) != 1) - goto write_error; - if (len > 0) { - if (fwrite(devpath, 1, len, udev_queue_export->queue_file) != len) - goto write_error; - } - - /* *must* flush output; caller may fork */ - if (fflush(udev_queue_export->queue_file) != 0) - goto write_error; - - return 0; - -write_error: - /* if we failed half way through writing a record to a file, - we should not try to write any further records to it. */ - err(udev_queue_export->udev, "error writing to queue file: %m\n"); - fclose(udev_queue_export->queue_file); - udev_queue_export->queue_file = NULL; - - return -1; -} - -enum device_state { - DEVICE_QUEUED, - DEVICE_FINISHED, -}; - -static inline size_t queue_record_size(size_t devpath_len) -{ - return sizeof(unsigned long long int) + sizeof(unsigned short int) + devpath_len; -} - -static int update_queue(struct udev_queue_export *udev_queue_export, - struct udev_device *udev_device, enum device_state state) -{ - unsigned long long int seqnum = udev_device_get_seqnum(udev_device); - const char *devpath = NULL; - size_t devpath_len = 0; - int bytes; - int err; - - /* FINISHED records have a zero length devpath */ - if (state == DEVICE_QUEUED) { - devpath = udev_device_get_devpath(udev_device); - devpath_len = strlen(devpath); - } - - /* recover from an earlier failed rebuild */ - if (udev_queue_export->queue_file == NULL) { - if (rebuild_queue_file(udev_queue_export) != 0) - return -1; - } - - /* if we're removing the last event from the queue, that's the best time to rebuild it */ - if (state != DEVICE_QUEUED && udev_queue_export->queued_count == 1) { - /* we don't need to read the old queue file */ - fclose(udev_queue_export->queue_file); - udev_queue_export->queue_file = NULL; - rebuild_queue_file(udev_queue_export); - return 0; - } - - /* try to rebuild the queue files before they grow larger than one page. */ - bytes = ftell(udev_queue_export->queue_file) + queue_record_size(devpath_len); - if ((udev_queue_export->waste_bytes > bytes / 2) && bytes > 4096) - rebuild_queue_file(udev_queue_export); - - /* don't record a finished event, if we already dropped the event in a failed rebuild */ - if (seqnum < udev_queue_export->seqnum_max) - return 0; - - /* now write to the queue */ - if (state == DEVICE_QUEUED) { - udev_queue_export->queued_count++; - udev_queue_export->seqnum_min = seqnum; - } else { - udev_queue_export->waste_bytes += queue_record_size(devpath_len) + queue_record_size(0); - udev_queue_export->queued_count--; - } - err = write_queue_record(udev_queue_export, seqnum, devpath, devpath_len); - - /* try to handle ENOSPC */ - if (err != 0 && udev_queue_export->queued_count == 0) { - udev_queue_export_cleanup(udev_queue_export); - err = rebuild_queue_file(udev_queue_export); - } - - return err; -} - -static int update(struct udev_queue_export *udev_queue_export, - struct udev_device *udev_device, enum device_state state) -{ - if (update_queue(udev_queue_export, udev_device, state) != 0) - return -1; - - return 0; -} - -int udev_queue_export_device_queued(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device) -{ - return update(udev_queue_export, udev_device, DEVICE_QUEUED); -} - -int udev_queue_export_device_finished(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device) -{ - return update(udev_queue_export, udev_device, DEVICE_FINISHED); -} diff --git a/src/udev/src/libudev-queue.c b/src/udev/src/libudev-queue.c deleted file mode 100644 index 0e82cb6ae8..0000000000 --- a/src/udev/src/libudev-queue.c +++ /dev/null @@ -1,474 +0,0 @@ -/* - * libudev - interface to udev device information - * - * Copyright (C) 2008 Kay Sievers - * Copyright (C) 2009 Alan Jenkins - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libudev.h" -#include "libudev-private.h" - -/** - * SECTION:libudev-queue - * @short_description: access to currently active events - * - * The udev daemon processes events asynchronously. All events which do not have - * interdependencies run in parallel. This exports the current state of the - * event processing queue, and the current event sequence numbers from the kernel - * and the udev daemon. - */ - -/** - * udev_queue: - * - * Opaque object representing the current event queue in the udev daemon. - */ -struct udev_queue { - struct udev *udev; - int refcount; - struct udev_list queue_list; -}; - -/** - * udev_queue_new: - * @udev: udev library context - * - * The initial refcount is 1, and needs to be decremented to - * release the resources of the udev queue context. - * - * Returns: the udev queue context, or #NULL on error. - **/ -UDEV_EXPORT struct udev_queue *udev_queue_new(struct udev *udev) -{ - struct udev_queue *udev_queue; - - if (udev == NULL) - return NULL; - - udev_queue = calloc(1, sizeof(struct udev_queue)); - if (udev_queue == NULL) - return NULL; - udev_queue->refcount = 1; - udev_queue->udev = udev; - udev_list_init(udev, &udev_queue->queue_list, false); - return udev_queue; -} - -/** - * udev_queue_ref: - * @udev_queue: udev queue context - * - * Take a reference of a udev queue context. - * - * Returns: the same udev queue context. - **/ -UDEV_EXPORT struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue) -{ - if (udev_queue == NULL) - return NULL; - udev_queue->refcount++; - return udev_queue; -} - -/** - * udev_queue_unref: - * @udev_queue: udev queue context - * - * Drop a reference of a udev queue context. If the refcount reaches zero, - * the resources of the queue context will be released. - **/ -UDEV_EXPORT void udev_queue_unref(struct udev_queue *udev_queue) -{ - if (udev_queue == NULL) - return; - udev_queue->refcount--; - if (udev_queue->refcount > 0) - return; - udev_list_cleanup(&udev_queue->queue_list); - free(udev_queue); -} - -/** - * udev_queue_get_udev: - * @udev_queue: udev queue context - * - * Retrieve the udev library context the queue context was created with. - * - * Returns: the udev library context. - **/ -UDEV_EXPORT struct udev *udev_queue_get_udev(struct udev_queue *udev_queue) -{ - if (udev_queue == NULL) - return NULL; - return udev_queue->udev; -} - -unsigned long long int udev_get_kernel_seqnum(struct udev *udev) -{ - char filename[UTIL_PATH_SIZE]; - unsigned long long int seqnum; - int fd; - char buf[32]; - ssize_t len; - - util_strscpyl(filename, sizeof(filename), udev_get_sys_path(udev), "/kernel/uevent_seqnum", NULL); - fd = open(filename, O_RDONLY|O_CLOEXEC); - if (fd < 0) - return 0; - len = read(fd, buf, sizeof(buf)); - close(fd); - if (len <= 2) - return 0; - buf[len-1] = '\0'; - seqnum = strtoull(buf, NULL, 10); - return seqnum; -} - -/** - * udev_queue_get_kernel_seqnum: - * @udev_queue: udev queue context - * - * Returns: the current kernel event sequence number. - **/ -UDEV_EXPORT unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue) -{ - unsigned long long int seqnum; - - if (udev_queue == NULL) - return -EINVAL; - - seqnum = udev_get_kernel_seqnum(udev_queue->udev); - dbg(udev_queue->udev, "seqnum=%llu\n", seqnum); - return seqnum; -} - -int udev_queue_read_seqnum(FILE *queue_file, unsigned long long int *seqnum) -{ - if (fread(seqnum, sizeof(unsigned long long int), 1, queue_file) != 1) - return -1; - - return 0; -} - -ssize_t udev_queue_skip_devpath(FILE *queue_file) -{ - unsigned short int len; - - if (fread(&len, sizeof(unsigned short int), 1, queue_file) == 1) { - char devpath[len]; - - /* use fread to skip, fseek might drop buffered data */ - if (fread(devpath, 1, len, queue_file) == len) - return len; - } - - return -1; -} - -ssize_t udev_queue_read_devpath(FILE *queue_file, char *devpath, size_t size) -{ - unsigned short int read_bytes = 0; - unsigned short int len; - - if (fread(&len, sizeof(unsigned short int), 1, queue_file) != 1) - return -1; - - read_bytes = (len < size - 1) ? len : size - 1; - if (fread(devpath, 1, read_bytes, queue_file) != read_bytes) - return -1; - devpath[read_bytes] = '\0'; - - /* if devpath was too long, skip unread characters */ - if (read_bytes != len) { - unsigned short int skip_bytes = len - read_bytes; - char buf[skip_bytes]; - - if (fread(buf, 1, skip_bytes, queue_file) != skip_bytes) - return -1; - } - - return read_bytes; -} - -static FILE *open_queue_file(struct udev_queue *udev_queue, unsigned long long int *seqnum_start) -{ - char filename[UTIL_PATH_SIZE]; - FILE *queue_file; - - util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_queue->udev), "/queue.bin", NULL); - queue_file = fopen(filename, "re"); - if (queue_file == NULL) - return NULL; - - if (udev_queue_read_seqnum(queue_file, seqnum_start) < 0) { - err(udev_queue->udev, "corrupt queue file\n"); - fclose(queue_file); - return NULL; - } - - return queue_file; -} - -/** - * udev_queue_get_udev_seqnum: - * @udev_queue: udev queue context - * - * Returns: the last known udev event sequence number. - **/ -UDEV_EXPORT unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue) -{ - unsigned long long int seqnum_udev; - FILE *queue_file; - - queue_file = open_queue_file(udev_queue, &seqnum_udev); - if (queue_file == NULL) - return 0; - - for (;;) { - unsigned long long int seqnum; - ssize_t devpath_len; - - if (udev_queue_read_seqnum(queue_file, &seqnum) < 0) - break; - devpath_len = udev_queue_skip_devpath(queue_file); - if (devpath_len < 0) - break; - if (devpath_len > 0) - seqnum_udev = seqnum; - } - - fclose(queue_file); - return seqnum_udev; -} - -/** - * udev_queue_get_udev_is_active: - * @udev_queue: udev queue context - * - * Returns: a flag indicating if udev is active. - **/ -UDEV_EXPORT int udev_queue_get_udev_is_active(struct udev_queue *udev_queue) -{ - unsigned long long int seqnum_start; - FILE *queue_file; - - queue_file = open_queue_file(udev_queue, &seqnum_start); - if (queue_file == NULL) - return 0; - - fclose(queue_file); - return 1; -} - -/** - * udev_queue_get_queue_is_empty: - * @udev_queue: udev queue context - * - * Returns: a flag indicating if udev is currently handling events. - **/ -UDEV_EXPORT int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue) -{ - unsigned long long int seqnum_kernel; - unsigned long long int seqnum_udev = 0; - int queued = 0; - int is_empty = 0; - FILE *queue_file; - - if (udev_queue == NULL) - return -EINVAL; - queue_file = open_queue_file(udev_queue, &seqnum_udev); - if (queue_file == NULL) - return 1; - - for (;;) { - unsigned long long int seqnum; - ssize_t devpath_len; - - if (udev_queue_read_seqnum(queue_file, &seqnum) < 0) - break; - devpath_len = udev_queue_skip_devpath(queue_file); - if (devpath_len < 0) - break; - - if (devpath_len > 0) { - queued++; - seqnum_udev = seqnum; - } else { - queued--; - } - } - - if (queued > 0) { - dbg(udev_queue->udev, "queue is not empty\n"); - goto out; - } - - seqnum_kernel = udev_queue_get_kernel_seqnum(udev_queue); - if (seqnum_udev < seqnum_kernel) { - dbg(udev_queue->udev, "queue is empty but kernel events still pending [%llu]<->[%llu]\n", - seqnum_kernel, seqnum_udev); - goto out; - } - - dbg(udev_queue->udev, "queue is empty\n"); - is_empty = 1; - -out: - fclose(queue_file); - return is_empty; -} - -/** - * udev_queue_get_seqnum_sequence_is_finished: - * @udev_queue: udev queue context - * @start: first event sequence number - * @end: last event sequence number - * - * Returns: a flag indicating if any of the sequence numbers in the given range is currently active. - **/ -UDEV_EXPORT int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue, - unsigned long long int start, unsigned long long int end) -{ - unsigned long long int seqnum; - ssize_t devpath_len; - int unfinished; - FILE *queue_file; - - if (udev_queue == NULL) - return -EINVAL; - queue_file = open_queue_file(udev_queue, &seqnum); - if (queue_file == NULL) - return 1; - if (start < seqnum) - start = seqnum; - if (start > end) { - fclose(queue_file); - return 1; - } - if (end - start > INT_MAX - 1) { - fclose(queue_file); - return -EOVERFLOW; - } - - /* - * we might start with 0, and handle the initial seqnum - * only when we find an entry in the queue file - **/ - unfinished = end - start; - - do { - if (udev_queue_read_seqnum(queue_file, &seqnum) < 0) - break; - devpath_len = udev_queue_skip_devpath(queue_file); - if (devpath_len < 0) - break; - - /* - * we might start with an empty or re-build queue file, where - * the initial seqnum is not recorded as finished - */ - if (start == seqnum && devpath_len > 0) - unfinished++; - - if (devpath_len == 0) { - if (seqnum >= start && seqnum <= end) - unfinished--; - } - } while (unfinished > 0); - - fclose(queue_file); - - return (unfinished == 0); -} - -/** - * udev_queue_get_seqnum_is_finished: - * @udev_queue: udev queue context - * @seqnum: sequence number - * - * Returns: a flag indicating if the given sequence number is currently active. - **/ -UDEV_EXPORT int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum) -{ - if (!udev_queue_get_seqnum_sequence_is_finished(udev_queue, seqnum, seqnum)) - return 0; - - dbg(udev_queue->udev, "seqnum: %llu finished\n", seqnum); - return 1; -} - -/** - * udev_queue_get_queued_list_entry: - * @udev_queue: udev queue context - * - * Returns: the first entry of the list of queued events. - **/ -UDEV_EXPORT struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue) -{ - unsigned long long int seqnum; - FILE *queue_file; - - if (udev_queue == NULL) - return NULL; - udev_list_cleanup(&udev_queue->queue_list); - - queue_file = open_queue_file(udev_queue, &seqnum); - if (queue_file == NULL) - return NULL; - - for (;;) { - char syspath[UTIL_PATH_SIZE]; - char *s; - size_t l; - ssize_t len; - char seqnum_str[32]; - struct udev_list_entry *list_entry; - - if (udev_queue_read_seqnum(queue_file, &seqnum) < 0) - break; - snprintf(seqnum_str, sizeof(seqnum_str), "%llu", seqnum); - - s = syspath; - l = util_strpcpyl(&s, sizeof(syspath), udev_get_sys_path(udev_queue->udev), NULL); - len = udev_queue_read_devpath(queue_file, s, l); - if (len < 0) - break; - - if (len > 0) { - udev_list_entry_add(&udev_queue->queue_list, syspath, seqnum_str); - } else { - udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_queue->queue_list)) { - if (strcmp(seqnum_str, udev_list_entry_get_value(list_entry)) == 0) { - udev_list_entry_delete(list_entry); - break; - } - } - } - } - fclose(queue_file); - - return udev_list_get_entry(&udev_queue->queue_list); -} - -struct udev_list_entry *udev_queue_get_failed_list_entry(struct udev_queue *udev_queue); -UDEV_EXPORT struct udev_list_entry *udev_queue_get_failed_list_entry(struct udev_queue *udev_queue) -{ - errno = ENOSYS; - return NULL; -} diff --git a/src/udev/src/libudev-selinux-private.c b/src/udev/src/libudev-selinux-private.c deleted file mode 100644 index 0f2a617b18..0000000000 --- a/src/udev/src/libudev-selinux-private.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * libudev - interface to udev device information - * - * Copyright (C) 2008 Kay Sievers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include - -#include "libudev.h" -#include "libudev-private.h" - -static int selinux_enabled; -security_context_t selinux_prev_scontext; - -void udev_selinux_init(struct udev *udev) -{ - /* record the present security context */ - selinux_enabled = (is_selinux_enabled() > 0); - info(udev, "selinux=%i\n", selinux_enabled); - if (!selinux_enabled) - return; - matchpathcon_init_prefix(NULL, udev_get_dev_path(udev)); - if (getfscreatecon(&selinux_prev_scontext) < 0) { - err(udev, "getfscreatecon failed\n"); - selinux_prev_scontext = NULL; - } -} - -void udev_selinux_exit(struct udev *udev) -{ - if (!selinux_enabled) - return; - freecon(selinux_prev_scontext); - selinux_prev_scontext = NULL; -} - -void udev_selinux_lsetfilecon(struct udev *udev, const char *file, unsigned int mode) -{ - security_context_t scontext = NULL; - - if (!selinux_enabled) - return; - if (matchpathcon(file, mode, &scontext) < 0) { - err(udev, "matchpathcon(%s) failed\n", file); - return; - } - if (lsetfilecon(file, scontext) < 0) - err(udev, "setfilecon %s failed: %m\n", file); - freecon(scontext); -} - -void udev_selinux_setfscreatecon(struct udev *udev, const char *file, unsigned int mode) -{ - security_context_t scontext = NULL; - - if (!selinux_enabled) - return; - - if (matchpathcon(file, mode, &scontext) < 0) { - err(udev, "matchpathcon(%s) failed\n", file); - return; - } - if (setfscreatecon(scontext) < 0) - err(udev, "setfscreatecon %s failed: %m\n", file); - freecon(scontext); -} - -void udev_selinux_resetfscreatecon(struct udev *udev) -{ - if (!selinux_enabled) - return; - if (setfscreatecon(selinux_prev_scontext) < 0) - err(udev, "setfscreatecon failed: %m\n"); -} - -void udev_selinux_setfscreateconat(struct udev *udev, int dfd, const char *file, unsigned int mode) -{ - char filename[UTIL_PATH_SIZE]; - - if (!selinux_enabled) - return; - - /* resolve relative filename */ - if (file[0] != '/') { - char procfd[UTIL_PATH_SIZE]; - char target[UTIL_PATH_SIZE]; - ssize_t len; - - snprintf(procfd, sizeof(procfd), "/proc/%u/fd/%u", getpid(), dfd); - len = readlink(procfd, target, sizeof(target)); - if (len <= 0 || len == sizeof(target)) - return; - target[len] = '\0'; - - util_strscpyl(filename, sizeof(filename), target, "/", file, NULL); - file = filename; - } - udev_selinux_setfscreatecon(udev, file, mode); -} diff --git a/src/udev/src/libudev-util-private.c b/src/udev/src/libudev-util-private.c deleted file mode 100644 index 08f0ba2228..0000000000 --- a/src/udev/src/libudev-util-private.c +++ /dev/null @@ -1,242 +0,0 @@ -/* - * libudev - interface to udev device information - * - * Copyright (C) 2003-2009 Kay Sievers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libudev.h" -#include "libudev-private.h" - -static int create_path(struct udev *udev, const char *path, bool selinux) -{ - char p[UTIL_PATH_SIZE]; - char *pos; - struct stat stats; - int err; - - util_strscpy(p, sizeof(p), path); - pos = strrchr(p, '/'); - if (pos == NULL) - return 0; - while (pos != p && pos[-1] == '/') - pos--; - if (pos == p) - return 0; - pos[0] = '\0'; - - dbg(udev, "stat '%s'\n", p); - if (stat(p, &stats) == 0) { - if ((stats.st_mode & S_IFMT) == S_IFDIR) - return 0; - else - return -ENOTDIR; - } - - err = util_create_path(udev, p); - if (err != 0) - return err; - - dbg(udev, "mkdir '%s'\n", p); - if (selinux) - udev_selinux_setfscreatecon(udev, p, S_IFDIR|0755); - err = mkdir(p, 0755); - if (err != 0) { - err = -errno; - if (err == -EEXIST && stat(p, &stats) == 0) { - if ((stats.st_mode & S_IFMT) == S_IFDIR) - err = 0; - else - err = -ENOTDIR; - } - } - if (selinux) - udev_selinux_resetfscreatecon(udev); - return err; -} - -int util_create_path(struct udev *udev, const char *path) -{ - return create_path(udev, path, false); -} - -int util_create_path_selinux(struct udev *udev, const char *path) -{ - return create_path(udev, path, true); -} - -int util_delete_path(struct udev *udev, const char *path) -{ - char p[UTIL_PATH_SIZE]; - char *pos; - int err = 0; - - if (path[0] == '/') - while(path[1] == '/') - path++; - util_strscpy(p, sizeof(p), path); - pos = strrchr(p, '/'); - if (pos == p || pos == NULL) - return 0; - - for (;;) { - *pos = '\0'; - pos = strrchr(p, '/'); - - /* don't remove the last one */ - if ((pos == p) || (pos == NULL)) - break; - - err = rmdir(p); - if (err < 0) { - if (errno == ENOENT) - err = 0; - break; - } - } - return err; -} - -uid_t util_lookup_user(struct udev *udev, const char *user) -{ - char *endptr; - size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX); - char buf[buflen]; - struct passwd pwbuf; - struct passwd *pw; - uid_t uid; - - if (strcmp(user, "root") == 0) - return 0; - uid = strtoul(user, &endptr, 10); - if (endptr[0] == '\0') - return uid; - - errno = getpwnam_r(user, &pwbuf, buf, buflen, &pw); - if (pw != NULL) - return pw->pw_uid; - if (errno == 0 || errno == ENOENT || errno == ESRCH) - err(udev, "specified user '%s' unknown\n", user); - else - err(udev, "error resolving user '%s': %m\n", user); - return 0; -} - -gid_t util_lookup_group(struct udev *udev, const char *group) -{ - char *endptr; - size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX); - char *buf; - struct group grbuf; - struct group *gr; - gid_t gid = 0; - - if (strcmp(group, "root") == 0) - return 0; - gid = strtoul(group, &endptr, 10); - if (endptr[0] == '\0') - return gid; - buf = NULL; - gid = 0; - for (;;) { - char *newbuf; - - newbuf = realloc(buf, buflen); - if (!newbuf) - break; - buf = newbuf; - errno = getgrnam_r(group, &grbuf, buf, buflen, &gr); - if (gr != NULL) { - gid = gr->gr_gid; - } else if (errno == ERANGE) { - buflen *= 2; - continue; - } else if (errno == 0 || errno == ENOENT || errno == ESRCH) { - err(udev, "specified group '%s' unknown\n", group); - } else { - err(udev, "error resolving group '%s': %m\n", group); - } - break; - } - free(buf); - return gid; -} - -/* handle "[/]" format */ -int util_resolve_subsys_kernel(struct udev *udev, const char *string, - char *result, size_t maxsize, int read_value) -{ - char temp[UTIL_PATH_SIZE]; - char *subsys; - char *sysname; - struct udev_device *dev; - char *attr; - - if (string[0] != '[') - return -1; - - util_strscpy(temp, sizeof(temp), string); - - subsys = &temp[1]; - - sysname = strchr(subsys, '/'); - if (sysname == NULL) - return -1; - sysname[0] = '\0'; - sysname = &sysname[1]; - - attr = strchr(sysname, ']'); - if (attr == NULL) - return -1; - attr[0] = '\0'; - attr = &attr[1]; - if (attr[0] == '/') - attr = &attr[1]; - if (attr[0] == '\0') - attr = NULL; - - if (read_value && attr == NULL) - return -1; - - dev = udev_device_new_from_subsystem_sysname(udev, subsys, sysname); - if (dev == NULL) - return -1; - - if (read_value) { - const char *val; - - val = udev_device_get_sysattr_value(dev, attr); - if (val != NULL) - util_strscpy(result, maxsize, val); - else - result[0] = '\0'; - info(udev, "value '[%s/%s]%s' is '%s'\n", subsys, sysname, attr, result); - } else { - size_t l; - char *s; - - s = result; - l = util_strpcpyl(&s, maxsize, udev_device_get_syspath(dev), NULL); - if (attr != NULL) - util_strpcpyl(&s, l, "/", attr, NULL); - info(udev, "path '[%s/%s]%s' is '%s'\n", subsys, sysname, attr, result); - } - udev_device_unref(dev); - return 0; -} diff --git a/src/udev/src/libudev-util.c b/src/udev/src/libudev-util.c deleted file mode 100644 index 7e345f0fb6..0000000000 --- a/src/udev/src/libudev-util.c +++ /dev/null @@ -1,570 +0,0 @@ -/* - * libudev - interface to udev device information - * - * Copyright (C) 2008-2011 Kay Sievers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libudev.h" -#include "libudev-private.h" - -/** - * SECTION:libudev-util - * @short_description: utils - */ - -ssize_t util_get_sys_core_link_value(struct udev *udev, const char *slink, const char *syspath, char *value, size_t size) -{ - char path[UTIL_PATH_SIZE]; - char target[UTIL_PATH_SIZE]; - ssize_t len; - const char *pos; - - util_strscpyl(path, sizeof(path), syspath, "/", slink, NULL); - len = readlink(path, target, sizeof(target)); - if (len <= 0 || len == (ssize_t)sizeof(target)) - return -1; - target[len] = '\0'; - pos = strrchr(target, '/'); - if (pos == NULL) - return -1; - pos = &pos[1]; - dbg(udev, "resolved link to: '%s'\n", pos); - return util_strscpy(value, size, pos); -} - -int util_resolve_sys_link(struct udev *udev, char *syspath, size_t size) -{ - char link_target[UTIL_PATH_SIZE]; - - ssize_t len; - int i; - int back; - char *base = NULL; - - len = readlink(syspath, link_target, sizeof(link_target)); - if (len <= 0 || len == (ssize_t)sizeof(link_target)) - return -1; - link_target[len] = '\0'; - dbg(udev, "path link '%s' points to '%s'\n", syspath, link_target); - - for (back = 0; strncmp(&link_target[back * 3], "../", 3) == 0; back++) - ; - dbg(udev, "base '%s', tail '%s', back %i\n", syspath, &link_target[back * 3], back); - for (i = 0; i <= back; i++) { - base = strrchr(syspath, '/'); - if (base == NULL) - return -EINVAL; - base[0] = '\0'; - } - if (base == NULL) - return -EINVAL; - dbg(udev, "after moving back '%s'\n", syspath); - util_strscpyl(base, size - (base - syspath), "/", &link_target[back * 3], NULL); - return 0; -} - -int util_log_priority(const char *priority) -{ - char *endptr; - int prio; - - prio = strtol(priority, &endptr, 10); - if (endptr[0] == '\0' || isspace(endptr[0])) - return prio; - if (strncmp(priority, "err", 3) == 0) - return LOG_ERR; - if (strncmp(priority, "info", 4) == 0) - return LOG_INFO; - if (strncmp(priority, "debug", 5) == 0) - return LOG_DEBUG; - return 0; -} - -size_t util_path_encode(const char *src, char *dest, size_t size) -{ - size_t i, j; - - for (i = 0, j = 0; src[i] != '\0'; i++) { - if (src[i] == '/') { - if (j+4 >= size) { - j = 0; - break; - } - memcpy(&dest[j], "\\x2f", 4); - j += 4; - } else if (src[i] == '\\') { - if (j+4 >= size) { - j = 0; - break; - } - memcpy(&dest[j], "\\x5c", 4); - j += 4; - } else { - if (j+1 >= size) { - j = 0; - break; - } - dest[j] = src[i]; - j++; - } - } - dest[j] = '\0'; - return j; -} - -size_t util_path_decode(char *s) -{ - size_t i, j; - - for (i = 0, j = 0; s[i] != '\0'; j++) { - if (memcmp(&s[i], "\\x2f", 4) == 0) { - s[j] = '/'; - i += 4; - } else if (memcmp(&s[i], "\\x5c", 4) == 0) { - s[j] = '\\'; - i += 4; - } else { - s[j] = s[i]; - i++; - } - } - s[j] = '\0'; - return j; -} - -void util_remove_trailing_chars(char *path, char c) -{ - size_t len; - - if (path == NULL) - return; - len = strlen(path); - while (len > 0 && path[len-1] == c) - path[--len] = '\0'; -} - -/* - * Concatenates strings. In any case, terminates in _all_ cases with '\0' - * and moves the @dest pointer forward to the added '\0'. Returns the - * remaining size, and 0 if the string was truncated. - */ -size_t util_strpcpy(char **dest, size_t size, const char *src) -{ - size_t len; - - len = strlen(src); - if (len >= size) { - if (size > 1) - *dest = mempcpy(*dest, src, size-1); - size = 0; - *dest[0] = '\0'; - } else { - if (len > 0) { - *dest = mempcpy(*dest, src, len); - size -= len; - } - *dest[0] = '\0'; - } - return size; -} - -/* concatenates list of strings, moves dest forward */ -size_t util_strpcpyl(char **dest, size_t size, const char *src, ...) -{ - va_list va; - - va_start(va, src); - do { - size = util_strpcpy(dest, size, src); - src = va_arg(va, char *); - } while (src != NULL); - va_end(va); - - return size; -} - -/* copies string */ -size_t util_strscpy(char *dest, size_t size, const char *src) -{ - char *s; - - s = dest; - return util_strpcpy(&s, size, src); -} - -/* concatenates list of strings */ -size_t util_strscpyl(char *dest, size_t size, const char *src, ...) -{ - va_list va; - char *s; - - va_start(va, src); - s = dest; - do { - size = util_strpcpy(&s, size, src); - src = va_arg(va, char *); - } while (src != NULL); - va_end(va); - - return size; -} - -/* count of characters used to encode one unicode char */ -static int utf8_encoded_expected_len(const char *str) -{ - unsigned char c = (unsigned char)str[0]; - - if (c < 0x80) - return 1; - if ((c & 0xe0) == 0xc0) - return 2; - if ((c & 0xf0) == 0xe0) - return 3; - if ((c & 0xf8) == 0xf0) - return 4; - if ((c & 0xfc) == 0xf8) - return 5; - if ((c & 0xfe) == 0xfc) - return 6; - return 0; -} - -/* decode one unicode char */ -static int utf8_encoded_to_unichar(const char *str) -{ - int unichar; - int len; - int i; - - len = utf8_encoded_expected_len(str); - switch (len) { - case 1: - return (int)str[0]; - case 2: - unichar = str[0] & 0x1f; - break; - case 3: - unichar = (int)str[0] & 0x0f; - break; - case 4: - unichar = (int)str[0] & 0x07; - break; - case 5: - unichar = (int)str[0] & 0x03; - break; - case 6: - unichar = (int)str[0] & 0x01; - break; - default: - return -1; - } - - for (i = 1; i < len; i++) { - if (((int)str[i] & 0xc0) != 0x80) - return -1; - unichar <<= 6; - unichar |= (int)str[i] & 0x3f; - } - - return unichar; -} - -/* expected size used to encode one unicode char */ -static int utf8_unichar_to_encoded_len(int unichar) -{ - if (unichar < 0x80) - return 1; - if (unichar < 0x800) - return 2; - if (unichar < 0x10000) - return 3; - if (unichar < 0x200000) - return 4; - if (unichar < 0x4000000) - return 5; - return 6; -} - -/* check if unicode char has a valid numeric range */ -static int utf8_unichar_valid_range(int unichar) -{ - if (unichar > 0x10ffff) - return 0; - if ((unichar & 0xfffff800) == 0xd800) - return 0; - if ((unichar > 0xfdcf) && (unichar < 0xfdf0)) - return 0; - if ((unichar & 0xffff) == 0xffff) - return 0; - return 1; -} - -/* validate one encoded unicode char and return its length */ -static int utf8_encoded_valid_unichar(const char *str) -{ - int len; - int unichar; - int i; - - len = utf8_encoded_expected_len(str); - if (len == 0) - return -1; - - /* ascii is valid */ - if (len == 1) - return 1; - - /* check if expected encoded chars are available */ - for (i = 0; i < len; i++) - if ((str[i] & 0x80) != 0x80) - return -1; - - unichar = utf8_encoded_to_unichar(str); - - /* check if encoded length matches encoded value */ - if (utf8_unichar_to_encoded_len(unichar) != len) - return -1; - - /* check if value has valid range */ - if (!utf8_unichar_valid_range(unichar)) - return -1; - - return len; -} - -int util_replace_whitespace(const char *str, char *to, size_t len) -{ - size_t i, j; - - /* strip trailing whitespace */ - len = strnlen(str, len); - while (len && isspace(str[len-1])) - len--; - - /* strip leading whitespace */ - i = 0; - while (isspace(str[i]) && (i < len)) - i++; - - j = 0; - while (i < len) { - /* substitute multiple whitespace with a single '_' */ - if (isspace(str[i])) { - while (isspace(str[i])) - i++; - to[j++] = '_'; - } - to[j++] = str[i++]; - } - to[j] = '\0'; - return 0; -} - -static int is_whitelisted(char c, const char *white) -{ - if ((c >= '0' && c <= '9') || - (c >= 'A' && c <= 'Z') || - (c >= 'a' && c <= 'z') || - strchr("#+-.:=@_", c) != NULL || - (white != NULL && strchr(white, c) != NULL)) - return 1; - return 0; -} - -/* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */ -int util_replace_chars(char *str, const char *white) -{ - size_t i = 0; - int replaced = 0; - - while (str[i] != '\0') { - int len; - - if (is_whitelisted(str[i], white)) { - i++; - continue; - } - - /* accept hex encoding */ - if (str[i] == '\\' && str[i+1] == 'x') { - i += 2; - continue; - } - - /* accept valid utf8 */ - len = utf8_encoded_valid_unichar(&str[i]); - if (len > 1) { - i += len; - continue; - } - - /* if space is allowed, replace whitespace with ordinary space */ - if (isspace(str[i]) && white != NULL && strchr(white, ' ') != NULL) { - str[i] = ' '; - i++; - replaced++; - continue; - } - - /* everything else is replaced with '_' */ - str[i] = '_'; - i++; - replaced++; - } - return replaced; -} - -/** - * udev_util_encode_string: - * @str: input string to be encoded - * @str_enc: output string to store the encoded input string - * @len: maximum size of the output string, which may be - * four times as long as the input string - * - * Encode all potentially unsafe characters of a string to the - * corresponding 2 char hex value prefixed by '\x'. - * - * Returns: 0 if the entire string was copied, non-zero otherwise. - **/ -UDEV_EXPORT int udev_util_encode_string(const char *str, char *str_enc, size_t len) -{ - size_t i, j; - - if (str == NULL || str_enc == NULL) - return -1; - - for (i = 0, j = 0; str[i] != '\0'; i++) { - int seqlen; - - seqlen = utf8_encoded_valid_unichar(&str[i]); - if (seqlen > 1) { - if (len-j < (size_t)seqlen) - goto err; - memcpy(&str_enc[j], &str[i], seqlen); - j += seqlen; - i += (seqlen-1); - } else if (str[i] == '\\' || !is_whitelisted(str[i], NULL)) { - if (len-j < 4) - goto err; - sprintf(&str_enc[j], "\\x%02x", (unsigned char) str[i]); - j += 4; - } else { - if (len-j < 1) - goto err; - str_enc[j] = str[i]; - j++; - } - } - if (len-j < 1) - goto err; - str_enc[j] = '\0'; - return 0; -err: - return -1; -} - -/* - * http://sites.google.com/site/murmurhash/ - * - * All code is released to the public domain. For business purposes, - * Murmurhash is under the MIT license. - * - */ -static unsigned int murmur_hash2(const char *key, int len, unsigned int seed) -{ - /* - * 'm' and 'r' are mixing constants generated offline. - * They're not really 'magic', they just happen to work well. - */ - const unsigned int m = 0x5bd1e995; - const int r = 24; - - /* initialize the hash to a 'random' value */ - unsigned int h = seed ^ len; - - /* mix 4 bytes at a time into the hash */ - const unsigned char * data = (const unsigned char *)key; - - while(len >= 4) { - unsigned int k = *(unsigned int *)data; - - k *= m; - k ^= k >> r; - k *= m; - h *= m; - h ^= k; - - data += 4; - len -= 4; - } - - /* handle the last few bytes of the input array */ - switch(len) { - case 3: - h ^= data[2] << 16; - case 2: - h ^= data[1] << 8; - case 1: - h ^= data[0]; - h *= m; - }; - - /* do a few final mixes of the hash to ensure the last few bytes are well-incorporated */ - h ^= h >> 13; - h *= m; - h ^= h >> 15; - - return h; -} - -unsigned int util_string_hash32(const char *str) -{ - return murmur_hash2(str, strlen(str), 0); -} - -/* get a bunch of bit numbers out of the hash, and set the bits in our bit field */ -uint64_t util_string_bloom64(const char *str) -{ - uint64_t bits = 0; - unsigned int hash = util_string_hash32(str); - - bits |= 1LLU << (hash & 63); - bits |= 1LLU << ((hash >> 6) & 63); - bits |= 1LLU << ((hash >> 12) & 63); - bits |= 1LLU << ((hash >> 18) & 63); - return bits; -} - -#define USEC_PER_SEC 1000000ULL -#define NSEC_PER_USEC 1000ULL -unsigned long long ts_usec(const struct timespec *ts) -{ - return (unsigned long long) ts->tv_sec * USEC_PER_SEC + - (unsigned long long) ts->tv_nsec / NSEC_PER_USEC; -} - -unsigned long long now_usec(void) -{ - struct timespec ts; - - if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) - return 0; - return ts_usec(&ts); -} diff --git a/src/udev/src/libudev.c b/src/udev/src/libudev.c deleted file mode 100644 index d954daef68..0000000000 --- a/src/udev/src/libudev.c +++ /dev/null @@ -1,457 +0,0 @@ -/* - * libudev - interface to udev device information - * - * Copyright (C) 2008-2010 Kay Sievers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libudev.h" -#include "libudev-private.h" - -/** - * SECTION:libudev - * @short_description: libudev context - * - * The context contains the default values read from the udev config file, - * and is passed to all library operations. - */ - -/** - * udev: - * - * Opaque object representing the library context. - */ -struct udev { - int refcount; - void (*log_fn)(struct udev *udev, - int priority, const char *file, int line, const char *fn, - const char *format, va_list args); - void *userdata; - char *sys_path; - char *dev_path; - char *rules_path[4]; - unsigned long long rules_path_ts[4]; - int rules_path_count; - char *run_path; - struct udev_list properties_list; - int log_priority; -}; - -void udev_log(struct udev *udev, - int priority, const char *file, int line, const char *fn, - const char *format, ...) -{ - va_list args; - - va_start(args, format); - udev->log_fn(udev, priority, file, line, fn, format, args); - va_end(args); -} - -static void log_stderr(struct udev *udev, - int priority, const char *file, int line, const char *fn, - const char *format, va_list args) -{ - fprintf(stderr, "libudev: %s: ", fn); - vfprintf(stderr, format, args); -} - -/** - * udev_get_userdata: - * @udev: udev library context - * - * Retrieve stored data pointer from library context. This might be useful - * to access from callbacks like a custom logging function. - * - * Returns: stored userdata - **/ -UDEV_EXPORT void *udev_get_userdata(struct udev *udev) -{ - if (udev == NULL) - return NULL; - return udev->userdata; -} - -/** - * udev_set_userdata: - * @udev: udev library context - * @userdata: data pointer - * - * Store custom @userdata in the library context. - **/ -UDEV_EXPORT void udev_set_userdata(struct udev *udev, void *userdata) -{ - if (udev == NULL) - return; - udev->userdata = userdata; -} - -static char *set_value(char **s, const char *v) -{ - free(*s); - *s = strdup(v); - util_remove_trailing_chars(*s, '/'); - return *s; -} - -/** - * udev_new: - * - * Create udev library context. This reads the udev configuration - * file, and fills in the default values. - * - * The initial refcount is 1, and needs to be decremented to - * release the resources of the udev library context. - * - * Returns: a new udev library context - **/ -UDEV_EXPORT struct udev *udev_new(void) -{ - struct udev *udev; - const char *env; - char *config_file = NULL; - FILE *f; - - udev = calloc(1, sizeof(struct udev)); - if (udev == NULL) - return NULL; - udev->refcount = 1; - udev->log_fn = log_stderr; - udev->log_priority = LOG_ERR; - udev_list_init(udev, &udev->properties_list, true); - - /* custom config file */ - env = getenv("UDEV_CONFIG_FILE"); - if (env != NULL) { - if (set_value(&config_file, env) == NULL) - goto err; - udev_add_property(udev, "UDEV_CONFIG_FILE", config_file); - } - - /* default config file */ - if (config_file == NULL) - config_file = strdup(SYSCONFDIR "/udev/udev.conf"); - if (config_file == NULL) - goto err; - - f = fopen(config_file, "re"); - if (f != NULL) { - char line[UTIL_LINE_SIZE]; - int line_nr = 0; - - while (fgets(line, sizeof(line), f)) { - size_t len; - char *key; - char *val; - - line_nr++; - - /* find key */ - key = line; - while (isspace(key[0])) - key++; - - /* comment or empty line */ - if (key[0] == '#' || key[0] == '\0') - continue; - - /* split key/value */ - val = strchr(key, '='); - if (val == NULL) { - err(udev, "missing = in '%s'[%i], skip line\n", config_file, line_nr); - continue; - } - val[0] = '\0'; - val++; - - /* find value */ - while (isspace(val[0])) - val++; - - /* terminate key */ - len = strlen(key); - if (len == 0) - continue; - while (isspace(key[len-1])) - len--; - key[len] = '\0'; - - /* terminate value */ - len = strlen(val); - if (len == 0) - continue; - while (isspace(val[len-1])) - len--; - val[len] = '\0'; - - if (len == 0) - continue; - - /* unquote */ - if (val[0] == '"' || val[0] == '\'') { - if (val[len-1] != val[0]) { - err(udev, "inconsistent quoting in '%s'[%i], skip line\n", config_file, line_nr); - continue; - } - val[len-1] = '\0'; - val++; - } - - if (strcmp(key, "udev_log") == 0) { - udev_set_log_priority(udev, util_log_priority(val)); - continue; - } - if (strcmp(key, "udev_root") == 0) { - set_value(&udev->dev_path, val); - continue; - } - if (strcmp(key, "udev_run") == 0) { - set_value(&udev->run_path, val); - continue; - } - if (strcmp(key, "udev_sys") == 0) { - set_value(&udev->sys_path, val); - continue; - } - if (strcmp(key, "udev_rules") == 0) { - set_value(&udev->rules_path[0], val); - udev->rules_path_count = 1; - continue; - } - } - fclose(f); - } - - /* environment overrides config */ - env = getenv("UDEV_LOG"); - if (env != NULL) - udev_set_log_priority(udev, util_log_priority(env)); - - /* set defaults */ - if (udev->dev_path == NULL) - if (set_value(&udev->dev_path, "/dev") == NULL) - goto err; - - if (udev->sys_path == NULL) - if (set_value(&udev->sys_path, "/sys") == NULL) - goto err; - - if (udev->run_path == NULL) - if (set_value(&udev->run_path, "/run/udev") == NULL) - goto err; - - if (udev->rules_path[0] == NULL) { - /* /usr/lib/udev -- system rules */ - udev->rules_path[0] = strdup(PKGLIBEXECDIR "/rules.d"); - if (!udev->rules_path[0]) - goto err; - - /* /run/udev -- runtime rules */ - if (asprintf(&udev->rules_path[2], "%s/rules.d", udev->run_path) < 0) - goto err; - - /* /etc/udev -- local administration rules */ - udev->rules_path[1] = strdup(SYSCONFDIR "/udev/rules.d"); - if (!udev->rules_path[1]) - goto err; - - udev->rules_path_count = 3; - } - - dbg(udev, "context %p created\n", udev); - dbg(udev, "log_priority=%d\n", udev->log_priority); - dbg(udev, "config_file='%s'\n", config_file); - dbg(udev, "dev_path='%s'\n", udev->dev_path); - dbg(udev, "sys_path='%s'\n", udev->sys_path); - dbg(udev, "run_path='%s'\n", udev->run_path); - dbg(udev, "rules_path='%s':'%s':'%s'\n", udev->rules_path[0], udev->rules_path[1], udev->rules_path[2]); - free(config_file); - return udev; -err: - free(config_file); - err(udev, "context creation failed\n"); - udev_unref(udev); - return NULL; -} - -/** - * udev_ref: - * @udev: udev library context - * - * Take a reference of the udev library context. - * - * Returns: the passed udev library context - **/ -UDEV_EXPORT struct udev *udev_ref(struct udev *udev) -{ - if (udev == NULL) - return NULL; - udev->refcount++; - return udev; -} - -/** - * udev_unref: - * @udev: udev library context - * - * Drop a reference of the udev library context. If the refcount - * reaches zero, the resources of the context will be released. - * - **/ -UDEV_EXPORT void udev_unref(struct udev *udev) -{ - if (udev == NULL) - return; - udev->refcount--; - if (udev->refcount > 0) - return; - udev_list_cleanup(&udev->properties_list); - free(udev->dev_path); - free(udev->sys_path); - free(udev->rules_path[0]); - free(udev->rules_path[1]); - free(udev->rules_path[2]); - free(udev->run_path); - dbg(udev, "context %p released\n", udev); - free(udev); -} - -/** - * udev_set_log_fn: - * @udev: udev library context - * @log_fn: function to be called for logging messages - * - * The built-in logging writes to stderr. It can be - * overridden by a custom function, to plug log messages - * into the users' logging functionality. - * - **/ -UDEV_EXPORT void udev_set_log_fn(struct udev *udev, - void (*log_fn)(struct udev *udev, - int priority, const char *file, int line, const char *fn, - const char *format, va_list args)) -{ - udev->log_fn = log_fn; - info(udev, "custom logging function %p registered\n", log_fn); -} - -/** - * udev_get_log_priority: - * @udev: udev library context - * - * The initial logging priority is read from the udev config file - * at startup. - * - * Returns: the current logging priority - **/ -UDEV_EXPORT int udev_get_log_priority(struct udev *udev) -{ - return udev->log_priority; -} - -/** - * udev_set_log_priority: - * @udev: udev library context - * @priority: the new logging priority - * - * Set the current logging priority. The value controls which messages - * are logged. - **/ -UDEV_EXPORT void udev_set_log_priority(struct udev *udev, int priority) -{ - char num[32]; - - udev->log_priority = priority; - snprintf(num, sizeof(num), "%u", udev->log_priority); - udev_add_property(udev, "UDEV_LOG", num); -} - -int udev_get_rules_path(struct udev *udev, char **path[], unsigned long long *stamp_usec[]) -{ - *path = udev->rules_path; - if (stamp_usec) - *stamp_usec = udev->rules_path_ts; - return udev->rules_path_count; -} - -/** - * udev_get_sys_path: - * @udev: udev library context - * - * Retrieve the sysfs mount point. The default is "/sys". For - * testing purposes, it can be overridden with udev_sys= - * in the udev configuration file. - * - * Returns: the sys mount point - **/ -UDEV_EXPORT const char *udev_get_sys_path(struct udev *udev) -{ - if (udev == NULL) - return NULL; - return udev->sys_path; -} - -/** - * udev_get_dev_path: - * @udev: udev library context - * - * Retrieve the device directory path. The default value is "/dev", - * the actual value may be overridden in the udev configuration - * file. - * - * Returns: the device directory path - **/ -UDEV_EXPORT const char *udev_get_dev_path(struct udev *udev) -{ - if (udev == NULL) - return NULL; - return udev->dev_path; -} - -/** - * udev_get_run_path: - * @udev: udev library context - * - * Retrieve the udev runtime directory path. The default is "/run/udev". - * - * Returns: the runtime directory path - **/ -UDEV_EXPORT const char *udev_get_run_path(struct udev *udev) -{ - if (udev == NULL) - return NULL; - return udev->run_path; -} - -struct udev_list_entry *udev_add_property(struct udev *udev, const char *key, const char *value) -{ - if (value == NULL) { - struct udev_list_entry *list_entry; - - list_entry = udev_get_properties_list_entry(udev); - list_entry = udev_list_entry_get_by_name(list_entry, key); - if (list_entry != NULL) - udev_list_entry_delete(list_entry); - return NULL; - } - return udev_list_entry_add(&udev->properties_list, key, value); -} - -struct udev_list_entry *udev_get_properties_list_entry(struct udev *udev) -{ - return udev_list_get_entry(&udev->properties_list); -} diff --git a/src/udev/src/libudev.h b/src/udev/src/libudev.h deleted file mode 100644 index 10e098d4f7..0000000000 --- a/src/udev/src/libudev.h +++ /dev/null @@ -1,189 +0,0 @@ -/* - * libudev - interface to udev device information - * - * Copyright (C) 2008-2011 Kay Sievers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - */ - -#ifndef _LIBUDEV_H_ -#define _LIBUDEV_H_ - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * udev - library context - * - * reads the udev config and system environment - * allows custom logging - */ -struct udev; -struct udev *udev_ref(struct udev *udev); -void udev_unref(struct udev *udev); -struct udev *udev_new(void); -void udev_set_log_fn(struct udev *udev, - void (*log_fn)(struct udev *udev, - int priority, const char *file, int line, const char *fn, - const char *format, va_list args)); -int udev_get_log_priority(struct udev *udev); -void udev_set_log_priority(struct udev *udev, int priority); -const char *udev_get_sys_path(struct udev *udev); -const char *udev_get_dev_path(struct udev *udev); -const char *udev_get_run_path(struct udev *udev); -void *udev_get_userdata(struct udev *udev); -void udev_set_userdata(struct udev *udev, void *userdata); - -/* - * udev_list - * - * access to libudev generated lists - */ -struct udev_list_entry; -struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry); -struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name); -const char *udev_list_entry_get_name(struct udev_list_entry *list_entry); -const char *udev_list_entry_get_value(struct udev_list_entry *list_entry); -/** - * udev_list_entry_foreach: - * @list_entry: entry to store the current position - * @first_entry: first entry to start with - * - * Helper to iterate over all entries of a list. - */ -#define udev_list_entry_foreach(list_entry, first_entry) \ - for (list_entry = first_entry; \ - list_entry != NULL; \ - list_entry = udev_list_entry_get_next(list_entry)) - -/* - * udev_device - * - * access to sysfs/kernel devices - */ -struct udev_device; -struct udev_device *udev_device_ref(struct udev_device *udev_device); -void udev_device_unref(struct udev_device *udev_device); -struct udev *udev_device_get_udev(struct udev_device *udev_device); -struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *syspath); -struct udev_device *udev_device_new_from_devnum(struct udev *udev, char type, dev_t devnum); -struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname); -struct udev_device *udev_device_new_from_environment(struct udev *udev); -/* udev_device_get_parent_*() does not take a reference on the returned device, it is automatically unref'd with the parent */ -struct udev_device *udev_device_get_parent(struct udev_device *udev_device); -struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_device *udev_device, - const char *subsystem, const char *devtype); -/* retrieve device properties */ -const char *udev_device_get_devpath(struct udev_device *udev_device); -const char *udev_device_get_subsystem(struct udev_device *udev_device); -const char *udev_device_get_devtype(struct udev_device *udev_device); -const char *udev_device_get_syspath(struct udev_device *udev_device); -const char *udev_device_get_sysname(struct udev_device *udev_device); -const char *udev_device_get_sysnum(struct udev_device *udev_device); -const char *udev_device_get_devnode(struct udev_device *udev_device); -int udev_device_get_is_initialized(struct udev_device *udev_device); -struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev_device *udev_device); -struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device *udev_device); -struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_device *udev_device); -struct udev_list_entry *udev_device_get_sysattr_list_entry(struct udev_device *udev_device); -const char *udev_device_get_property_value(struct udev_device *udev_device, const char *key); -const char *udev_device_get_driver(struct udev_device *udev_device); -dev_t udev_device_get_devnum(struct udev_device *udev_device); -const char *udev_device_get_action(struct udev_device *udev_device); -unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device); -unsigned long long int udev_device_get_usec_since_initialized(struct udev_device *udev_device); -const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr); -int udev_device_has_tag(struct udev_device *udev_device, const char *tag); - -/* - * udev_monitor - * - * access to kernel uevents and udev events - */ -struct udev_monitor; -struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor); -void udev_monitor_unref(struct udev_monitor *udev_monitor); -struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor); -/* kernel and udev generated events over netlink */ -struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name); -/* custom socket (use netlink and filters instead) */ -struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char *socket_path); -/* bind socket */ -int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor); -int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size); -int udev_monitor_get_fd(struct udev_monitor *udev_monitor); -struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor); -/* in-kernel socket filters to select messages that get delivered to a listener */ -int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, - const char *subsystem, const char *devtype); -int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag); -int udev_monitor_filter_update(struct udev_monitor *udev_monitor); -int udev_monitor_filter_remove(struct udev_monitor *udev_monitor); - -/* - * udev_enumerate - * - * search sysfs for specific devices and provide a sorted list - */ -struct udev_enumerate; -struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate); -void udev_enumerate_unref(struct udev_enumerate *udev_enumerate); -struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate); -struct udev_enumerate *udev_enumerate_new(struct udev *udev); -/* device properties filter */ -int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem); -int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem); -int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value); -int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value); -int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value); -int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname); -int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag); -int udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *parent); -int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate); -int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath); -/* run enumeration with active filters */ -int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate); -int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate); -/* return device list */ -struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate); - -/* - * udev_queue - * - * access to the currently running udev events - */ -struct udev_queue; -struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue); -void udev_queue_unref(struct udev_queue *udev_queue); -struct udev *udev_queue_get_udev(struct udev_queue *udev_queue); -struct udev_queue *udev_queue_new(struct udev *udev); -unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue); -unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue); -int udev_queue_get_udev_is_active(struct udev_queue *udev_queue); -int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue); -int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum); -int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue, - unsigned long long int start, unsigned long long int end); -struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue); - -/* - * udev_util - * - * udev specific utilities - */ -int udev_util_encode_string(const char *str, char *str_enc, size_t len); - - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif diff --git a/src/udev/src/libudev.pc.in b/src/udev/src/libudev.pc.in deleted file mode 100644 index c9a47fc9b8..0000000000 --- a/src/udev/src/libudev.pc.in +++ /dev/null @@ -1,11 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: libudev -Description: Library to access udev device information -Version: @VERSION@ -Libs: -L${libdir} -ludev -lrt -Libs.private: -Cflags: -I${includedir} diff --git a/src/udev/src/mtd_probe/75-probe_mtd.rules b/src/udev/src/mtd_probe/75-probe_mtd.rules deleted file mode 100644 index c0e0839785..0000000000 --- a/src/udev/src/mtd_probe/75-probe_mtd.rules +++ /dev/null @@ -1,8 +0,0 @@ -# do not edit this file, it will be overwritten on update - -ACTION!="add", GOTO="mtd_probe_end" - -KERNEL=="mtd*ro", IMPORT{program}="mtd_probe $devnode" -KERNEL=="mtd*ro", ENV{MTD_FTL}=="smartmedia", IMPORT{builtin}="kmod load sm_ftl" - -LABEL="mtd_probe_end" diff --git a/src/udev/src/mtd_probe/mtd_probe.c b/src/udev/src/mtd_probe/mtd_probe.c deleted file mode 100644 index 1aa08d3851..0000000000 --- a/src/udev/src/mtd_probe/mtd_probe.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2010 - Maxim Levitsky - * - * mtd_probe is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * mtd_probe is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with mtd_probe; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301 USA - */ -#include "mtd_probe.h" -#include -#include -#include -#include -#include -#include -#include -#include - -int main(int argc, char** argv) -{ - if (argc != 2) { - printf("usage: mtd_probe /dev/mtd[n]\n"); - return 1; - } - - int mtd_fd = open(argv[1], O_RDONLY); - if (mtd_fd == -1) { - perror("open"); - exit(-1); - } - - mtd_info_t mtd_info; - int error = ioctl(mtd_fd, MEMGETINFO, &mtd_info); - if (error == -1) { - perror("ioctl"); - exit(-1); - } - - probe_smart_media(mtd_fd, &mtd_info); - return -1; -} diff --git a/src/udev/src/mtd_probe/mtd_probe.h b/src/udev/src/mtd_probe/mtd_probe.h deleted file mode 100644 index 2a37ede578..0000000000 --- a/src/udev/src/mtd_probe/mtd_probe.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2010 - Maxim Levitsky - * - * mtd_probe is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * mtd_probe is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with mtd_probe; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301 USA - */ - -#include - -/* Full oob structure as written on the flash */ -struct sm_oob { - uint32_t reserved; - uint8_t data_status; - uint8_t block_status; - uint8_t lba_copy1[2]; - uint8_t ecc2[3]; - uint8_t lba_copy2[2]; - uint8_t ecc1[3]; -} __attribute__((packed)); - - -/* one sector is always 512 bytes, but it can consist of two nand pages */ -#define SM_SECTOR_SIZE 512 - -/* oob area is also 16 bytes, but might be from two pages */ -#define SM_OOB_SIZE 16 - -/* This is maximum zone size, and all devices that have more that one zone - have this size */ -#define SM_MAX_ZONE_SIZE 1024 - -/* support for small page nand */ -#define SM_SMALL_PAGE 256 -#define SM_SMALL_OOB_SIZE 8 - - -void probe_smart_media(int mtd_fd, mtd_info_t *info); diff --git a/src/udev/src/mtd_probe/probe_smartmedia.c b/src/udev/src/mtd_probe/probe_smartmedia.c deleted file mode 100644 index b3cdefc633..0000000000 --- a/src/udev/src/mtd_probe/probe_smartmedia.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2010 - Maxim Levitsky - * - * mtd_probe is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * mtd_probe is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with mtd_probe; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "mtd_probe.h" - -static const uint8_t cis_signature[] = { - 0x01, 0x03, 0xD9, 0x01, 0xFF, 0x18, 0x02, 0xDF, 0x01, 0x20 -}; - - -void probe_smart_media(int mtd_fd, mtd_info_t* info) -{ - char* cis_buffer = malloc(SM_SECTOR_SIZE); - - if (!cis_buffer) - return; - - if (info->type != MTD_NANDFLASH) - goto exit; - - int sector_size = info->writesize; - int block_size = info->erasesize; - int size_in_megs = info->size / (1024 * 1024); - int spare_count; - - - if (sector_size != SM_SECTOR_SIZE && sector_size != SM_SMALL_PAGE) - goto exit; - - switch(size_in_megs) { - case 1: - case 2: - spare_count = 6; - break; - case 4: - spare_count = 12; - break; - default: - spare_count = 24; - break; - } - - - int offset; - int cis_found = 0; - - for (offset = 0 ; offset < block_size * spare_count ; - offset += sector_size) { - - lseek(mtd_fd, SEEK_SET, offset); - if (read(mtd_fd, cis_buffer, SM_SECTOR_SIZE) == SM_SECTOR_SIZE){ - cis_found = 1; - break; - } - } - - if (!cis_found) - goto exit; - - if (memcmp(cis_buffer, cis_signature, sizeof(cis_signature)) != 0 && - (memcmp(cis_buffer + SM_SMALL_PAGE, cis_signature, - sizeof(cis_signature)) != 0)) - goto exit; - - printf("MTD_FTL=smartmedia\n"); - free(cis_buffer); - exit(0); -exit: - free(cis_buffer); - return; -} diff --git a/src/udev/src/rule_generator/75-cd-aliases-generator.rules b/src/udev/src/rule_generator/75-cd-aliases-generator.rules deleted file mode 100644 index e6da0101d6..0000000000 --- a/src/udev/src/rule_generator/75-cd-aliases-generator.rules +++ /dev/null @@ -1,9 +0,0 @@ -# these rules generate rules for the /dev/{cdrom,dvd,...} symlinks - -# the "path" of usb/ieee1394 devices changes frequently, use "id" -ACTION=="add", SUBSYSTEM=="block", SUBSYSTEMS=="usb|ieee1394", ENV{ID_CDROM}=="?*", ENV{GENERATED}!="?*", \ - PROGRAM="write_cd_rules by-id", SYMLINK+="%c", GOTO="persistent_cd_end" - -ACTION=="add", SUBSYSTEM=="block", ENV{ID_CDROM}=="?*", ENV{GENERATED}!="?*", PROGRAM="write_cd_rules", SYMLINK+="%c" - -LABEL="persistent_cd_end" diff --git a/src/udev/src/rule_generator/75-persistent-net-generator.rules b/src/udev/src/rule_generator/75-persistent-net-generator.rules deleted file mode 100644 index 4f80573478..0000000000 --- a/src/udev/src/rule_generator/75-persistent-net-generator.rules +++ /dev/null @@ -1,102 +0,0 @@ -# do not edit this file, it will be overwritten on update - -# these rules generate rules for persistent network device naming -# -# variables used to communicate: -# MATCHADDR MAC address used for the match -# MATCHID bus_id used for the match -# MATCHDRV driver name used for the match -# MATCHIFTYPE interface type match -# COMMENT comment to add to the generated rule -# INTERFACE_NAME requested name supplied by external tool -# INTERFACE_NEW new interface name returned by rule writer - -ACTION!="add", GOTO="persistent_net_generator_end" -SUBSYSTEM!="net", GOTO="persistent_net_generator_end" - -# ignore the interface if a name has already been set -NAME=="?*", GOTO="persistent_net_generator_end" - -# device name whitelist -KERNEL!="eth*|ath*|wlan*[0-9]|msh*|ra*|sta*|ctc*|lcs*|hsi*", GOTO="persistent_net_generator_end" - -# ignore Xen virtual interfaces -SUBSYSTEMS=="xen", GOTO="persistent_net_generator_end" - -# read MAC address -ENV{MATCHADDR}="$attr{address}" - -# match interface type -ENV{MATCHIFTYPE}="$attr{type}" - -# ignore KVM virtual interfaces -ENV{MATCHADDR}=="52:54:00:*", GOTO="persistent_net_generator_end" -# ignore VMWare virtual interfaces -ENV{MATCHADDR}=="00:0c:29:*|00:50:56:*", GOTO="persistent_net_generator_end" -# ignore Hyper-V virtual interfaces -ENV{MATCHADDR}=="00:15:5d:*", GOTO="persistent_net_generator_end" - -# These vendors are known to violate the local MAC address assignment scheme -# Interlan, DEC (UNIBUS or QBUS), Apollo, Cisco, Racal-Datacom -ENV{MATCHADDR}=="02:07:01:*", GOTO="globally_administered_whitelist" -# 3Com -ENV{MATCHADDR}=="02:60:60:*", GOTO="globally_administered_whitelist" -# 3Com IBM PC; Imagen; Valid; Cisco; Apple -ENV{MATCHADDR}=="02:60:8c:*", GOTO="globally_administered_whitelist" -# Intel -ENV{MATCHADDR}=="02:a0:c9:*", GOTO="globally_administered_whitelist" -# Olivetti -ENV{MATCHADDR}=="02:aa:3c:*", GOTO="globally_administered_whitelist" -# CMC Masscomp; Silicon Graphics; Prime EXL -ENV{MATCHADDR}=="02:cf:1f:*", GOTO="globally_administered_whitelist" -# Prominet Corporation Gigabit Ethernet Switch -ENV{MATCHADDR}=="02:e0:3b:*", GOTO="globally_administered_whitelist" -# BTI (Bus-Tech, Inc.) IBM Mainframes -ENV{MATCHADDR}=="02:e6:d3:*", GOTO="globally_administered_whitelist" -# Realtek -ENV{MATCHADDR}=="52:54:00:*", GOTO="globally_administered_whitelist" -# Novell 2000 -ENV{MATCHADDR}=="52:54:4c:*", GOTO="globally_administered_whitelist" -# Realtec -ENV{MATCHADDR}=="52:54:ab:*", GOTO="globally_administered_whitelist" -# Kingston Technologies -ENV{MATCHADDR}=="e2:0c:0f:*", GOTO="globally_administered_whitelist" -# Xensource -ENV{MATCHADDR}=="00:16:3e:*", GOTO="globally_administered_whitelist" - -# match interface dev_id -ATTR{dev_id}=="?*", ENV{MATCHDEVID}="$attr{dev_id}" - -# do not use "locally administered" MAC address -ENV{MATCHADDR}=="?[2367abef]:*", ENV{MATCHADDR}="" - -# do not use empty address -ENV{MATCHADDR}=="00:00:00:00:00:00", ENV{MATCHADDR}="" - -LABEL="globally_administered_whitelist" - -# build comment line for generated rule: -SUBSYSTEMS=="pci", ENV{COMMENT}="PCI device $attr{vendor}:$attr{device} ($driver)" -SUBSYSTEMS=="usb", ATTRS{idVendor}=="?*", ENV{COMMENT}="USB device 0x$attr{idVendor}:0x$attr{idProduct} ($driver)" -SUBSYSTEMS=="pcmcia", ENV{COMMENT}="PCMCIA device $attr{card_id}:$attr{manf_id} ($driver)" -SUBSYSTEMS=="ieee1394", ENV{COMMENT}="Firewire device $attr{host_id})" - -# ibmveth likes to use "locally administered" MAC addresses -DRIVERS=="ibmveth", ENV{MATCHADDR}="$attr{address}", ENV{COMMENT}="ibmveth ($id)" - -# S/390 uses id matches only, do not use MAC address match -SUBSYSTEMS=="ccwgroup", ENV{COMMENT}="S/390 $driver device at $id", ENV{MATCHID}="$id", ENV{MATCHDRV}="$driver", ENV{MATCHADDR}="" - -# see if we got enough data to create a rule -ENV{MATCHADDR}=="", ENV{MATCHID}=="", ENV{INTERFACE_NAME}=="", GOTO="persistent_net_generator_end" - -# default comment -ENV{COMMENT}=="", ENV{COMMENT}="net device ($attr{driver})" - -# write rule -DRIVERS=="?*", IMPORT{program}="write_net_rules" - -# rename interface if needed -ENV{INTERFACE_NEW}=="?*", NAME="$env{INTERFACE_NEW}" - -LABEL="persistent_net_generator_end" diff --git a/src/udev/src/rule_generator/rule_generator.functions b/src/udev/src/rule_generator/rule_generator.functions deleted file mode 100644 index 2eec1b6abf..0000000000 --- a/src/udev/src/rule_generator/rule_generator.functions +++ /dev/null @@ -1,113 +0,0 @@ -# functions used by the udev rule generator - -# Copyright (C) 2006 Marco d'Itri - -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -PATH='/usr/bin:/bin:/usr/sbin:/sbin' - -# Read a single line from file $1 in the $DEVPATH directory. -# The function must not return an error even if the file does not exist. -sysread() { - local file="$1" - [ -e "/sys$DEVPATH/$file" ] || return 0 - local value - read value < "/sys$DEVPATH/$file" || return 0 - echo "$value" -} - -sysreadlink() { - local file="$1" - [ -e "/sys$DEVPATH/$file" ] || return 0 - readlink -f /sys$DEVPATH/$file 2> /dev/null || true -} - -# Return true if a directory is writeable. -writeable() { - if ln -s test-link $1/.is-writeable 2> /dev/null; then - rm -f $1/.is-writeable - return 0 - else - return 1 - fi -} - -# Create a lock file for the current rules file. -lock_rules_file() { - RUNDIR=$(udevadm info --run) - [ -e "$RUNDIR" ] || return 0 - - RULES_LOCK="$RUNDIR/.lock-${RULES_FILE##*/}" - - retry=30 - while ! mkdir $RULES_LOCK 2> /dev/null; do - if [ $retry -eq 0 ]; then - echo "Cannot lock $RULES_FILE!" >&2 - exit 2 - fi - sleep 1 - retry=$(($retry - 1)) - done -} - -unlock_rules_file() { - [ "$RULES_LOCK" ] || return 0 - rmdir $RULES_LOCK || true -} - -# Choose the real rules file if it is writeable or a temporary file if not. -# Both files should be checked later when looking for existing rules. -choose_rules_file() { - RUNDIR=$(udevadm info --run) - local tmp_rules_file="$RUNDIR/tmp-rules--${RULES_FILE##*/}" - [ -e "$RULES_FILE" -o -e "$tmp_rules_file" ] || PRINT_HEADER=1 - - if writeable ${RULES_FILE%/*}; then - RO_RULES_FILE='/dev/null' - else - RO_RULES_FILE=$RULES_FILE - RULES_FILE=$tmp_rules_file - fi -} - -# Return the name of the first free device. -raw_find_next_available() { - local links="$1" - - local basename=${links%%[ 0-9]*} - local max=-1 - for name in $links; do - local num=${name#$basename} - [ "$num" ] || num=0 - [ $num -gt $max ] && max=$num - done - - local max=$(($max + 1)) - # "name0" actually is just "name" - [ $max -eq 0 ] && return - echo "$max" -} - -# Find all rules matching a key (with action) and a pattern. -find_all_rules() { - local key="$1" - local linkre="$2" - local match="$3" - - local search='.*[[:space:],]'"$key"'"('"$linkre"')".*' - echo $(sed -n -r -e 's/^#.*//' -e "${match}s/${search}/\1/p" \ - $RO_RULES_FILE \ - $([ -e $RULES_FILE ] && echo $RULES_FILE) \ - 2>/dev/null) -} diff --git a/src/udev/src/rule_generator/write_cd_rules b/src/udev/src/rule_generator/write_cd_rules deleted file mode 100644 index 645b9cd521..0000000000 --- a/src/udev/src/rule_generator/write_cd_rules +++ /dev/null @@ -1,126 +0,0 @@ -#!/bin/sh -e - -# This script is run if an optical drive lacks a rule for persistent naming. -# -# It adds symlinks for optical drives based on the device class determined -# by cdrom_id and used ID_PATH to identify the device. - -# (C) 2006 Marco d'Itri -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -# debug, if UDEV_LOG= -if [ -n "$UDEV_LOG" ]; then - if [ "$UDEV_LOG" -ge 7 ]; then - set -x - fi -fi - -RULES_FILE="/etc/udev/rules.d/70-persistent-cd.rules" - -. /lib/udev/rule_generator.functions - -find_next_available() { - raw_find_next_available "$(find_all_rules 'SYMLINK\+=' "$1")" -} - -write_rule() { - local match="$1" - local link="$2" - local comment="$3" - - { - if [ "$PRINT_HEADER" ]; then - PRINT_HEADER= - echo "# This file was automatically generated by the $0" - echo "# program, run by the cd-aliases-generator.rules rules file." - echo "#" - echo "# You can modify it, as long as you keep each rule on a single" - echo "# line, and set the \$GENERATED variable." - echo "" - fi - - [ "$comment" ] && echo "# $comment" - echo "$match, SYMLINK+=\"$link\", ENV{GENERATED}=\"1\"" - } >> $RULES_FILE - SYMLINKS="$SYMLINKS $link" -} - -if [ -z "$DEVPATH" ]; then - echo "Missing \$DEVPATH." >&2 - exit 1 -fi -if [ -z "$ID_CDROM" ]; then - echo "$DEVPATH is not a CD reader." >&2 - exit 1 -fi - -if [ "$1" ]; then - METHOD="$1" -else - METHOD='by-path' -fi - -case "$METHOD" in - by-path) - if [ -z "$ID_PATH" ]; then - echo "$DEVPATH not supported by path_id. by-id may work." >&2 - exit 1 - fi - RULE="ENV{ID_PATH}==\"$ID_PATH\"" - ;; - - by-id) - if [ "$ID_SERIAL" ]; then - RULE="ENV{ID_SERIAL}==\"$ID_SERIAL\"" - elif [ "$ID_MODEL" -a "$ID_REVISION" ]; then - RULE="ENV{ID_MODEL}==\"$ID_MODEL\", ENV{ID_REVISION}==\"$ID_REVISION\"" - else - echo "$DEVPATH not supported by ata_id. by-path may work." >&2 - exit 1 - fi - ;; - - *) - echo "Invalid argument (must be either by-path or by-id)." >&2 - exit 1 - ;; -esac - -# Prevent concurrent processes from modifying the file at the same time. -lock_rules_file - -# Check if the rules file is writeable. -choose_rules_file - -link_num=$(find_next_available 'cdrom[0-9]*') - -match="SUBSYSTEM==\"block\", ENV{ID_CDROM}==\"?*\", $RULE" - -comment="$ID_MODEL ($ID_PATH)" - - write_rule "$match" "cdrom$link_num" "$comment" -[ "$ID_CDROM_CD_R" -o "$ID_CDROM_CD_RW" ] && \ - write_rule "$match" "cdrw$link_num" -[ "$ID_CDROM_DVD" ] && \ - write_rule "$match" "dvd$link_num" -[ "$ID_CDROM_DVD_R" -o "$ID_CDROM_DVD_RW" -o "$ID_CDROM_DVD_RAM" ] && \ - write_rule "$match" "dvdrw$link_num" -echo >> $RULES_FILE - -unlock_rules_file - -echo $SYMLINKS - -exit 0 diff --git a/src/udev/src/rule_generator/write_net_rules b/src/udev/src/rule_generator/write_net_rules deleted file mode 100644 index bcea4b09dc..0000000000 --- a/src/udev/src/rule_generator/write_net_rules +++ /dev/null @@ -1,141 +0,0 @@ -#!/bin/sh -e - -# This script is run to create persistent network device naming rules -# based on properties of the device. -# If the interface needs to be renamed, INTERFACE_NEW= will be printed -# on stdout to allow udev to IMPORT it. - -# variables used to communicate: -# MATCHADDR MAC address used for the match -# MATCHID bus_id used for the match -# MATCHDEVID dev_id used for the match -# MATCHDRV driver name used for the match -# MATCHIFTYPE interface type match -# COMMENT comment to add to the generated rule -# INTERFACE_NAME requested name supplied by external tool -# INTERFACE_NEW new interface name returned by rule writer - -# Copyright (C) 2006 Marco d'Itri -# Copyright (C) 2007 Kay Sievers -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -# debug, if UDEV_LOG= -if [ -n "$UDEV_LOG" ]; then - if [ "$UDEV_LOG" -ge 7 ]; then - set -x - fi -fi - -RULES_FILE='/etc/udev/rules.d/70-persistent-net.rules' - -. /lib/udev/rule_generator.functions - -interface_name_taken() { - local value="$(find_all_rules 'NAME=' $INTERFACE)" - if [ "$value" ]; then - return 0 - else - return 1 - fi -} - -find_next_available() { - raw_find_next_available "$(find_all_rules 'NAME=' "$1")" -} - -write_rule() { - local match="$1" - local name="$2" - local comment="$3" - - { - if [ "$PRINT_HEADER" ]; then - PRINT_HEADER= - echo "# This file was automatically generated by the $0" - echo "# program, run by the persistent-net-generator.rules rules file." - echo "#" - echo "# You can modify it, as long as you keep each rule on a single" - echo "# line, and change only the value of the NAME= key." - fi - - echo "" - [ "$comment" ] && echo "# $comment" - echo "SUBSYSTEM==\"net\", ACTION==\"add\"$match, NAME=\"$name\"" - } >> $RULES_FILE -} - -if [ -z "$INTERFACE" ]; then - echo "missing \$INTERFACE" >&2 - exit 1 -fi - -# Prevent concurrent processes from modifying the file at the same time. -lock_rules_file - -# Check if the rules file is writeable. -choose_rules_file - -# the DRIVERS key is needed to not match bridges and VLAN sub-interfaces -if [ "$MATCHADDR" ]; then - match="$match, DRIVERS==\"?*\", ATTR{address}==\"$MATCHADDR\"" -fi - -if [ "$MATCHDRV" ]; then - match="$match, DRIVERS==\"$MATCHDRV\"" -fi - -if [ "$MATCHDEVID" ]; then - match="$match, ATTR{dev_id}==\"$MATCHDEVID\"" -fi - -if [ "$MATCHID" ]; then - match="$match, KERNELS==\"$MATCHID\"" -fi - -if [ "$MATCHIFTYPE" ]; then - match="$match, ATTR{type}==\"$MATCHIFTYPE\"" -fi - -if [ -z "$match" ]; then - echo "missing valid match" >&2 - unlock_rules_file - exit 1 -fi - -basename=${INTERFACE%%[0-9]*} -match="$match, KERNEL==\"$basename*\"" - -if [ "$INTERFACE_NAME" ]; then - # external tools may request a custom name - COMMENT="$COMMENT (custom name provided by external tool)" - if [ "$INTERFACE_NAME" != "$INTERFACE" ]; then - INTERFACE=$INTERFACE_NAME; - echo "INTERFACE_NEW=$INTERFACE" - fi -else - # if a rule using the current name already exists, find a new name - if interface_name_taken; then - INTERFACE="$basename$(find_next_available "$basename[0-9]*")" - # prevent INTERFACE from being "eth" instead of "eth0" - [ "$INTERFACE" = "${INTERFACE%%[ \[\]0-9]*}" ] && INTERFACE=${INTERFACE}0 - echo "INTERFACE_NEW=$INTERFACE" - fi -fi - -write_rule "$match" "$INTERFACE" "$COMMENT" - -unlock_rules_file - -exit 0 diff --git a/src/udev/src/scsi_id/.gitignore b/src/udev/src/scsi_id/.gitignore deleted file mode 100644 index 6aebddd809..0000000000 --- a/src/udev/src/scsi_id/.gitignore +++ /dev/null @@ -1 +0,0 @@ -scsi_id_version.h diff --git a/src/udev/src/scsi_id/README b/src/udev/src/scsi_id/README deleted file mode 100644 index 9cfe73991c..0000000000 --- a/src/udev/src/scsi_id/README +++ /dev/null @@ -1,4 +0,0 @@ -scsi_id - generate a SCSI unique identifier for a given SCSI device - -Please send questions, comments or patches to or -. diff --git a/src/udev/src/scsi_id/scsi.h b/src/udev/src/scsi_id/scsi.h deleted file mode 100644 index c423cac574..0000000000 --- a/src/udev/src/scsi_id/scsi.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * scsi.h - * - * General scsi and linux scsi specific defines and structs. - * - * Copyright (C) IBM Corp. 2003 - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation version 2 of the License. - */ - -#include - -struct scsi_ioctl_command { - unsigned int inlen; /* excluding scsi command length */ - unsigned int outlen; - unsigned char data[1]; - /* on input, scsi command starts here then opt. data */ -}; - -/* - * Default 5 second timeout - */ -#define DEF_TIMEOUT 5000 - -#define SENSE_BUFF_LEN 32 - -/* - * The request buffer size passed to the SCSI INQUIRY commands, use 254, - * as this is a nice value for some devices, especially some of the usb - * mass storage devices. - */ -#define SCSI_INQ_BUFF_LEN 254 - -/* - * SCSI INQUIRY vendor and model (really product) lengths. - */ -#define VENDOR_LENGTH 8 -#define MODEL_LENGTH 16 - -#define INQUIRY_CMD 0x12 -#define INQUIRY_CMDLEN 6 - -/* - * INQUIRY VPD page 0x83 identifier descriptor related values. Reference the - * SCSI Primary Commands specification for details. - */ - -/* - * id type values of id descriptors. These are assumed to fit in 4 bits. - */ -#define SCSI_ID_VENDOR_SPECIFIC 0 -#define SCSI_ID_T10_VENDOR 1 -#define SCSI_ID_EUI_64 2 -#define SCSI_ID_NAA 3 -#define SCSI_ID_RELPORT 4 -#define SCSI_ID_TGTGROUP 5 -#define SCSI_ID_LUNGROUP 6 -#define SCSI_ID_MD5 7 -#define SCSI_ID_NAME 8 - -/* - * Supported NAA values. These fit in 4 bits, so the "don't care" value - * cannot conflict with real values. - */ -#define SCSI_ID_NAA_DONT_CARE 0xff -#define SCSI_ID_NAA_IEEE_REG 5 -#define SCSI_ID_NAA_IEEE_REG_EXTENDED 6 - -/* - * Supported Code Set values. - */ -#define SCSI_ID_BINARY 1 -#define SCSI_ID_ASCII 2 - -struct scsi_id_search_values { - u_char id_type; - u_char naa_type; - u_char code_set; -}; - -/* - * Following are the "true" SCSI status codes. Linux has traditionally - * used a 1 bit right and masked version of these. So now CHECK_CONDITION - * and friends (in ) are deprecated. - */ -#define SCSI_CHECK_CONDITION 0x2 -#define SCSI_CONDITION_MET 0x4 -#define SCSI_BUSY 0x8 -#define SCSI_IMMEDIATE 0x10 -#define SCSI_IMMEDIATE_CONDITION_MET 0x14 -#define SCSI_RESERVATION_CONFLICT 0x18 -#define SCSI_COMMAND_TERMINATED 0x22 -#define SCSI_TASK_SET_FULL 0x28 -#define SCSI_ACA_ACTIVE 0x30 -#define SCSI_TASK_ABORTED 0x40 diff --git a/src/udev/src/scsi_id/scsi_id.8 b/src/udev/src/scsi_id/scsi_id.8 deleted file mode 100644 index 0d4dba9149..0000000000 --- a/src/udev/src/scsi_id/scsi_id.8 +++ /dev/null @@ -1,119 +0,0 @@ -.TH SCSI_ID 8 "December 2003" "" "Linux Administrator's Manual" -.SH NAME -scsi_id \- retrieve and generate a unique SCSI identifier -.SH SYNOPSIS -.BI scsi_id -[\fIoptions\fP] -.SH "DESCRIPTION" -.B scsi_id -queries a SCSI device via the SCSI INQUIRY vital product data (VPD) page 0x80 or -0x83 and uses the resulting data to generate a value that is unique across -all SCSI devices that properly support page 0x80 or page 0x83. - -If a result is generated it is sent to standard output, and the program -exits with a zero value. If no identifier is output, the program exits -with a non\-zero value. - -\fBscsi_id\fP is primarily for use by other utilities such as \fBudev\fP -that require a unique SCSI identifier. - -By default all devices are assumed black listed, the \fB\-\-whitelisted\fP option must -be specified on the command line or in the config file for any useful -behaviour. - -SCSI commands are sent directly to the device via the SG_IO ioctl -interface. - -In order to generate unique values for either page 0x80 or page 0x83, the -serial numbers or world wide names are prefixed as follows. - -Identifiers based on page 0x80 are prefixed by the character 'S', the SCSI -vendor, the SCSI product (model) and then the the serial number returned -by page 0x80. For example: - -.sp -.nf -# /usr/lib/udev/scsi_id \-\-page=0x80 \-\-whitelisted \-\-device=/dev/sda -SIBM 3542 1T05078453 -.fi -.P - -Identifiers based on page 0x83 are prefixed by the identifier type -followed by the page 0x83 identifier. For example, a device with a NAA -(Name Address Authority) type of 3 (also in this case the page 0x83 -identifier starts with the NAA value of 6): - -.sp -.nf -# /usr/lib/udev/scsi_id \-\-page=0x83 \-\-whitelisted \-\-device=/dev/sda -3600a0b80000b174b000000d63efc5c8c -.fi -.P - -.SH OPTIONS -.TP -.BI \-\-blacklisted -The default behaviour \- treat the device as black listed, and do nothing -unless a white listed device is found in the scsi_id config\-file. -.TP -.BI \-\-device=\| device\^ -Send SG_IO commands to \fBdevice\fP, such as \fB/dev/sdc\fP. -.TP -.BI \-\-config=\| config\-file -Read configuration and black/white list entries from -.B config\-file -rather than the default -.B /etc/scsi_id.config -file. -.TP -.BI \-\-whitelisted -Treat the device as white listed. The \fB\-\-whitelisted\fP option must be specified -on the command line or in the scsi_id configuration file for -.B scsi_id -to generate any output. -.TP -.BI \-\-page=\| 0x80 | 0x83 | pre-spc3-83 -Use SCSI INQUIRY VPD page code 0x80, 0x83, or pre-spc3-83. -.sp -The default -behaviour is to query the available VPD pages, and use page 0x83 if found, -else page 0x80 if found, else nothing. -.sp -Page pre-spc3-83 should only be utilized for those scsi devices which -are not compliant with the SPC-2 or SPC-3 format for page 83. While this -option is used for older model 4, 5, and 6 EMC Symmetrix devices, its -use with SPC-2 or SPC-3 compliant devices will fallback to the page 83 -format supported by these devices. -.TP -.BI \-\-replace-whitespace -Reformat the output : replace all whitespaces by underscores. -.TP -.BI \-\-export -Export all data in KEY= format used to import in other programs. -.TP -.BI \-\-verbose -Generate verbose debugging output. -.TP -.BI \-\-version -Display version number and exit. -.RE - -.SH "FILES" -.nf -.ft B -.ft -.TP -\fI/etc/scsi_id.config\fP -Configuration of black/white list entries and per device options: -# one config per line, short match strings match longer strings -# vendor=string[,model=string],options= -vendor="ATA",options=-p 0x80 -.RE -.fi -.LP -.SH "SEE ALSO" -.BR udev (7) -.SH AUTHORS -Developed by Patrick Mansfield based on SCSI ID -source included in earlier linux 2.5 kernels, sg_utils source, and SCSI -specifications. diff --git a/src/udev/src/scsi_id/scsi_id.c b/src/udev/src/scsi_id/scsi_id.c deleted file mode 100644 index 9bb0d7f538..0000000000 --- a/src/udev/src/scsi_id/scsi_id.c +++ /dev/null @@ -1,657 +0,0 @@ -/* - * Copyright (C) IBM Corp. 2003 - * Copyright (C) SUSE Linux Products GmbH, 2006 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libudev.h" -#include "libudev-private.h" -#include "scsi_id.h" - -static const struct option options[] = { - { "device", required_argument, NULL, 'd' }, - { "config", required_argument, NULL, 'f' }, - { "page", required_argument, NULL, 'p' }, - { "blacklisted", no_argument, NULL, 'b' }, - { "whitelisted", no_argument, NULL, 'g' }, - { "replace-whitespace", no_argument, NULL, 'u' }, - { "sg-version", required_argument, NULL, 's' }, - { "verbose", no_argument, NULL, 'v' }, - { "version", no_argument, NULL, 'V' }, - { "export", no_argument, NULL, 'x' }, - { "help", no_argument, NULL, 'h' }, - {} -}; - -static const char short_options[] = "d:f:ghip:uvVx"; -static const char dev_short_options[] = "bgp:"; - -static int all_good; -static int dev_specified; -static char config_file[MAX_PATH_LEN] = SYSCONFDIR "/scsi_id.config"; -static enum page_code default_page_code; -static int sg_version = 4; -static int use_stderr; -static int debug; -static int reformat_serial; -static int export; -static char vendor_str[64]; -static char model_str[64]; -static char vendor_enc_str[256]; -static char model_enc_str[256]; -static char revision_str[16]; -static char type_str[16]; - -static void log_fn(struct udev *udev, int priority, - const char *file, int line, const char *fn, - const char *format, va_list args) -{ - vsyslog(priority, format, args); -} - -static void set_type(const char *from, char *to, size_t len) -{ - int type_num; - char *eptr; - char *type = "generic"; - - type_num = strtoul(from, &eptr, 0); - if (eptr != from) { - switch (type_num) { - case 0: - type = "disk"; - break; - case 1: - type = "tape"; - break; - case 4: - type = "optical"; - break; - case 5: - type = "cd"; - break; - case 7: - type = "optical"; - break; - case 0xe: - type = "disk"; - break; - case 0xf: - type = "optical"; - break; - default: - break; - } - } - util_strscpy(to, len, type); -} - -/* - * get_value: - * - * buf points to an '=' followed by a quoted string ("foo") or a string ending - * with a space or ','. - * - * Return a pointer to the NUL terminated string, returns NULL if no - * matches. - */ -static char *get_value(char **buffer) -{ - static char *quote_string = "\"\n"; - static char *comma_string = ",\n"; - char *val; - char *end; - - if (**buffer == '"') { - /* - * skip leading quote, terminate when quote seen - */ - (*buffer)++; - end = quote_string; - } else { - end = comma_string; - } - val = strsep(buffer, end); - if (val && end == quote_string) - /* - * skip trailing quote - */ - (*buffer)++; - - while (isspace(**buffer)) - (*buffer)++; - - return val; -} - -static int argc_count(char *opts) -{ - int i = 0; - while (*opts != '\0') - if (*opts++ == ' ') - i++; - return i; -} - -/* - * get_file_options: - * - * If vendor == NULL, find a line in the config file with only "OPTIONS="; - * if vendor and model are set find the first OPTIONS line in the config - * file that matches. Set argc and argv to match the OPTIONS string. - * - * vendor and model can end in '\n'. - */ -static int get_file_options(struct udev *udev, - const char *vendor, const char *model, - int *argc, char ***newargv) -{ - char *buffer; - FILE *fd; - char *buf; - char *str1; - char *vendor_in, *model_in, *options_in; /* read in from file */ - int lineno; - int c; - int retval = 0; - - dbg(udev, "vendor='%s'; model='%s'\n", vendor, model); - fd = fopen(config_file, "r"); - if (fd == NULL) { - dbg(udev, "can't open %s\n", config_file); - if (errno == ENOENT) { - return 1; - } else { - err(udev, "can't open %s: %s\n", config_file, strerror(errno)); - return -1; - } - } - - /* - * Allocate a buffer rather than put it on the stack so we can - * keep it around to parse any options (any allocated newargv - * points into this buffer for its strings). - */ - buffer = malloc(MAX_BUFFER_LEN); - if (!buffer) { - fclose(fd); - err(udev, "can't allocate memory\n"); - return -1; - } - - *newargv = NULL; - lineno = 0; - while (1) { - vendor_in = model_in = options_in = NULL; - - buf = fgets(buffer, MAX_BUFFER_LEN, fd); - if (buf == NULL) - break; - lineno++; - if (buf[strlen(buffer) - 1] != '\n') { - err(udev, "Config file line %d too long\n", lineno); - break; - } - - while (isspace(*buf)) - buf++; - - /* blank or all whitespace line */ - if (*buf == '\0') - continue; - - /* comment line */ - if (*buf == '#') - continue; - - dbg(udev, "lineno %d: '%s'\n", lineno, buf); - str1 = strsep(&buf, "="); - if (str1 && strcasecmp(str1, "VENDOR") == 0) { - str1 = get_value(&buf); - if (!str1) { - retval = -1; - break; - } - vendor_in = str1; - - str1 = strsep(&buf, "="); - if (str1 && strcasecmp(str1, "MODEL") == 0) { - str1 = get_value(&buf); - if (!str1) { - retval = -1; - break; - } - model_in = str1; - str1 = strsep(&buf, "="); - } - } - - if (str1 && strcasecmp(str1, "OPTIONS") == 0) { - str1 = get_value(&buf); - if (!str1) { - retval = -1; - break; - } - options_in = str1; - } - dbg(udev, "config file line %d:\n" - " vendor '%s'; model '%s'; options '%s'\n", - lineno, vendor_in, model_in, options_in); - /* - * Only allow: [vendor=foo[,model=bar]]options=stuff - */ - if (!options_in || (!vendor_in && model_in)) { - err(udev, "Error parsing config file line %d '%s'\n", lineno, buffer); - retval = -1; - break; - } - if (vendor == NULL) { - if (vendor_in == NULL) { - dbg(udev, "matched global option\n"); - break; - } - } else if ((vendor_in && strncmp(vendor, vendor_in, - strlen(vendor_in)) == 0) && - (!model_in || (strncmp(model, model_in, - strlen(model_in)) == 0))) { - /* - * Matched vendor and optionally model. - * - * Note: a short vendor_in or model_in can - * give a partial match (that is FOO - * matches FOOBAR). - */ - dbg(udev, "matched vendor/model\n"); - break; - } else { - dbg(udev, "no match\n"); - } - } - - if (retval == 0) { - if (vendor_in != NULL || model_in != NULL || - options_in != NULL) { - /* - * Something matched. Allocate newargv, and store - * values found in options_in. - */ - strcpy(buffer, options_in); - c = argc_count(buffer) + 2; - *newargv = calloc(c, sizeof(**newargv)); - if (!*newargv) { - err(udev, "can't allocate memory\n"); - retval = -1; - } else { - *argc = c; - c = 0; - /* - * argv[0] at 0 is skipped by getopt, but - * store the buffer address there for - * later freeing - */ - (*newargv)[c] = buffer; - for (c = 1; c < *argc; c++) - (*newargv)[c] = strsep(&buffer, " \t"); - } - } else { - /* No matches */ - retval = 1; - } - } - if (retval != 0) - free(buffer); - fclose(fd); - return retval; -} - -static int set_options(struct udev *udev, - int argc, char **argv, const char *short_opts, - char *maj_min_dev) -{ - int option; - - /* - * optind is a global extern used by getopt. Since we can call - * set_options twice (once for command line, and once for config - * file) we have to reset this back to 1. - */ - optind = 1; - while (1) { - option = getopt_long(argc, argv, short_opts, options, NULL); - if (option == -1) - break; - - if (optarg) - dbg(udev, "option '%c' arg '%s'\n", option, optarg); - else - dbg(udev, "option '%c'\n", option); - - switch (option) { - case 'b': - all_good = 0; - break; - - case 'd': - dev_specified = 1; - util_strscpy(maj_min_dev, MAX_PATH_LEN, optarg); - break; - - case 'e': - use_stderr = 1; - break; - - case 'f': - util_strscpy(config_file, MAX_PATH_LEN, optarg); - break; - - case 'g': - all_good = 1; - break; - - case 'h': - printf("Usage: scsi_id OPTIONS \n" - " --device= device node for SG_IO commands\n" - " --config= location of config file\n" - " --page=0x80|0x83|pre-spc3-83 SCSI page (0x80, 0x83, pre-spc3-83)\n" - " --sg-version=3|4 use SGv3 or SGv4\n" - " --blacklisted threat device as blacklisted\n" - " --whitelisted threat device as whitelisted\n" - " --replace-whitespace replace all whitespaces by underscores\n" - " --verbose verbose logging\n" - " --version print version\n" - " --export print values as environment keys\n" - " --help print this help text\n\n"); - exit(0); - - case 'p': - if (strcmp(optarg, "0x80") == 0) { - default_page_code = PAGE_80; - } else if (strcmp(optarg, "0x83") == 0) { - default_page_code = PAGE_83; - } else if (strcmp(optarg, "pre-spc3-83") == 0) { - default_page_code = PAGE_83_PRE_SPC3; - } else { - err(udev, "Unknown page code '%s'\n", optarg); - return -1; - } - break; - - case 's': - sg_version = atoi(optarg); - if (sg_version < 3 || sg_version > 4) { - err(udev, "Unknown SG version '%s'\n", optarg); - return -1; - } - break; - - case 'u': - reformat_serial = 1; - break; - - case 'x': - export = 1; - break; - - case 'v': - debug++; - break; - - case 'V': - printf("%s\n", VERSION); - exit(0); - break; - - default: - exit(1); - } - } - if (optind < argc && !dev_specified) { - dev_specified = 1; - util_strscpy(maj_min_dev, MAX_PATH_LEN, argv[optind]); - } - return 0; -} - -static int per_dev_options(struct udev *udev, - struct scsi_id_device *dev_scsi, int *good_bad, int *page_code) -{ - int retval; - int newargc; - char **newargv = NULL; - int option; - - *good_bad = all_good; - *page_code = default_page_code; - - retval = get_file_options(udev, vendor_str, model_str, &newargc, &newargv); - - optind = 1; /* reset this global extern */ - while (retval == 0) { - option = getopt_long(newargc, newargv, dev_short_options, options, NULL); - if (option == -1) - break; - - if (optarg) - dbg(udev, "option '%c' arg '%s'\n", option, optarg); - else - dbg(udev, "option '%c'\n", option); - - switch (option) { - case 'b': - *good_bad = 0; - break; - - case 'g': - *good_bad = 1; - break; - - case 'p': - if (strcmp(optarg, "0x80") == 0) { - *page_code = PAGE_80; - } else if (strcmp(optarg, "0x83") == 0) { - *page_code = PAGE_83; - } else if (strcmp(optarg, "pre-spc3-83") == 0) { - *page_code = PAGE_83_PRE_SPC3; - } else { - err(udev, "Unknown page code '%s'\n", optarg); - retval = -1; - } - break; - - default: - err(udev, "Unknown or bad option '%c' (0x%x)\n", option, option); - retval = -1; - break; - } - } - - if (newargv) { - free(newargv[0]); - free(newargv); - } - return retval; -} - -static int set_inq_values(struct udev *udev, struct scsi_id_device *dev_scsi, const char *path) -{ - int retval; - - dev_scsi->use_sg = sg_version; - - retval = scsi_std_inquiry(udev, dev_scsi, path); - if (retval) - return retval; - - udev_util_encode_string(dev_scsi->vendor, vendor_enc_str, sizeof(vendor_enc_str)); - udev_util_encode_string(dev_scsi->model, model_enc_str, sizeof(model_enc_str)); - - util_replace_whitespace(dev_scsi->vendor, vendor_str, sizeof(vendor_str)); - util_replace_chars(vendor_str, NULL); - util_replace_whitespace(dev_scsi->model, model_str, sizeof(model_str)); - util_replace_chars(model_str, NULL); - set_type(dev_scsi->type, type_str, sizeof(type_str)); - util_replace_whitespace(dev_scsi->revision, revision_str, sizeof(revision_str)); - util_replace_chars(revision_str, NULL); - return 0; -} - -/* - * scsi_id: try to get an id, if one is found, printf it to stdout. - * returns a value passed to exit() - 0 if printed an id, else 1. - */ -static int scsi_id(struct udev *udev, char *maj_min_dev) -{ - struct scsi_id_device dev_scsi; - int good_dev; - int page_code; - int retval = 0; - - memset(&dev_scsi, 0x00, sizeof(struct scsi_id_device)); - - if (set_inq_values(udev, &dev_scsi, maj_min_dev) < 0) { - retval = 1; - goto out; - } - - /* get per device (vendor + model) options from the config file */ - per_dev_options(udev, &dev_scsi, &good_dev, &page_code); - dbg(udev, "per dev options: good %d; page code 0x%x\n", good_dev, page_code); - if (!good_dev) { - retval = 1; - goto out; - } - - /* read serial number from mode pages (no values for optical drives) */ - scsi_get_serial(udev, &dev_scsi, maj_min_dev, page_code, MAX_SERIAL_LEN); - - if (export) { - char serial_str[MAX_SERIAL_LEN]; - - printf("ID_SCSI=1\n"); - printf("ID_VENDOR=%s\n", vendor_str); - printf("ID_VENDOR_ENC=%s\n", vendor_enc_str); - printf("ID_MODEL=%s\n", model_str); - printf("ID_MODEL_ENC=%s\n", model_enc_str); - printf("ID_REVISION=%s\n", revision_str); - printf("ID_TYPE=%s\n", type_str); - if (dev_scsi.serial[0] != '\0') { - util_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str)); - util_replace_chars(serial_str, NULL); - printf("ID_SERIAL=%s\n", serial_str); - util_replace_whitespace(dev_scsi.serial_short, serial_str, sizeof(serial_str)); - util_replace_chars(serial_str, NULL); - printf("ID_SERIAL_SHORT=%s\n", serial_str); - } - if (dev_scsi.wwn[0] != '\0') { - printf("ID_WWN=0x%s\n", dev_scsi.wwn); - if (dev_scsi.wwn_vendor_extension[0] != '\0') { - printf("ID_WWN_VENDOR_EXTENSION=0x%s\n", dev_scsi.wwn_vendor_extension); - printf("ID_WWN_WITH_EXTENSION=0x%s%s\n", dev_scsi.wwn, dev_scsi.wwn_vendor_extension); - } else { - printf("ID_WWN_WITH_EXTENSION=0x%s\n", dev_scsi.wwn); - } - } - if (dev_scsi.tgpt_group[0] != '\0') { - printf("ID_TARGET_PORT=%s\n", dev_scsi.tgpt_group); - } - if (dev_scsi.unit_serial_number[0] != '\0') { - printf("ID_SCSI_SERIAL=%s\n", dev_scsi.unit_serial_number); - } - goto out; - } - - if (dev_scsi.serial[0] == '\0') { - retval = 1; - goto out; - } - - if (reformat_serial) { - char serial_str[MAX_SERIAL_LEN]; - - util_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str)); - util_replace_chars(serial_str, NULL); - printf("%s\n", serial_str); - goto out; - } - - printf("%s\n", dev_scsi.serial); -out: - return retval; -} - -int main(int argc, char **argv) -{ - struct udev *udev; - int retval = 0; - char maj_min_dev[MAX_PATH_LEN]; - int newargc; - char **newargv; - - udev = udev_new(); - if (udev == NULL) - goto exit; - - udev_log_init("scsi_id"); - udev_set_log_fn(udev, log_fn); - - /* - * Get config file options. - */ - newargv = NULL; - retval = get_file_options(udev, NULL, NULL, &newargc, &newargv); - if (retval < 0) { - retval = 1; - goto exit; - } - if (newargv && (retval == 0)) { - if (set_options(udev, newargc, newargv, short_options, maj_min_dev) < 0) { - retval = 2; - goto exit; - } - free(newargv); - } - - /* - * Get command line options (overriding any config file settings). - */ - if (set_options(udev, argc, argv, short_options, maj_min_dev) < 0) - exit(1); - - if (!dev_specified) { - err(udev, "no device specified\n"); - retval = 1; - goto exit; - } - - retval = scsi_id(udev, maj_min_dev); - -exit: - udev_unref(udev); - udev_log_close(); - return retval; -} diff --git a/src/udev/src/scsi_id/scsi_id.h b/src/udev/src/scsi_id/scsi_id.h deleted file mode 100644 index 828a98305f..0000000000 --- a/src/udev/src/scsi_id/scsi_id.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) IBM Corp. 2003 - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#define MAX_PATH_LEN 512 - -/* - * MAX_ATTR_LEN: maximum length of the result of reading a sysfs - * attribute. - */ -#define MAX_ATTR_LEN 256 - -/* - * MAX_SERIAL_LEN: the maximum length of the serial number, including - * added prefixes such as vendor and product (model) strings. - */ -#define MAX_SERIAL_LEN 256 - -/* - * MAX_BUFFER_LEN: maximum buffer size and line length used while reading - * the config file. - */ -#define MAX_BUFFER_LEN 256 - -struct scsi_id_device { - char vendor[9]; - char model[17]; - char revision[5]; - char type[33]; - char kernel[64]; - char serial[MAX_SERIAL_LEN]; - char serial_short[MAX_SERIAL_LEN]; - int use_sg; - - /* Always from page 0x80 e.g. 'B3G1P8500RWT' - may not be unique */ - char unit_serial_number[MAX_SERIAL_LEN]; - - /* NULs if not set - otherwise hex encoding using lower-case e.g. '50014ee0016eb572' */ - char wwn[17]; - - /* NULs if not set - otherwise hex encoding using lower-case e.g. '0xe00000d80000' */ - char wwn_vendor_extension[17]; - - /* NULs if not set - otherwise decimal number */ - char tgpt_group[8]; -}; - -extern int scsi_std_inquiry(struct udev *udev, struct scsi_id_device *dev_scsi, const char *devname); -extern int scsi_get_serial (struct udev *udev, struct scsi_id_device *dev_scsi, const char *devname, - int page_code, int len); - -/* - * Page code values. - */ -enum page_code { - PAGE_83_PRE_SPC3 = -0x83, - PAGE_UNSPECIFIED = 0x00, - PAGE_80 = 0x80, - PAGE_83 = 0x83, -}; diff --git a/src/udev/src/scsi_id/scsi_serial.c b/src/udev/src/scsi_id/scsi_serial.c deleted file mode 100644 index f1d63f40cc..0000000000 --- a/src/udev/src/scsi_id/scsi_serial.c +++ /dev/null @@ -1,990 +0,0 @@ -/* - * Copyright (C) IBM Corp. 2003 - * - * Author: Patrick Mansfield - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libudev.h" -#include "libudev-private.h" -#include "scsi.h" -#include "scsi_id.h" - -/* - * A priority based list of id, naa, and binary/ascii for the identifier - * descriptor in VPD page 0x83. - * - * Brute force search for a match starting with the first value in the - * following id_search_list. This is not a performance issue, since there - * is normally one or some small number of descriptors. - */ -static const struct scsi_id_search_values id_search_list[] = { - { SCSI_ID_TGTGROUP, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, - { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG_EXTENDED, SCSI_ID_BINARY }, - { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG_EXTENDED, SCSI_ID_ASCII }, - { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG, SCSI_ID_BINARY }, - { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG, SCSI_ID_ASCII }, - /* - * Devices already exist using NAA values that are now marked - * reserved. These should not conflict with other values, or it is - * a bug in the device. As long as we find the IEEE extended one - * first, we really don't care what other ones are used. Using - * don't care here means that a device that returns multiple - * non-IEEE descriptors in a random order will get different - * names. - */ - { SCSI_ID_NAA, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, - { SCSI_ID_NAA, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII }, - { SCSI_ID_EUI_64, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, - { SCSI_ID_EUI_64, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII }, - { SCSI_ID_T10_VENDOR, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, - { SCSI_ID_T10_VENDOR, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII }, - { SCSI_ID_VENDOR_SPECIFIC, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, - { SCSI_ID_VENDOR_SPECIFIC, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII }, -}; - -static const char hex_str[]="0123456789abcdef"; - -/* - * Values returned in the result/status, only the ones used by the code - * are used here. - */ - -#define DID_NO_CONNECT 0x01 /* Unable to connect before timeout */ -#define DID_BUS_BUSY 0x02 /* Bus remain busy until timeout */ -#define DID_TIME_OUT 0x03 /* Timed out for some other reason */ -#define DRIVER_TIMEOUT 0x06 -#define DRIVER_SENSE 0x08 /* Sense_buffer has been set */ - -/* The following "category" function returns one of the following */ -#define SG_ERR_CAT_CLEAN 0 /* No errors or other information */ -#define SG_ERR_CAT_MEDIA_CHANGED 1 /* interpreted from sense buffer */ -#define SG_ERR_CAT_RESET 2 /* interpreted from sense buffer */ -#define SG_ERR_CAT_TIMEOUT 3 -#define SG_ERR_CAT_RECOVERED 4 /* Successful command after recovered err */ -#define SG_ERR_CAT_NOTSUPPORTED 5 /* Illegal / unsupported command */ -#define SG_ERR_CAT_SENSE 98 /* Something else in the sense buffer */ -#define SG_ERR_CAT_OTHER 99 /* Some other error/warning */ - -static int do_scsi_page80_inquiry(struct udev *udev, - struct scsi_id_device *dev_scsi, int fd, - char *serial, char *serial_short, int max_len); - -static int sg_err_category_new(struct udev *udev, - int scsi_status, int msg_status, int - host_status, int driver_status, const - unsigned char *sense_buffer, int sb_len) -{ - scsi_status &= 0x7e; - - /* - * XXX change to return only two values - failed or OK. - */ - - if (!scsi_status && !host_status && !driver_status) - return SG_ERR_CAT_CLEAN; - - if ((scsi_status == SCSI_CHECK_CONDITION) || - (scsi_status == SCSI_COMMAND_TERMINATED) || - ((driver_status & 0xf) == DRIVER_SENSE)) { - if (sense_buffer && (sb_len > 2)) { - int sense_key; - unsigned char asc; - - if (sense_buffer[0] & 0x2) { - sense_key = sense_buffer[1] & 0xf; - asc = sense_buffer[2]; - } else { - sense_key = sense_buffer[2] & 0xf; - asc = (sb_len > 12) ? sense_buffer[12] : 0; - } - - if (sense_key == RECOVERED_ERROR) - return SG_ERR_CAT_RECOVERED; - else if (sense_key == UNIT_ATTENTION) { - if (0x28 == asc) - return SG_ERR_CAT_MEDIA_CHANGED; - if (0x29 == asc) - return SG_ERR_CAT_RESET; - } else if (sense_key == ILLEGAL_REQUEST) { - return SG_ERR_CAT_NOTSUPPORTED; - } - } - return SG_ERR_CAT_SENSE; - } - if (host_status) { - if ((host_status == DID_NO_CONNECT) || - (host_status == DID_BUS_BUSY) || - (host_status == DID_TIME_OUT)) - return SG_ERR_CAT_TIMEOUT; - } - if (driver_status) { - if (driver_status == DRIVER_TIMEOUT) - return SG_ERR_CAT_TIMEOUT; - } - return SG_ERR_CAT_OTHER; -} - -static int sg_err_category3(struct udev *udev, struct sg_io_hdr *hp) -{ - return sg_err_category_new(udev, - hp->status, hp->msg_status, - hp->host_status, hp->driver_status, - hp->sbp, hp->sb_len_wr); -} - -static int sg_err_category4(struct udev *udev, struct sg_io_v4 *hp) -{ - return sg_err_category_new(udev, hp->device_status, 0, - hp->transport_status, hp->driver_status, - (unsigned char *)(uintptr_t)hp->response, - hp->response_len); -} - -static int scsi_dump_sense(struct udev *udev, - struct scsi_id_device *dev_scsi, - unsigned char *sense_buffer, int sb_len) -{ - int s; - int code; - int sense_class; - int sense_key; - int asc, ascq; -#ifdef DUMP_SENSE - char out_buffer[256]; - int i, j; -#endif - - /* - * Figure out and print the sense key, asc and ascq. - * - * If you want to suppress these for a particular drive model, add - * a black list entry in the scsi_id config file. - * - * XXX We probably need to: lookup the sense/asc/ascq in a retry - * table, and if found return 1 (after dumping the sense, asc, and - * ascq). So, if/when we get something like a power on/reset, - * we'll retry the command. - */ - - dbg(udev, "got check condition\n"); - - if (sb_len < 1) { - info(udev, "%s: sense buffer empty\n", dev_scsi->kernel); - return -1; - } - - sense_class = (sense_buffer[0] >> 4) & 0x07; - code = sense_buffer[0] & 0xf; - - if (sense_class == 7) { - /* - * extended sense data. - */ - s = sense_buffer[7] + 8; - if (sb_len < s) { - info(udev, "%s: sense buffer too small %d bytes, %d bytes too short\n", - dev_scsi->kernel, sb_len, s - sb_len); - return -1; - } - if ((code == 0x0) || (code == 0x1)) { - sense_key = sense_buffer[2] & 0xf; - if (s < 14) { - /* - * Possible? - */ - info(udev, "%s: sense result too" " small %d bytes\n", - dev_scsi->kernel, s); - return -1; - } - asc = sense_buffer[12]; - ascq = sense_buffer[13]; - } else if ((code == 0x2) || (code == 0x3)) { - sense_key = sense_buffer[1] & 0xf; - asc = sense_buffer[2]; - ascq = sense_buffer[3]; - } else { - info(udev, "%s: invalid sense code 0x%x\n", - dev_scsi->kernel, code); - return -1; - } - info(udev, "%s: sense key 0x%x ASC 0x%x ASCQ 0x%x\n", - dev_scsi->kernel, sense_key, asc, ascq); - } else { - if (sb_len < 4) { - info(udev, "%s: sense buffer too small %d bytes, %d bytes too short\n", - dev_scsi->kernel, sb_len, 4 - sb_len); - return -1; - } - - if (sense_buffer[0] < 15) - info(udev, "%s: old sense key: 0x%x\n", dev_scsi->kernel, sense_buffer[0] & 0x0f); - else - info(udev, "%s: sense = %2x %2x\n", - dev_scsi->kernel, sense_buffer[0], sense_buffer[2]); - info(udev, "%s: non-extended sense class %d code 0x%0x\n", - dev_scsi->kernel, sense_class, code); - - } - -#ifdef DUMP_SENSE - for (i = 0, j = 0; (i < s) && (j < 254); i++) { - dbg(udev, "i %d, j %d\n", i, j); - out_buffer[j++] = hex_str[(sense_buffer[i] & 0xf0) >> 4]; - out_buffer[j++] = hex_str[sense_buffer[i] & 0x0f]; - out_buffer[j++] = ' '; - } - out_buffer[j] = '\0'; - info(udev, "%s: sense dump:\n", dev_scsi->kernel); - info(udev, "%s: %s\n", dev_scsi->kernel, out_buffer); - -#endif - return -1; -} - -static int scsi_dump(struct udev *udev, - struct scsi_id_device *dev_scsi, struct sg_io_hdr *io) -{ - if (!io->status && !io->host_status && !io->msg_status && - !io->driver_status) { - /* - * Impossible, should not be called. - */ - info(udev, "%s: called with no error\n", __FUNCTION__); - return -1; - } - - info(udev, "%s: sg_io failed status 0x%x 0x%x 0x%x 0x%x\n", - dev_scsi->kernel, io->driver_status, io->host_status, io->msg_status, io->status); - if (io->status == SCSI_CHECK_CONDITION) - return scsi_dump_sense(udev, dev_scsi, io->sbp, io->sb_len_wr); - else - return -1; -} - -static int scsi_dump_v4(struct udev *udev, - struct scsi_id_device *dev_scsi, struct sg_io_v4 *io) -{ - if (!io->device_status && !io->transport_status && - !io->driver_status) { - /* - * Impossible, should not be called. - */ - info(udev, "%s: called with no error\n", __FUNCTION__); - return -1; - } - - info(udev, "%s: sg_io failed status 0x%x 0x%x 0x%x\n", - dev_scsi->kernel, io->driver_status, io->transport_status, - io->device_status); - if (io->device_status == SCSI_CHECK_CONDITION) - return scsi_dump_sense(udev, dev_scsi, (unsigned char *)(uintptr_t)io->response, - io->response_len); - else - return -1; -} - -static int scsi_inquiry(struct udev *udev, - struct scsi_id_device *dev_scsi, int fd, - unsigned char evpd, unsigned char page, - unsigned char *buf, unsigned int buflen) -{ - unsigned char inq_cmd[INQUIRY_CMDLEN] = - { INQUIRY_CMD, evpd, page, 0, buflen, 0 }; - unsigned char sense[SENSE_BUFF_LEN]; - void *io_buf; - struct sg_io_v4 io_v4; - struct sg_io_hdr io_hdr; - int retry = 3; /* rather random */ - int retval; - - if (buflen > SCSI_INQ_BUFF_LEN) { - info(udev, "buflen %d too long\n", buflen); - return -1; - } - -resend: - dbg(udev, "%s evpd %d, page 0x%x\n", dev_scsi->kernel, evpd, page); - - if (dev_scsi->use_sg == 4) { - memset(&io_v4, 0, sizeof(struct sg_io_v4)); - io_v4.guard = 'Q'; - io_v4.protocol = BSG_PROTOCOL_SCSI; - io_v4.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD; - io_v4.request_len = sizeof(inq_cmd); - io_v4.request = (uintptr_t)inq_cmd; - io_v4.max_response_len = sizeof(sense); - io_v4.response = (uintptr_t)sense; - io_v4.din_xfer_len = buflen; - io_v4.din_xferp = (uintptr_t)buf; - io_buf = (void *)&io_v4; - } else { - memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = sizeof(inq_cmd); - io_hdr.mx_sb_len = sizeof(sense); - io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; - io_hdr.dxfer_len = buflen; - io_hdr.dxferp = buf; - io_hdr.cmdp = inq_cmd; - io_hdr.sbp = sense; - io_hdr.timeout = DEF_TIMEOUT; - io_buf = (void *)&io_hdr; - } - - retval = ioctl(fd, SG_IO, io_buf); - if (retval < 0) { - if ((errno == EINVAL || errno == ENOSYS) && dev_scsi->use_sg == 4) { - dev_scsi->use_sg = 3; - goto resend; - } - info(udev, "%s: ioctl failed: %s\n", dev_scsi->kernel, strerror(errno)); - goto error; - } - - if (dev_scsi->use_sg == 4) - retval = sg_err_category4(udev, io_buf); - else - retval = sg_err_category3(udev, io_buf); - - switch (retval) { - case SG_ERR_CAT_NOTSUPPORTED: - buf[1] = 0; - /* Fallthrough */ - case SG_ERR_CAT_CLEAN: - case SG_ERR_CAT_RECOVERED: - retval = 0; - break; - - default: - if (dev_scsi->use_sg == 4) - retval = scsi_dump_v4(udev, dev_scsi, io_buf); - else - retval = scsi_dump(udev, dev_scsi, io_buf); - } - - if (!retval) { - retval = buflen; - } else if (retval > 0) { - if (--retry > 0) { - dbg(udev, "%s: Retrying ...\n", dev_scsi->kernel); - goto resend; - } - retval = -1; - } - -error: - if (retval < 0) - info(udev, "%s: Unable to get INQUIRY vpd %d page 0x%x.\n", - dev_scsi->kernel, evpd, page); - - return retval; -} - -/* Get list of supported EVPD pages */ -static int do_scsi_page0_inquiry(struct udev *udev, - struct scsi_id_device *dev_scsi, int fd, - unsigned char *buffer, unsigned int len) -{ - int retval; - - memset(buffer, 0, len); - retval = scsi_inquiry(udev, dev_scsi, fd, 1, 0x0, buffer, len); - if (retval < 0) - return 1; - - if (buffer[1] != 0) { - info(udev, "%s: page 0 not available.\n", dev_scsi->kernel); - return 1; - } - if (buffer[3] > len) { - info(udev, "%s: page 0 buffer too long %d\n", dev_scsi->kernel, buffer[3]); - return 1; - } - - /* - * Following check is based on code once included in the 2.5.x - * kernel. - * - * Some ill behaved devices return the standard inquiry here - * rather than the evpd data, snoop the data to verify. - */ - if (buffer[3] > MODEL_LENGTH) { - /* - * If the vendor id appears in the page assume the page is - * invalid. - */ - if (!strncmp((char *)&buffer[VENDOR_LENGTH], dev_scsi->vendor, VENDOR_LENGTH)) { - info(udev, "%s: invalid page0 data\n", dev_scsi->kernel); - return 1; - } - } - return 0; -} - -/* - * The caller checks that serial is long enough to include the vendor + - * model. - */ -static int prepend_vendor_model(struct udev *udev, - struct scsi_id_device *dev_scsi, char *serial) -{ - int ind; - - strncpy(serial, dev_scsi->vendor, VENDOR_LENGTH); - strncat(serial, dev_scsi->model, MODEL_LENGTH); - ind = strlen(serial); - - /* - * This is not a complete check, since we are using strncat/cpy - * above, ind will never be too large. - */ - if (ind != (VENDOR_LENGTH + MODEL_LENGTH)) { - info(udev, "%s: expected length %d, got length %d\n", - dev_scsi->kernel, (VENDOR_LENGTH + MODEL_LENGTH), ind); - return -1; - } - return ind; -} - -/** - * check_fill_0x83_id - check the page 0x83 id, if OK allocate and fill - * serial number. - **/ -static int check_fill_0x83_id(struct udev *udev, - struct scsi_id_device *dev_scsi, - unsigned char *page_83, - const struct scsi_id_search_values - *id_search, char *serial, char *serial_short, - int max_len, char *wwn, - char *wwn_vendor_extension, char *tgpt_group) -{ - int i, j, s, len; - - /* - * ASSOCIATION must be with the device (value 0) - * or with the target port for SCSI_ID_TGTPORT - */ - if ((page_83[1] & 0x30) == 0x10) { - if (id_search->id_type != SCSI_ID_TGTGROUP) - return 1; - } else if ((page_83[1] & 0x30) != 0) { - return 1; - } - - if ((page_83[1] & 0x0f) != id_search->id_type) - return 1; - - /* - * Possibly check NAA sub-type. - */ - if ((id_search->naa_type != SCSI_ID_NAA_DONT_CARE) && - (id_search->naa_type != (page_83[4] & 0xf0) >> 4)) - return 1; - - /* - * Check for matching code set - ASCII or BINARY. - */ - if ((page_83[0] & 0x0f) != id_search->code_set) - return 1; - - /* - * page_83[3]: identifier length - */ - len = page_83[3]; - if ((page_83[0] & 0x0f) != SCSI_ID_ASCII) - /* - * If not ASCII, use two bytes for each binary value. - */ - len *= 2; - - /* - * Add one byte for the NUL termination, and one for the id_type. - */ - len += 2; - if (id_search->id_type == SCSI_ID_VENDOR_SPECIFIC) - len += VENDOR_LENGTH + MODEL_LENGTH; - - if (max_len < len) { - info(udev, "%s: length %d too short - need %d\n", - dev_scsi->kernel, max_len, len); - return 1; - } - - if (id_search->id_type == SCSI_ID_TGTGROUP && tgpt_group != NULL) { - unsigned int group; - - group = ((unsigned int)page_83[6] << 8) | page_83[7]; - sprintf(tgpt_group,"%x", group); - return 1; - } - - serial[0] = hex_str[id_search->id_type]; - - /* - * For SCSI_ID_VENDOR_SPECIFIC prepend the vendor and model before - * the id since it is not unique across all vendors and models, - * this differs from SCSI_ID_T10_VENDOR, where the vendor is - * included in the identifier. - */ - if (id_search->id_type == SCSI_ID_VENDOR_SPECIFIC) - if (prepend_vendor_model(udev, dev_scsi, &serial[1]) < 0) { - dbg(udev, "prepend failed\n"); - return 1; - } - - i = 4; /* offset to the start of the identifier */ - s = j = strlen(serial); - if ((page_83[0] & 0x0f) == SCSI_ID_ASCII) { - /* - * ASCII descriptor. - */ - while (i < (4 + page_83[3])) - serial[j++] = page_83[i++]; - } else { - /* - * Binary descriptor, convert to ASCII, using two bytes of - * ASCII for each byte in the page_83. - */ - while (i < (4 + page_83[3])) { - serial[j++] = hex_str[(page_83[i] & 0xf0) >> 4]; - serial[j++] = hex_str[page_83[i] & 0x0f]; - i++; - } - } - - strcpy(serial_short, &serial[s]); - - if (id_search->id_type == SCSI_ID_NAA && wwn != NULL) { - strncpy(wwn, &serial[s], 16); - if (wwn_vendor_extension != NULL) { - strncpy(wwn_vendor_extension, &serial[s + 16], 16); - } - } - - return 0; -} - -/* Extract the raw binary from VPD 0x83 pre-SPC devices */ -static int check_fill_0x83_prespc3(struct udev *udev, - struct scsi_id_device *dev_scsi, - unsigned char *page_83, - const struct scsi_id_search_values - *id_search, char *serial, char *serial_short, int max_len) -{ - int i, j; - - dbg(udev, "using pre-spc3-83 for %s\n", dev_scsi->kernel); - serial[0] = hex_str[id_search->id_type]; - /* serial has been memset to zero before */ - j = strlen(serial); /* j = 1; */ - - for (i = 0; (i < page_83[3]) && (j < max_len-3); ++i) { - serial[j++] = hex_str[(page_83[4+i] & 0xf0) >> 4]; - serial[j++] = hex_str[ page_83[4+i] & 0x0f]; - } - serial[max_len-1] = 0; - strncpy(serial_short, serial, max_len-1); - return 0; -} - - -/* Get device identification VPD page */ -static int do_scsi_page83_inquiry(struct udev *udev, - struct scsi_id_device *dev_scsi, int fd, - char *serial, char *serial_short, int len, - char *unit_serial_number, char *wwn, - char *wwn_vendor_extension, char *tgpt_group) -{ - int retval; - unsigned int id_ind, j; - unsigned char page_83[SCSI_INQ_BUFF_LEN]; - - /* also pick up the page 80 serial number */ - do_scsi_page80_inquiry(udev, dev_scsi, fd, NULL, unit_serial_number, MAX_SERIAL_LEN); - - memset(page_83, 0, SCSI_INQ_BUFF_LEN); - retval = scsi_inquiry(udev, dev_scsi, fd, 1, PAGE_83, page_83, - SCSI_INQ_BUFF_LEN); - if (retval < 0) - return 1; - - if (page_83[1] != PAGE_83) { - info(udev, "%s: Invalid page 0x83\n", dev_scsi->kernel); - return 1; - } - - /* - * XXX Some devices (IBM 3542) return all spaces for an identifier if - * the LUN is not actually configured. This leads to identifiers of - * the form: "1 ". - */ - - /* - * Model 4, 5, and (some) model 6 EMC Symmetrix devices return - * a page 83 reply according to SCSI-2 format instead of SPC-2/3. - * - * The SCSI-2 page 83 format returns an IEEE WWN in binary - * encoded hexi-decimal in the 16 bytes following the initial - * 4-byte page 83 reply header. - * - * Both the SPC-2 and SPC-3 formats return an IEEE WWN as part - * of an Identification descriptor. The 3rd byte of the first - * Identification descriptor is a reserved (BSZ) byte field. - * - * Reference the 7th byte of the page 83 reply to determine - * whether the reply is compliant with SCSI-2 or SPC-2/3 - * specifications. A zero value in the 7th byte indicates - * an SPC-2/3 conformant reply, (i.e., the reserved field of the - * first Identification descriptor). This byte will be non-zero - * for a SCSI-2 conformant page 83 reply from these EMC - * Symmetrix models since the 7th byte of the reply corresponds - * to the 4th and 5th nibbles of the 6-byte OUI for EMC, that is, - * 0x006048. - */ - - if (page_83[6] != 0) - return check_fill_0x83_prespc3(udev, - dev_scsi, page_83, id_search_list, - serial, serial_short, len); - - /* - * Search for a match in the prioritized id_search_list - since WWN ids - * come first we can pick up the WWN in check_fill_0x83_id(). - */ - for (id_ind = 0; - id_ind < sizeof(id_search_list)/sizeof(id_search_list[0]); - id_ind++) { - /* - * Examine each descriptor returned. There is normally only - * one or a small number of descriptors. - */ - for (j = 4; j <= (unsigned int)page_83[3] + 3; j += page_83[j + 3] + 4) { - retval = check_fill_0x83_id(udev, - dev_scsi, &page_83[j], - &id_search_list[id_ind], - serial, serial_short, len, - wwn, wwn_vendor_extension, - tgpt_group); - dbg(udev, "%s id desc %d/%d/%d\n", dev_scsi->kernel, - id_search_list[id_ind].id_type, - id_search_list[id_ind].naa_type, - id_search_list[id_ind].code_set); - if (!retval) { - dbg(udev, " used\n"); - return retval; - } else if (retval < 0) { - dbg(udev, " failed\n"); - return retval; - } else { - dbg(udev, " not used\n"); - } - } - } - return 1; -} - -/* - * Get device identification VPD page for older SCSI-2 device which is not - * compliant with either SPC-2 or SPC-3 format. - * - * Return the hard coded error code value 2 if the page 83 reply is not - * conformant to the SCSI-2 format. - */ -static int do_scsi_page83_prespc3_inquiry(struct udev *udev, - struct scsi_id_device *dev_scsi, int fd, - char *serial, char *serial_short, int len) -{ - int retval; - int i, j; - unsigned char page_83[SCSI_INQ_BUFF_LEN]; - - memset(page_83, 0, SCSI_INQ_BUFF_LEN); - retval = scsi_inquiry(udev, dev_scsi, fd, 1, PAGE_83, page_83, SCSI_INQ_BUFF_LEN); - if (retval < 0) - return 1; - - if (page_83[1] != PAGE_83) { - info(udev, "%s: Invalid page 0x83\n", dev_scsi->kernel); - return 1; - } - /* - * Model 4, 5, and (some) model 6 EMC Symmetrix devices return - * a page 83 reply according to SCSI-2 format instead of SPC-2/3. - * - * The SCSI-2 page 83 format returns an IEEE WWN in binary - * encoded hexi-decimal in the 16 bytes following the initial - * 4-byte page 83 reply header. - * - * Both the SPC-2 and SPC-3 formats return an IEEE WWN as part - * of an Identification descriptor. The 3rd byte of the first - * Identification descriptor is a reserved (BSZ) byte field. - * - * Reference the 7th byte of the page 83 reply to determine - * whether the reply is compliant with SCSI-2 or SPC-2/3 - * specifications. A zero value in the 7th byte indicates - * an SPC-2/3 conformant reply, (i.e., the reserved field of the - * first Identification descriptor). This byte will be non-zero - * for a SCSI-2 conformant page 83 reply from these EMC - * Symmetrix models since the 7th byte of the reply corresponds - * to the 4th and 5th nibbles of the 6-byte OUI for EMC, that is, - * 0x006048. - */ - if (page_83[6] == 0) - return 2; - - serial[0] = hex_str[id_search_list[0].id_type]; - /* - * The first four bytes contain data, not a descriptor. - */ - i = 4; - j = strlen(serial); - /* - * Binary descriptor, convert to ASCII, - * using two bytes of ASCII for each byte - * in the page_83. - */ - while (i < (page_83[3]+4)) { - serial[j++] = hex_str[(page_83[i] & 0xf0) >> 4]; - serial[j++] = hex_str[page_83[i] & 0x0f]; - i++; - } - dbg(udev, "using pre-spc3-83 for %s\n", dev_scsi->kernel); - return 0; -} - -/* Get unit serial number VPD page */ -static int do_scsi_page80_inquiry(struct udev *udev, - struct scsi_id_device *dev_scsi, int fd, - char *serial, char *serial_short, int max_len) -{ - int retval; - int ser_ind; - int i; - int len; - unsigned char buf[SCSI_INQ_BUFF_LEN]; - - memset(buf, 0, SCSI_INQ_BUFF_LEN); - retval = scsi_inquiry(udev, dev_scsi, fd, 1, PAGE_80, buf, SCSI_INQ_BUFF_LEN); - if (retval < 0) - return retval; - - if (buf[1] != PAGE_80) { - info(udev, "%s: Invalid page 0x80\n", dev_scsi->kernel); - return 1; - } - - len = 1 + VENDOR_LENGTH + MODEL_LENGTH + buf[3]; - if (max_len < len) { - info(udev, "%s: length %d too short - need %d\n", - dev_scsi->kernel, max_len, len); - return 1; - } - /* - * Prepend 'S' to avoid unlikely collision with page 0x83 vendor - * specific type where we prepend '0' + vendor + model. - */ - len = buf[3]; - if (serial != NULL) { - serial[0] = 'S'; - ser_ind = prepend_vendor_model(udev, dev_scsi, &serial[1]); - if (ser_ind < 0) - return 1; - for (i = 4; i < len + 4; i++, ser_ind++) - serial[ser_ind] = buf[i]; - } - if (serial_short != NULL) { - memcpy(serial_short, &buf[4], len); - serial_short[len] = '\0'; - } - return 0; -} - -int scsi_std_inquiry(struct udev *udev, - struct scsi_id_device *dev_scsi, const char *devname) -{ - int fd; - unsigned char buf[SCSI_INQ_BUFF_LEN]; - struct stat statbuf; - int err = 0; - - dbg(udev, "opening %s\n", devname); - fd = open(devname, O_RDONLY | O_NONBLOCK); - if (fd < 0) { - info(udev, "scsi_id: cannot open %s: %s\n", - devname, strerror(errno)); - return 1; - } - - if (fstat(fd, &statbuf) < 0) { - info(udev, "scsi_id: cannot stat %s: %s\n", - devname, strerror(errno)); - err = 2; - goto out; - } - sprintf(dev_scsi->kernel,"%d:%d", major(statbuf.st_rdev), - minor(statbuf.st_rdev)); - - memset(buf, 0, SCSI_INQ_BUFF_LEN); - err = scsi_inquiry(udev, dev_scsi, fd, 0, 0, buf, SCSI_INQ_BUFF_LEN); - if (err < 0) - goto out; - - err = 0; - memcpy(dev_scsi->vendor, buf + 8, 8); - dev_scsi->vendor[8] = '\0'; - memcpy(dev_scsi->model, buf + 16, 16); - dev_scsi->model[16] = '\0'; - memcpy(dev_scsi->revision, buf + 32, 4); - dev_scsi->revision[4] = '\0'; - sprintf(dev_scsi->type,"%x", buf[0] & 0x1f); - -out: - close(fd); - return err; -} - -int scsi_get_serial(struct udev *udev, - struct scsi_id_device *dev_scsi, const char *devname, - int page_code, int len) -{ - unsigned char page0[SCSI_INQ_BUFF_LEN]; - int fd = -1; - int cnt; - int ind; - int retval; - - memset(dev_scsi->serial, 0, len); - dbg(udev, "opening %s\n", devname); - srand((unsigned int)getpid()); - for (cnt = 20; cnt > 0; cnt--) { - struct timespec duration; - - fd = open(devname, O_RDONLY | O_NONBLOCK); - if (fd >= 0 || errno != EBUSY) - break; - duration.tv_sec = 0; - duration.tv_nsec = (200 * 1000 * 1000) + (rand() % 100 * 1000 * 1000); - nanosleep(&duration, NULL); - } - if (fd < 0) - return 1; - - if (page_code == PAGE_80) { - if (do_scsi_page80_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len)) { - retval = 1; - goto completed; - } else { - retval = 0; - goto completed; - } - } else if (page_code == PAGE_83) { - if (do_scsi_page83_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len, dev_scsi->unit_serial_number, dev_scsi->wwn, dev_scsi->wwn_vendor_extension, dev_scsi->tgpt_group)) { - retval = 1; - goto completed; - } else { - retval = 0; - goto completed; - } - } else if (page_code == PAGE_83_PRE_SPC3) { - retval = do_scsi_page83_prespc3_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len); - if (retval) { - /* - * Fallback to servicing a SPC-2/3 compliant page 83 - * inquiry if the page 83 reply format does not - * conform to pre-SPC3 expectations. - */ - if (retval == 2) { - if (do_scsi_page83_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len, dev_scsi->unit_serial_number, dev_scsi->wwn, dev_scsi->wwn_vendor_extension, dev_scsi->tgpt_group)) { - retval = 1; - goto completed; - } else { - retval = 0; - goto completed; - } - } - else { - retval = 1; - goto completed; - } - } else { - retval = 0; - goto completed; - } - } else if (page_code != 0x00) { - info(udev, "%s: unsupported page code 0x%d\n", dev_scsi->kernel, page_code); - return 1; - } - - /* - * Get page 0, the page of the pages. By default, try from best to - * worst of supported pages: 0x83 then 0x80. - */ - if (do_scsi_page0_inquiry(udev, dev_scsi, fd, page0, SCSI_INQ_BUFF_LEN)) { - /* - * Don't try anything else. Black list if a specific page - * should be used for this vendor+model, or maybe have an - * optional fall-back to page 0x80 or page 0x83. - */ - retval = 1; - goto completed; - } - - dbg(udev, "%s: Checking page0\n", dev_scsi->kernel); - - for (ind = 4; ind <= page0[3] + 3; ind++) - if (page0[ind] == PAGE_83) - if (!do_scsi_page83_inquiry(udev, dev_scsi, fd, - dev_scsi->serial, dev_scsi->serial_short, len, dev_scsi->unit_serial_number, dev_scsi->wwn, dev_scsi->wwn_vendor_extension, dev_scsi->tgpt_group)) { - /* - * Success - */ - retval = 0; - goto completed; - } - - for (ind = 4; ind <= page0[3] + 3; ind++) - if (page0[ind] == PAGE_80) - if (!do_scsi_page80_inquiry(udev, dev_scsi, fd, - dev_scsi->serial, dev_scsi->serial_short, len)) { - /* - * Success - */ - retval = 0; - goto completed; - } - retval = 1; - -completed: - close(fd); - return retval; -} diff --git a/src/udev/src/sd-daemon.c b/src/udev/src/sd-daemon.c deleted file mode 100644 index 763e079b4e..0000000000 --- a/src/udev/src/sd-daemon.c +++ /dev/null @@ -1,530 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - Copyright 2010 Lennart Poettering - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation files - (the "Software"), to deal in the Software without restriction, - including without limitation the rights to use, copy, modify, merge, - publish, distribute, sublicense, and/or sell copies of the Software, - and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -***/ - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include -#include -#include -#include -#ifdef __BIONIC__ -#include -#else -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(__linux__) -#include -#endif - -#include "sd-daemon.h" - -#if (__GNUC__ >= 4) -#ifdef SD_EXPORT_SYMBOLS -/* Export symbols */ -#define _sd_export_ __attribute__ ((visibility("default"))) -#else -/* Don't export the symbols */ -#define _sd_export_ __attribute__ ((visibility("hidden"))) -#endif -#else -#define _sd_export_ -#endif - -_sd_export_ int sd_listen_fds(int unset_environment) { - -#if defined(DISABLE_SYSTEMD) || !defined(__linux__) - return 0; -#else - int r, fd; - const char *e; - char *p = NULL; - unsigned long l; - - if (!(e = getenv("LISTEN_PID"))) { - r = 0; - goto finish; - } - - errno = 0; - l = strtoul(e, &p, 10); - - if (errno != 0) { - r = -errno; - goto finish; - } - - if (!p || *p || l <= 0) { - r = -EINVAL; - goto finish; - } - - /* Is this for us? */ - if (getpid() != (pid_t) l) { - r = 0; - goto finish; - } - - if (!(e = getenv("LISTEN_FDS"))) { - r = 0; - goto finish; - } - - errno = 0; - l = strtoul(e, &p, 10); - - if (errno != 0) { - r = -errno; - goto finish; - } - - if (!p || *p) { - r = -EINVAL; - goto finish; - } - - for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) l; fd ++) { - int flags; - - if ((flags = fcntl(fd, F_GETFD)) < 0) { - r = -errno; - goto finish; - } - - if (flags & FD_CLOEXEC) - continue; - - if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) { - r = -errno; - goto finish; - } - } - - r = (int) l; - -finish: - if (unset_environment) { - unsetenv("LISTEN_PID"); - unsetenv("LISTEN_FDS"); - } - - return r; -#endif -} - -_sd_export_ int sd_is_fifo(int fd, const char *path) { - struct stat st_fd; - - if (fd < 0) - return -EINVAL; - - memset(&st_fd, 0, sizeof(st_fd)); - if (fstat(fd, &st_fd) < 0) - return -errno; - - if (!S_ISFIFO(st_fd.st_mode)) - return 0; - - if (path) { - struct stat st_path; - - memset(&st_path, 0, sizeof(st_path)); - if (stat(path, &st_path) < 0) { - - if (errno == ENOENT || errno == ENOTDIR) - return 0; - - return -errno; - } - - return - st_path.st_dev == st_fd.st_dev && - st_path.st_ino == st_fd.st_ino; - } - - return 1; -} - -_sd_export_ int sd_is_special(int fd, const char *path) { - struct stat st_fd; - - if (fd < 0) - return -EINVAL; - - if (fstat(fd, &st_fd) < 0) - return -errno; - - if (!S_ISREG(st_fd.st_mode) && !S_ISCHR(st_fd.st_mode)) - return 0; - - if (path) { - struct stat st_path; - - if (stat(path, &st_path) < 0) { - - if (errno == ENOENT || errno == ENOTDIR) - return 0; - - return -errno; - } - - if (S_ISREG(st_fd.st_mode) && S_ISREG(st_path.st_mode)) - return - st_path.st_dev == st_fd.st_dev && - st_path.st_ino == st_fd.st_ino; - else if (S_ISCHR(st_fd.st_mode) && S_ISCHR(st_path.st_mode)) - return st_path.st_rdev == st_fd.st_rdev; - else - return 0; - } - - return 1; -} - -static int sd_is_socket_internal(int fd, int type, int listening) { - struct stat st_fd; - - if (fd < 0 || type < 0) - return -EINVAL; - - if (fstat(fd, &st_fd) < 0) - return -errno; - - if (!S_ISSOCK(st_fd.st_mode)) - return 0; - - if (type != 0) { - int other_type = 0; - socklen_t l = sizeof(other_type); - - if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0) - return -errno; - - if (l != sizeof(other_type)) - return -EINVAL; - - if (other_type != type) - return 0; - } - - if (listening >= 0) { - int accepting = 0; - socklen_t l = sizeof(accepting); - - if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0) - return -errno; - - if (l != sizeof(accepting)) - return -EINVAL; - - if (!accepting != !listening) - return 0; - } - - return 1; -} - -union sockaddr_union { - struct sockaddr sa; - struct sockaddr_in in4; - struct sockaddr_in6 in6; - struct sockaddr_un un; - struct sockaddr_storage storage; -}; - -_sd_export_ int sd_is_socket(int fd, int family, int type, int listening) { - int r; - - if (family < 0) - return -EINVAL; - - if ((r = sd_is_socket_internal(fd, type, listening)) <= 0) - return r; - - if (family > 0) { - union sockaddr_union sockaddr; - socklen_t l; - - memset(&sockaddr, 0, sizeof(sockaddr)); - l = sizeof(sockaddr); - - if (getsockname(fd, &sockaddr.sa, &l) < 0) - return -errno; - - if (l < sizeof(sa_family_t)) - return -EINVAL; - - return sockaddr.sa.sa_family == family; - } - - return 1; -} - -_sd_export_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) { - union sockaddr_union sockaddr; - socklen_t l; - int r; - - if (family != 0 && family != AF_INET && family != AF_INET6) - return -EINVAL; - - if ((r = sd_is_socket_internal(fd, type, listening)) <= 0) - return r; - - memset(&sockaddr, 0, sizeof(sockaddr)); - l = sizeof(sockaddr); - - if (getsockname(fd, &sockaddr.sa, &l) < 0) - return -errno; - - if (l < sizeof(sa_family_t)) - return -EINVAL; - - if (sockaddr.sa.sa_family != AF_INET && - sockaddr.sa.sa_family != AF_INET6) - return 0; - - if (family > 0) - if (sockaddr.sa.sa_family != family) - return 0; - - if (port > 0) { - if (sockaddr.sa.sa_family == AF_INET) { - if (l < sizeof(struct sockaddr_in)) - return -EINVAL; - - return htons(port) == sockaddr.in4.sin_port; - } else { - if (l < sizeof(struct sockaddr_in6)) - return -EINVAL; - - return htons(port) == sockaddr.in6.sin6_port; - } - } - - return 1; -} - -_sd_export_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) { - union sockaddr_union sockaddr; - socklen_t l; - int r; - - if ((r = sd_is_socket_internal(fd, type, listening)) <= 0) - return r; - - memset(&sockaddr, 0, sizeof(sockaddr)); - l = sizeof(sockaddr); - - if (getsockname(fd, &sockaddr.sa, &l) < 0) - return -errno; - - if (l < sizeof(sa_family_t)) - return -EINVAL; - - if (sockaddr.sa.sa_family != AF_UNIX) - return 0; - - if (path) { - if (length <= 0) - length = strlen(path); - - if (length <= 0) - /* Unnamed socket */ - return l == offsetof(struct sockaddr_un, sun_path); - - if (path[0]) - /* Normal path socket */ - return - (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) && - memcmp(path, sockaddr.un.sun_path, length+1) == 0; - else - /* Abstract namespace socket */ - return - (l == offsetof(struct sockaddr_un, sun_path) + length) && - memcmp(path, sockaddr.un.sun_path, length) == 0; - } - - return 1; -} - -_sd_export_ int sd_is_mq(int fd, const char *path) { -#if !defined(__linux__) - return 0; -#else - struct mq_attr attr; - - if (fd < 0) - return -EINVAL; - - if (mq_getattr(fd, &attr) < 0) - return -errno; - - if (path) { - char fpath[PATH_MAX]; - struct stat a, b; - - if (path[0] != '/') - return -EINVAL; - - if (fstat(fd, &a) < 0) - return -errno; - - strncpy(stpcpy(fpath, "/dev/mqueue"), path, sizeof(fpath) - 12); - fpath[sizeof(fpath)-1] = 0; - - if (stat(fpath, &b) < 0) - return -errno; - - if (a.st_dev != b.st_dev || - a.st_ino != b.st_ino) - return 0; - } - - return 1; -#endif -} - -_sd_export_ int sd_notify(int unset_environment, const char *state) { -#if defined(DISABLE_SYSTEMD) || !defined(__linux__) || !defined(SOCK_CLOEXEC) - return 0; -#else - int fd = -1, r; - struct msghdr msghdr; - struct iovec iovec; - union sockaddr_union sockaddr; - const char *e; - - if (!state) { - r = -EINVAL; - goto finish; - } - - if (!(e = getenv("NOTIFY_SOCKET"))) - return 0; - - /* Must be an abstract socket, or an absolute path */ - if ((e[0] != '@' && e[0] != '/') || e[1] == 0) { - r = -EINVAL; - goto finish; - } - - if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) { - r = -errno; - goto finish; - } - - memset(&sockaddr, 0, sizeof(sockaddr)); - sockaddr.sa.sa_family = AF_UNIX; - strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path)); - - if (sockaddr.un.sun_path[0] == '@') - sockaddr.un.sun_path[0] = 0; - - memset(&iovec, 0, sizeof(iovec)); - iovec.iov_base = (char*) state; - iovec.iov_len = strlen(state); - - memset(&msghdr, 0, sizeof(msghdr)); - msghdr.msg_name = &sockaddr; - msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e); - - if (msghdr.msg_namelen > sizeof(struct sockaddr_un)) - msghdr.msg_namelen = sizeof(struct sockaddr_un); - - msghdr.msg_iov = &iovec; - msghdr.msg_iovlen = 1; - - if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) { - r = -errno; - goto finish; - } - - r = 1; - -finish: - if (unset_environment) - unsetenv("NOTIFY_SOCKET"); - - if (fd >= 0) - close(fd); - - return r; -#endif -} - -_sd_export_ int sd_notifyf(int unset_environment, const char *format, ...) { -#if defined(DISABLE_SYSTEMD) || !defined(__linux__) - return 0; -#else - va_list ap; - char *p = NULL; - int r; - - va_start(ap, format); - r = vasprintf(&p, format, ap); - va_end(ap); - - if (r < 0 || !p) - return -ENOMEM; - - r = sd_notify(unset_environment, p); - free(p); - - return r; -#endif -} - -_sd_export_ int sd_booted(void) { -#if defined(DISABLE_SYSTEMD) || !defined(__linux__) - return 0; -#else - - struct stat a, b; - - /* We simply test whether the systemd cgroup hierarchy is - * mounted */ - - if (lstat("/sys/fs/cgroup", &a) < 0) - return 0; - - if (lstat("/sys/fs/cgroup/systemd", &b) < 0) - return 0; - - return a.st_dev != b.st_dev; -#endif -} diff --git a/src/udev/src/sd-daemon.h b/src/udev/src/sd-daemon.h deleted file mode 100644 index fe51159ee6..0000000000 --- a/src/udev/src/sd-daemon.h +++ /dev/null @@ -1,282 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#ifndef foosddaemonhfoo -#define foosddaemonhfoo - -/*** - Copyright 2010 Lennart Poettering - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation files - (the "Software"), to deal in the Software without restriction, - including without limitation the rights to use, copy, modify, merge, - publish, distribute, sublicense, and/or sell copies of the Software, - and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -***/ - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* - Reference implementation of a few systemd related interfaces for - writing daemons. These interfaces are trivial to implement. To - simplify porting we provide this reference implementation. - Applications are welcome to reimplement the algorithms described - here if they do not want to include these two source files. - - The following functionality is provided: - - - Support for logging with log levels on stderr - - File descriptor passing for socket-based activation - - Daemon startup and status notification - - Detection of systemd boots - - You may compile this with -DDISABLE_SYSTEMD to disable systemd - support. This makes all those calls NOPs that are directly related to - systemd (i.e. only sd_is_xxx() will stay useful). - - Since this is drop-in code we don't want any of our symbols to be - exported in any case. Hence we declare hidden visibility for all of - them. - - You may find an up-to-date version of these source files online: - - http://cgit.freedesktop.org/systemd/systemd/plain/src/systemd/sd-daemon.h - http://cgit.freedesktop.org/systemd/systemd/plain/src/sd-daemon.c - - This should compile on non-Linux systems, too, but with the - exception of the sd_is_xxx() calls all functions will become NOPs. - - See sd-daemon(7) for more information. -*/ - -#ifndef _sd_printf_attr_ -#if __GNUC__ >= 4 -#define _sd_printf_attr_(a,b) __attribute__ ((format (printf, a, b))) -#else -#define _sd_printf_attr_(a,b) -#endif -#endif - -/* - Log levels for usage on stderr: - - fprintf(stderr, SD_NOTICE "Hello World!\n"); - - This is similar to printk() usage in the kernel. -*/ -#define SD_EMERG "<0>" /* system is unusable */ -#define SD_ALERT "<1>" /* action must be taken immediately */ -#define SD_CRIT "<2>" /* critical conditions */ -#define SD_ERR "<3>" /* error conditions */ -#define SD_WARNING "<4>" /* warning conditions */ -#define SD_NOTICE "<5>" /* normal but significant condition */ -#define SD_INFO "<6>" /* informational */ -#define SD_DEBUG "<7>" /* debug-level messages */ - -/* The first passed file descriptor is fd 3 */ -#define SD_LISTEN_FDS_START 3 - -/* - Returns how many file descriptors have been passed, or a negative - errno code on failure. Optionally, removes the $LISTEN_FDS and - $LISTEN_PID file descriptors from the environment (recommended, but - problematic in threaded environments). If r is the return value of - this function you'll find the file descriptors passed as fds - SD_LISTEN_FDS_START to SD_LISTEN_FDS_START+r-1. Returns a negative - errno style error code on failure. This function call ensures that - the FD_CLOEXEC flag is set for the passed file descriptors, to make - sure they are not passed on to child processes. If FD_CLOEXEC shall - not be set, the caller needs to unset it after this call for all file - descriptors that are used. - - See sd_listen_fds(3) for more information. -*/ -int sd_listen_fds(int unset_environment); - -/* - Helper call for identifying a passed file descriptor. Returns 1 if - the file descriptor is a FIFO in the file system stored under the - specified path, 0 otherwise. If path is NULL a path name check will - not be done and the call only verifies if the file descriptor - refers to a FIFO. Returns a negative errno style error code on - failure. - - See sd_is_fifo(3) for more information. -*/ -int sd_is_fifo(int fd, const char *path); - -/* - Helper call for identifying a passed file descriptor. Returns 1 if - the file descriptor is a special character device on the file - system stored under the specified path, 0 otherwise. - If path is NULL a path name check will not be done and the call - only verifies if the file descriptor refers to a special character. - Returns a negative errno style error code on failure. - - See sd_is_special(3) for more information. -*/ -int sd_is_special(int fd, const char *path); - -/* - Helper call for identifying a passed file descriptor. Returns 1 if - the file descriptor is a socket of the specified family (AF_INET, - ...) and type (SOCK_DGRAM, SOCK_STREAM, ...), 0 otherwise. If - family is 0 a socket family check will not be done. If type is 0 a - socket type check will not be done and the call only verifies if - the file descriptor refers to a socket. If listening is > 0 it is - verified that the socket is in listening mode. (i.e. listen() has - been called) If listening is == 0 it is verified that the socket is - not in listening mode. If listening is < 0 no listening mode check - is done. Returns a negative errno style error code on failure. - - See sd_is_socket(3) for more information. -*/ -int sd_is_socket(int fd, int family, int type, int listening); - -/* - Helper call for identifying a passed file descriptor. Returns 1 if - the file descriptor is an Internet socket, of the specified family - (either AF_INET or AF_INET6) and the specified type (SOCK_DGRAM, - SOCK_STREAM, ...), 0 otherwise. If version is 0 a protocol version - check is not done. If type is 0 a socket type check will not be - done. If port is 0 a socket port check will not be done. The - listening flag is used the same way as in sd_is_socket(). Returns a - negative errno style error code on failure. - - See sd_is_socket_inet(3) for more information. -*/ -int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port); - -/* - Helper call for identifying a passed file descriptor. Returns 1 if - the file descriptor is an AF_UNIX socket of the specified type - (SOCK_DGRAM, SOCK_STREAM, ...) and path, 0 otherwise. If type is 0 - a socket type check will not be done. If path is NULL a socket path - check will not be done. For normal AF_UNIX sockets set length to - 0. For abstract namespace sockets set length to the length of the - socket name (including the initial 0 byte), and pass the full - socket path in path (including the initial 0 byte). The listening - flag is used the same way as in sd_is_socket(). Returns a negative - errno style error code on failure. - - See sd_is_socket_unix(3) for more information. -*/ -int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length); - -/* - Helper call for identifying a passed file descriptor. Returns 1 if - the file descriptor is a POSIX Message Queue of the specified name, - 0 otherwise. If path is NULL a message queue name check is not - done. Returns a negative errno style error code on failure. -*/ -int sd_is_mq(int fd, const char *path); - -/* - Informs systemd about changed daemon state. This takes a number of - newline separated environment-style variable assignments in a - string. The following variables are known: - - READY=1 Tells systemd that daemon startup is finished (only - relevant for services of Type=notify). The passed - argument is a boolean "1" or "0". Since there is - little value in signaling non-readiness the only - value daemons should send is "READY=1". - - STATUS=... Passes a single-line status string back to systemd - that describes the daemon state. This is free-from - and can be used for various purposes: general state - feedback, fsck-like programs could pass completion - percentages and failing programs could pass a human - readable error message. Example: "STATUS=Completed - 66% of file system check..." - - ERRNO=... If a daemon fails, the errno-style error code, - formatted as string. Example: "ERRNO=2" for ENOENT. - - BUSERROR=... If a daemon fails, the D-Bus error-style error - code. Example: "BUSERROR=org.freedesktop.DBus.Error.TimedOut" - - MAINPID=... The main pid of a daemon, in case systemd did not - fork off the process itself. Example: "MAINPID=4711" - - WATCHDOG=1 Tells systemd to update the watchdog timestamp. - Services using this feature should do this in - regular intervals. A watchdog framework can use the - timestamps to detect failed services. - - Daemons can choose to send additional variables. However, it is - recommended to prefix variable names not listed above with X_. - - Returns a negative errno-style error code on failure. Returns > 0 - if systemd could be notified, 0 if it couldn't possibly because - systemd is not running. - - Example: When a daemon finished starting up, it could issue this - call to notify systemd about it: - - sd_notify(0, "READY=1"); - - See sd_notifyf() for more complete examples. - - See sd_notify(3) for more information. -*/ -int sd_notify(int unset_environment, const char *state); - -/* - Similar to sd_notify() but takes a format string. - - Example 1: A daemon could send the following after initialization: - - sd_notifyf(0, "READY=1\n" - "STATUS=Processing requests...\n" - "MAINPID=%lu", - (unsigned long) getpid()); - - Example 2: A daemon could send the following shortly before - exiting, on failure: - - sd_notifyf(0, "STATUS=Failed to start up: %s\n" - "ERRNO=%i", - strerror(errno), - errno); - - See sd_notifyf(3) for more information. -*/ -int sd_notifyf(int unset_environment, const char *format, ...) _sd_printf_attr_(2,3); - -/* - Returns > 0 if the system was booted with systemd. Returns < 0 on - error. Returns 0 if the system was not booted with systemd. Note - that all of the functions above handle non-systemd boots just - fine. You should NOT protect them with a call to this function. Also - note that this function checks whether the system, not the user - session is controlled by systemd. However the functions above work - for both user and system services. - - See sd_booted(3) for more information. -*/ -int sd_booted(void); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/udev/src/test-libudev.c b/src/udev/src/test-libudev.c deleted file mode 100644 index 6161fb3e31..0000000000 --- a/src/udev/src/test-libudev.c +++ /dev/null @@ -1,501 +0,0 @@ -/* - * test-libudev - * - * Copyright (C) 2008 Kay Sievers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libudev.h" - -#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) - -static void log_fn(struct udev *udev, - int priority, const char *file, int line, const char *fn, - const char *format, va_list args) -{ - printf("test-libudev: %s %s:%d ", fn, file, line); - vprintf(format, args); -} - -static void print_device(struct udev_device *device) -{ - const char *str; - dev_t devnum; - int count; - struct udev_list_entry *list_entry; - - printf("*** device: %p ***\n", device); - str = udev_device_get_action(device); - if (str != NULL) - printf("action: '%s'\n", str); - - str = udev_device_get_syspath(device); - printf("syspath: '%s'\n", str); - - str = udev_device_get_sysname(device); - printf("sysname: '%s'\n", str); - - str = udev_device_get_sysnum(device); - if (str != NULL) - printf("sysnum: '%s'\n", str); - - str = udev_device_get_devpath(device); - printf("devpath: '%s'\n", str); - - str = udev_device_get_subsystem(device); - if (str != NULL) - printf("subsystem: '%s'\n", str); - - str = udev_device_get_devtype(device); - if (str != NULL) - printf("devtype: '%s'\n", str); - - str = udev_device_get_driver(device); - if (str != NULL) - printf("driver: '%s'\n", str); - - str = udev_device_get_devnode(device); - if (str != NULL) - printf("devname: '%s'\n", str); - - devnum = udev_device_get_devnum(device); - if (major(devnum) > 0) - printf("devnum: %u:%u\n", major(devnum), minor(devnum)); - - count = 0; - udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device)) { - printf("link: '%s'\n", udev_list_entry_get_name(list_entry)); - count++; - } - if (count > 0) - printf("found %i links\n", count); - - count = 0; - udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device)) { - printf("property: '%s=%s'\n", - udev_list_entry_get_name(list_entry), - udev_list_entry_get_value(list_entry)); - count++; - } - if (count > 0) - printf("found %i properties\n", count); - - str = udev_device_get_property_value(device, "MAJOR"); - if (str != NULL) - printf("MAJOR: '%s'\n", str); - - str = udev_device_get_sysattr_value(device, "dev"); - if (str != NULL) - printf("attr{dev}: '%s'\n", str); - - printf("\n"); -} - -static int test_device(struct udev *udev, const char *syspath) -{ - struct udev_device *device; - - printf("looking at device: %s\n", syspath); - device = udev_device_new_from_syspath(udev, syspath); - if (device == NULL) { - printf("no device found\n"); - return -1; - } - print_device(device); - udev_device_unref(device); - return 0; -} - -static int test_device_parents(struct udev *udev, const char *syspath) -{ - struct udev_device *device; - struct udev_device *device_parent; - - printf("looking at device: %s\n", syspath); - device = udev_device_new_from_syspath(udev, syspath); - if (device == NULL) - return -1; - - printf("looking at parents\n"); - device_parent = device; - do { - print_device(device_parent); - device_parent = udev_device_get_parent(device_parent); - } while (device_parent != NULL); - - printf("looking at parents again\n"); - device_parent = device; - do { - print_device(device_parent); - device_parent = udev_device_get_parent(device_parent); - } while (device_parent != NULL); - udev_device_unref(device); - - return 0; -} - -static int test_device_devnum(struct udev *udev) -{ - dev_t devnum = makedev(1, 3); - struct udev_device *device; - - printf("looking up device: %u:%u\n", major(devnum), minor(devnum)); - device = udev_device_new_from_devnum(udev, 'c', devnum); - if (device == NULL) - return -1; - print_device(device); - udev_device_unref(device); - return 0; -} - -static int test_device_subsys_name(struct udev *udev) -{ - struct udev_device *device; - - printf("looking up device: 'block':'sda'\n"); - device = udev_device_new_from_subsystem_sysname(udev, "block", "sda"); - if (device == NULL) - return -1; - print_device(device); - udev_device_unref(device); - - printf("looking up device: 'subsystem':'pci'\n"); - device = udev_device_new_from_subsystem_sysname(udev, "subsystem", "pci"); - if (device == NULL) - return -1; - print_device(device); - udev_device_unref(device); - - printf("looking up device: 'drivers':'scsi:sd'\n"); - device = udev_device_new_from_subsystem_sysname(udev, "drivers", "scsi:sd"); - if (device == NULL) - return -1; - print_device(device); - udev_device_unref(device); - - printf("looking up device: 'module':'printk'\n"); - device = udev_device_new_from_subsystem_sysname(udev, "module", "printk"); - if (device == NULL) - return -1; - print_device(device); - udev_device_unref(device); - return 0; -} - -static int test_enumerate_print_list(struct udev_enumerate *enumerate) -{ - struct udev_list_entry *list_entry; - int count = 0; - - udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(enumerate)) { - struct udev_device *device; - - device = udev_device_new_from_syspath(udev_enumerate_get_udev(enumerate), - udev_list_entry_get_name(list_entry)); - if (device != NULL) { - printf("device: '%s' (%s)\n", - udev_device_get_syspath(device), - udev_device_get_subsystem(device)); - udev_device_unref(device); - count++; - } - } - printf("found %i devices\n\n", count); - return count; -} - -static int test_monitor(struct udev *udev) -{ - struct udev_monitor *udev_monitor = NULL; - int fd_ep; - int fd_udev = -1; - struct epoll_event ep_udev, ep_stdin; - - fd_ep = epoll_create1(EPOLL_CLOEXEC); - if (fd_ep < 0) { - printf("error creating epoll fd: %m\n"); - goto out; - } - - udev_monitor = udev_monitor_new_from_netlink(udev, "udev"); - if (udev_monitor == NULL) { - printf("no socket\n"); - goto out; - } - fd_udev = udev_monitor_get_fd(udev_monitor); - - if (udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "block", NULL) < 0 || - udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "tty", NULL) < 0 || - udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "usb", "usb_device") < 0) { - printf("filter failed\n"); - goto out; - } - - if (udev_monitor_enable_receiving(udev_monitor) < 0) { - printf("bind failed\n"); - goto out; - } - - memset(&ep_udev, 0, sizeof(struct epoll_event)); - ep_udev.events = EPOLLIN; - ep_udev.data.fd = fd_udev; - if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_udev, &ep_udev) < 0) { - printf("fail to add fd to epoll: %m\n"); - goto out; - } - - memset(&ep_stdin, 0, sizeof(struct epoll_event)); - ep_stdin.events = EPOLLIN; - ep_stdin.data.fd = STDIN_FILENO; - if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, STDIN_FILENO, &ep_stdin) < 0) { - printf("fail to add fd to epoll: %m\n"); - goto out; - } - - for (;;) { - int fdcount; - struct epoll_event ev[4]; - struct udev_device *device; - int i; - - printf("waiting for events from udev, press ENTER to exit\n"); - fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), -1); - printf("epoll fd count: %i\n", fdcount); - - for (i = 0; i < fdcount; i++) { - if (ev[i].data.fd == fd_udev && ev[i].events & EPOLLIN) { - device = udev_monitor_receive_device(udev_monitor); - if (device == NULL) { - printf("no device from socket\n"); - continue; - } - print_device(device); - udev_device_unref(device); - } else if (ev[i].data.fd == STDIN_FILENO && ev[i].events & EPOLLIN) { - printf("exiting loop\n"); - goto out; - } - } - } -out: - if (fd_ep >= 0) - close(fd_ep); - udev_monitor_unref(udev_monitor); - return 0; -} - -static int test_queue(struct udev *udev) -{ - struct udev_queue *udev_queue; - unsigned long long int seqnum; - struct udev_list_entry *list_entry; - - udev_queue = udev_queue_new(udev); - if (udev_queue == NULL) - return -1; - seqnum = udev_queue_get_kernel_seqnum(udev_queue); - printf("seqnum kernel: %llu\n", seqnum); - seqnum = udev_queue_get_udev_seqnum(udev_queue); - printf("seqnum udev : %llu\n", seqnum); - - if (udev_queue_get_queue_is_empty(udev_queue)) - printf("queue is empty\n"); - printf("get queue list\n"); - udev_list_entry_foreach(list_entry, udev_queue_get_queued_list_entry(udev_queue)) - printf("queued: '%s' [%s]\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry)); - printf("\n"); - printf("get queue list again\n"); - udev_list_entry_foreach(list_entry, udev_queue_get_queued_list_entry(udev_queue)) - printf("queued: '%s' [%s]\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry)); - printf("\n"); - - list_entry = udev_queue_get_queued_list_entry(udev_queue); - if (list_entry != NULL) { - printf("event [%llu] is queued\n", seqnum); - seqnum = strtoull(udev_list_entry_get_value(list_entry), NULL, 10); - if (udev_queue_get_seqnum_is_finished(udev_queue, seqnum)) - printf("event [%llu] is not finished\n", seqnum); - else - printf("event [%llu] is finished\n", seqnum); - } - printf("\n"); - udev_queue_unref(udev_queue); - return 0; -} - -static int test_enumerate(struct udev *udev, const char *subsystem) -{ - struct udev_enumerate *udev_enumerate; - - printf("enumerate '%s'\n", subsystem == NULL ? "" : subsystem); - udev_enumerate = udev_enumerate_new(udev); - if (udev_enumerate == NULL) - return -1; - udev_enumerate_add_match_subsystem(udev_enumerate, subsystem); - udev_enumerate_scan_devices(udev_enumerate); - test_enumerate_print_list(udev_enumerate); - udev_enumerate_unref(udev_enumerate); - - printf("enumerate 'net' + duplicated scan + null + zero\n"); - udev_enumerate = udev_enumerate_new(udev); - if (udev_enumerate == NULL) - return -1; - udev_enumerate_add_match_subsystem(udev_enumerate, "net"); - udev_enumerate_scan_devices(udev_enumerate); - udev_enumerate_scan_devices(udev_enumerate); - udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/zero"); - udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/null"); - udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/zero"); - udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/null"); - udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/zero"); - udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/null"); - udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/null"); - udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/zero"); - udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/zero"); - udev_enumerate_scan_devices(udev_enumerate); - test_enumerate_print_list(udev_enumerate); - udev_enumerate_unref(udev_enumerate); - - printf("enumerate 'block'\n"); - udev_enumerate = udev_enumerate_new(udev); - if (udev_enumerate == NULL) - return -1; - udev_enumerate_add_match_subsystem(udev_enumerate,"block"); - udev_enumerate_add_match_is_initialized(udev_enumerate); - udev_enumerate_scan_devices(udev_enumerate); - test_enumerate_print_list(udev_enumerate); - udev_enumerate_unref(udev_enumerate); - - printf("enumerate 'not block'\n"); - udev_enumerate = udev_enumerate_new(udev); - if (udev_enumerate == NULL) - return -1; - udev_enumerate_add_nomatch_subsystem(udev_enumerate, "block"); - udev_enumerate_scan_devices(udev_enumerate); - test_enumerate_print_list(udev_enumerate); - udev_enumerate_unref(udev_enumerate); - - printf("enumerate 'pci, mem, vc'\n"); - udev_enumerate = udev_enumerate_new(udev); - if (udev_enumerate == NULL) - return -1; - udev_enumerate_add_match_subsystem(udev_enumerate, "pci"); - udev_enumerate_add_match_subsystem(udev_enumerate, "mem"); - udev_enumerate_add_match_subsystem(udev_enumerate, "vc"); - udev_enumerate_scan_devices(udev_enumerate); - test_enumerate_print_list(udev_enumerate); - udev_enumerate_unref(udev_enumerate); - - printf("enumerate 'subsystem'\n"); - udev_enumerate = udev_enumerate_new(udev); - if (udev_enumerate == NULL) - return -1; - udev_enumerate_scan_subsystems(udev_enumerate); - test_enumerate_print_list(udev_enumerate); - udev_enumerate_unref(udev_enumerate); - - printf("enumerate 'property IF_FS_*=filesystem'\n"); - udev_enumerate = udev_enumerate_new(udev); - if (udev_enumerate == NULL) - return -1; - udev_enumerate_add_match_property(udev_enumerate, "ID_FS*", "filesystem"); - udev_enumerate_scan_devices(udev_enumerate); - test_enumerate_print_list(udev_enumerate); - udev_enumerate_unref(udev_enumerate); - return 0; -} - -int main(int argc, char *argv[]) -{ - struct udev *udev = NULL; - static const struct option options[] = { - { "syspath", required_argument, NULL, 'p' }, - { "subsystem", required_argument, NULL, 's' }, - { "debug", no_argument, NULL, 'd' }, - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'V' }, - {} - }; - const char *syspath = "/devices/virtual/mem/null"; - const char *subsystem = NULL; - char path[1024]; - const char *str; - - udev = udev_new(); - printf("context: %p\n", udev); - if (udev == NULL) { - printf("no context\n"); - return 1; - } - udev_set_log_fn(udev, log_fn); - printf("set log: %p\n", log_fn); - - for (;;) { - int option; - - option = getopt_long(argc, argv, "+p:s:dhV", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'p': - syspath = optarg; - break; - case 's': - subsystem = optarg; - break; - case 'd': - if (udev_get_log_priority(udev) < LOG_INFO) - udev_set_log_priority(udev, LOG_INFO); - break; - case 'h': - printf("--debug --syspath= --subsystem= --help\n"); - goto out; - case 'V': - printf("%s\n", VERSION); - goto out; - default: - goto out; - } - } - - str = udev_get_sys_path(udev); - printf("sys_path: '%s'\n", str); - str = udev_get_dev_path(udev); - printf("dev_path: '%s'\n", str); - - /* add sys path if needed */ - if (strncmp(syspath, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) != 0) { - snprintf(path, sizeof(path), "%s%s", udev_get_sys_path(udev), syspath); - syspath = path; - } - - test_device(udev, syspath); - test_device_devnum(udev); - test_device_subsys_name(udev); - test_device_parents(udev, syspath); - - test_enumerate(udev, subsystem); - - test_queue(udev); - - test_monitor(udev); -out: - udev_unref(udev); - return 0; -} diff --git a/src/udev/src/test-udev.c b/src/udev/src/test-udev.c deleted file mode 100644 index c9712e974d..0000000000 --- a/src/udev/src/test-udev.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2003-2004 Greg Kroah-Hartman - * Copyright (C) 2004-2008 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -void udev_main_log(struct udev *udev, int priority, - const char *file, int line, const char *fn, - const char *format, va_list args) {} - -int main(int argc, char *argv[]) -{ - struct udev *udev; - struct udev_event *event = NULL; - struct udev_device *dev = NULL; - struct udev_rules *rules = NULL; - char syspath[UTIL_PATH_SIZE]; - const char *devpath; - const char *action; - sigset_t mask, sigmask_orig; - int err = -EINVAL; - - udev = udev_new(); - if (udev == NULL) - exit(1); - info(udev, "version %s\n", VERSION); - udev_selinux_init(udev); - - sigprocmask(SIG_SETMASK, NULL, &sigmask_orig); - - action = argv[1]; - if (action == NULL) { - err(udev, "action missing\n"); - goto out; - } - - devpath = argv[2]; - if (devpath == NULL) { - err(udev, "devpath missing\n"); - goto out; - } - - rules = udev_rules_new(udev, 1); - - util_strscpyl(syspath, sizeof(syspath), udev_get_sys_path(udev), devpath, NULL); - dev = udev_device_new_from_syspath(udev, syspath); - if (dev == NULL) { - info(udev, "unknown device '%s'\n", devpath); - goto out; - } - - udev_device_set_action(dev, action); - event = udev_event_new(dev); - - sigfillset(&mask); - sigprocmask(SIG_SETMASK, &mask, &sigmask_orig); - event->fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); - if (event->fd_signal < 0) { - fprintf(stderr, "error creating signalfd\n"); - goto out; - } - - /* do what devtmpfs usually provides us */ - if (udev_device_get_devnode(dev) != NULL) { - mode_t mode; - - if (strcmp(udev_device_get_subsystem(dev), "block") == 0) - mode |= S_IFBLK; - else - mode |= S_IFCHR; - - if (strcmp(action, "remove") != 0) { - util_create_path(udev, udev_device_get_devnode(dev)); - mknod(udev_device_get_devnode(dev), mode, udev_device_get_devnum(dev)); - } else { - unlink(udev_device_get_devnode(dev)); - util_delete_path(udev, udev_device_get_devnode(dev)); - } - } - - err = udev_event_execute_rules(event, rules, &sigmask_orig); - if (err == 0) - udev_event_execute_run(event, NULL); -out: - if (event != NULL && event->fd_signal >= 0) - close(event->fd_signal); - udev_event_unref(event); - udev_device_unref(dev); - udev_rules_unref(rules); - udev_selinux_exit(udev); - udev_unref(udev); - if (err != 0) - return 1; - return 0; -} diff --git a/src/udev/src/udev-builtin-blkid.c b/src/udev/src/udev-builtin-blkid.c deleted file mode 100644 index e57f03e5a1..0000000000 --- a/src/udev/src/udev-builtin-blkid.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - * probe disks for filesystems and partitions - * - * Copyright (C) 2011 Kay Sievers - * Copyright (C) 2011 Karel Zak - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -static void print_property(struct udev_device *dev, bool test, const char *name, const char *value) -{ - char s[265]; - - s[0] = '\0'; - - if (!strcmp(name, "TYPE")) { - udev_builtin_add_property(dev, test, "ID_FS_TYPE", value); - - } else if (!strcmp(name, "USAGE")) { - udev_builtin_add_property(dev, test, "ID_FS_USAGE", value); - - } else if (!strcmp(name, "VERSION")) { - udev_builtin_add_property(dev, test, "ID_FS_VERSION", value); - - } else if (!strcmp(name, "UUID")) { - blkid_safe_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_UUID", s); - blkid_encode_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_UUID_ENC", s); - - } else if (!strcmp(name, "UUID_SUB")) { - blkid_safe_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_UUID_SUB", s); - blkid_encode_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_UUID_SUB_ENC", s); - - } else if (!strcmp(name, "LABEL")) { - blkid_safe_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_LABEL", s); - blkid_encode_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_FS_LABEL_ENC", s); - - } else if (!strcmp(name, "PTTYPE")) { - udev_builtin_add_property(dev, test, "ID_PART_TABLE_TYPE", value); - - } else if (!strcmp(name, "PART_ENTRY_NAME")) { - blkid_encode_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_PART_ENTRY_NAME", s); - - } else if (!strcmp(name, "PART_ENTRY_TYPE")) { - blkid_encode_string(value, s, sizeof(s)); - udev_builtin_add_property(dev, test, "ID_PART_ENTRY_TYPE", s); - - } else if (!strncmp(name, "PART_ENTRY_", 11)) { - util_strscpyl(s, sizeof(s), "ID_", name, NULL); - udev_builtin_add_property(dev, test, s, value); - } -} - -static int probe_superblocks(blkid_probe pr) -{ - struct stat st; - int rc; - - if (fstat(blkid_probe_get_fd(pr), &st)) - return -1; - - blkid_probe_enable_partitions(pr, 1); - - if (!S_ISCHR(st.st_mode) && blkid_probe_get_size(pr) <= 1024 * 1440 && - blkid_probe_is_wholedisk(pr)) { - /* - * check if the small disk is partitioned, if yes then - * don't probe for filesystems. - */ - blkid_probe_enable_superblocks(pr, 0); - - rc = blkid_do_fullprobe(pr); - if (rc < 0) - return rc; /* -1 = error, 1 = nothing, 0 = succes */ - - if (blkid_probe_lookup_value(pr, "PTTYPE", NULL, NULL) == 0) - return 0; /* partition table detected */ - } - - blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS); - blkid_probe_enable_superblocks(pr, 1); - - return blkid_do_safeprobe(pr); -} - -static int builtin_blkid(struct udev_device *dev, int argc, char *argv[], bool test) -{ - struct udev *udev = udev_device_get_udev(dev); - int64_t offset = 0; - bool noraid = false; - int fd = -1; - blkid_probe pr; - const char *data; - const char *name; - int nvals; - int i; - size_t len; - int err = 0; - - static const struct option options[] = { - { "offset", optional_argument, NULL, 'o' }, - { "noraid", no_argument, NULL, 'R' }, - {} - }; - - for (;;) { - int option; - - option = getopt_long(argc, argv, "oR", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'o': - offset = strtoull(optarg, NULL, 0); - break; - case 'R': - noraid = true; - break; - } - } - - pr = blkid_new_probe(); - if (!pr) { - err = -ENOMEM; - return EXIT_FAILURE; - } - - blkid_probe_set_superblocks_flags(pr, - BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID | - BLKID_SUBLKS_TYPE | BLKID_SUBLKS_SECTYPE | - BLKID_SUBLKS_USAGE | BLKID_SUBLKS_VERSION); - - if (noraid) - blkid_probe_filter_superblocks_usage(pr, BLKID_FLTR_NOTIN, BLKID_USAGE_RAID); - - fd = open(udev_device_get_devnode(dev), O_RDONLY|O_CLOEXEC); - if (fd < 0) { - fprintf(stderr, "error: %s: %m\n", udev_device_get_devnode(dev)); - goto out; - } - - err = blkid_probe_set_device(pr, fd, offset, 0); - if (err < 0) - goto out; - - info(udev, "probe %s %sraid offset=%llu\n", - udev_device_get_devnode(dev), - noraid ? "no" : "", (unsigned long long) offset); - - err = probe_superblocks(pr); - if (err < 0) - goto out; - - nvals = blkid_probe_numof_values(pr); - for (i = 0; i < nvals; i++) { - if (blkid_probe_get_value(pr, i, &name, &data, &len)) - continue; - len = strnlen((char *) data, len); - print_property(dev, test, name, (char *) data); - } - - blkid_free_probe(pr); -out: - if (fd > 0) - close(fd); - if (err < 0) - return EXIT_FAILURE; - return EXIT_SUCCESS; -} - -const struct udev_builtin udev_builtin_blkid = { - .name = "blkid", - .cmd = builtin_blkid, - .help = "filesystem and partition probing", - .run_once = true, -}; diff --git a/src/udev/src/udev-builtin-firmware.c b/src/udev/src/udev-builtin-firmware.c deleted file mode 100644 index d212c64b4d..0000000000 --- a/src/udev/src/udev-builtin-firmware.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * firmware - Kernel firmware loader - * - * Copyright (C) 2009 Piter Punk - * Copyright (C) 2009-2011 Kay Sievers - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details:* - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -static bool set_loading(struct udev *udev, char *loadpath, const char *state) -{ - FILE *ldfile; - - ldfile = fopen(loadpath, "we"); - if (ldfile == NULL) { - err(udev, "error: can not open '%s'\n", loadpath); - return false; - }; - fprintf(ldfile, "%s\n", state); - fclose(ldfile); - return true; -} - -static bool copy_firmware(struct udev *udev, const char *source, const char *target, size_t size) -{ - char *buf; - FILE *fsource = NULL, *ftarget = NULL; - bool ret = false; - - buf = malloc(size); - if (buf == NULL) { - err(udev,"No memory available to load firmware file"); - return false; - } - - info(udev, "writing '%s' (%zi) to '%s'\n", source, size, target); - - fsource = fopen(source, "re"); - if (fsource == NULL) - goto exit; - ftarget = fopen(target, "we"); - if (ftarget == NULL) - goto exit; - if (fread(buf, size, 1, fsource) != 1) - goto exit; - if (fwrite(buf, size, 1, ftarget) == 1) - ret = true; -exit: - if (ftarget != NULL) - fclose(ftarget); - if (fsource != NULL) - fclose(fsource); - free(buf); - return ret; -} - -static int builtin_firmware(struct udev_device *dev, int argc, char *argv[], bool test) -{ - struct udev *udev = udev_device_get_udev(dev); - static const char *searchpath[] = { FIRMWARE_PATH }; - char fwencpath[UTIL_PATH_SIZE]; - char misspath[UTIL_PATH_SIZE]; - char loadpath[UTIL_PATH_SIZE]; - char datapath[UTIL_PATH_SIZE]; - char fwpath[UTIL_PATH_SIZE]; - const char *firmware; - FILE *fwfile; - struct utsname kernel; - struct stat statbuf; - unsigned int i; - int rc = EXIT_SUCCESS; - - firmware = udev_device_get_property_value(dev, "FIRMWARE"); - if (firmware == NULL) { - err(udev, "firmware parameter missing\n\n"); - rc = EXIT_FAILURE; - goto exit; - } - - /* lookup firmware file */ - uname(&kernel); - for (i = 0; i < ARRAY_SIZE(searchpath); i++) { - util_strscpyl(fwpath, sizeof(fwpath), searchpath[i], kernel.release, "/", firmware, NULL); - dbg(udev, "trying %s\n", fwpath); - fwfile = fopen(fwpath, "re"); - if (fwfile != NULL) - break; - - util_strscpyl(fwpath, sizeof(fwpath), searchpath[i], firmware, NULL); - dbg(udev, "trying %s\n", fwpath); - fwfile = fopen(fwpath, "re"); - if (fwfile != NULL) - break; - } - - util_path_encode(firmware, fwencpath, sizeof(fwencpath)); - util_strscpyl(misspath, sizeof(misspath), udev_get_run_path(udev), "/firmware-missing/", fwencpath, NULL); - util_strscpyl(loadpath, sizeof(loadpath), udev_device_get_syspath(dev), "/loading", NULL); - - if (fwfile == NULL) { - int err; - - /* This link indicates the missing firmware file and the associated device */ - info(udev, "did not find firmware file '%s'\n", firmware); - do { - err = util_create_path(udev, misspath); - if (err != 0 && err != -ENOENT) - break; - err = symlink(udev_device_get_devpath(dev), misspath); - if (err != 0) - err = -errno; - } while (err == -ENOENT); - rc = EXIT_FAILURE; - set_loading(udev, loadpath, "-1"); - goto exit; - } - - if (stat(fwpath, &statbuf) < 0 || statbuf.st_size == 0) { - rc = EXIT_FAILURE; - goto exit; - } - if (unlink(misspath) == 0) - util_delete_path(udev, misspath); - - if (!set_loading(udev, loadpath, "1")) - goto exit; - - util_strscpyl(datapath, sizeof(datapath), udev_device_get_syspath(dev), "/data", NULL); - if (!copy_firmware(udev, fwpath, datapath, statbuf.st_size)) { - err(udev, "error sending firmware '%s' to device\n", firmware); - set_loading(udev, loadpath, "-1"); - rc = EXIT_FAILURE; - goto exit; - }; - - set_loading(udev, loadpath, "0"); -exit: - if (fwfile) - fclose(fwfile); - return rc; -} - -const struct udev_builtin udev_builtin_firmware = { - .name = "firmware", - .cmd = builtin_firmware, - .help = "kernel firmware loader", - .run_once = true, -}; diff --git a/src/udev/src/udev-builtin-hwdb.c b/src/udev/src/udev-builtin-hwdb.c deleted file mode 100644 index aa996f375d..0000000000 --- a/src/udev/src/udev-builtin-hwdb.c +++ /dev/null @@ -1,247 +0,0 @@ -/* - * usb-db, pci-db - lookup vendor/product database - * - * Copyright (C) 2009 Lennart Poettering - * Copyright (C) 2011 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -static int get_id_attr( - struct udev_device *parent, - const char *name, - uint16_t *value) { - - const char *t; - unsigned u; - - if (!(t = udev_device_get_sysattr_value(parent, name))) { - fprintf(stderr, "%s lacks %s.\n", udev_device_get_syspath(parent), name); - return -1; - } - - if (!strncmp(t, "0x", 2)) - t += 2; - - if (sscanf(t, "%04x", &u) != 1 || u > 0xFFFFU) { - fprintf(stderr, "Failed to parse %s on %s.\n", name, udev_device_get_syspath(parent)); - return -1; - } - - *value = (uint16_t) u; - return 0; -} - -static int get_vid_pid( - struct udev_device *parent, - const char *vendor_attr, - const char *product_attr, - uint16_t *vid, - uint16_t *pid) { - - if (get_id_attr(parent, vendor_attr, vid) < 0) - return -1; - else if (*vid <= 0) { - fprintf(stderr, "Invalid vendor id.\n"); - return -1; - } - - if (get_id_attr(parent, product_attr, pid) < 0) - return -1; - - return 0; -} - -static void rstrip(char *n) { - size_t i; - - for (i = strlen(n); i > 0 && isspace(n[i-1]); i--) - n[i-1] = 0; -} - -#define HEXCHARS "0123456789abcdefABCDEF" -#define WHITESPACE " \t\n\r" -static int lookup_vid_pid(const char *database, - uint16_t vid, uint16_t pid, - char **vendor, char **product) -{ - - FILE *f; - int ret = -1; - int found_vendor = 0; - char *line = NULL; - - *vendor = *product = NULL; - - if (!(f = fopen(database, "rme"))) { - fprintf(stderr, "Failed to open database file '%s': %s\n", database, strerror(errno)); - return -1; - } - - for (;;) { - size_t n; - - if (getline(&line, &n, f) < 0) - break; - - rstrip(line); - - if (line[0] == '#' || line[0] == 0) - continue; - - if (strspn(line, HEXCHARS) == 4) { - unsigned u; - - if (found_vendor) - break; - - if (sscanf(line, "%04x", &u) == 1 && u == vid) { - char *t; - - t = line+4; - t += strspn(t, WHITESPACE); - - if (!(*vendor = strdup(t))) { - fprintf(stderr, "Out of memory.\n"); - goto finish; - } - - found_vendor = 1; - } - - continue; - } - - if (found_vendor && line[0] == '\t' && strspn(line+1, HEXCHARS) == 4) { - unsigned u; - - if (sscanf(line+1, "%04x", &u) == 1 && u == pid) { - char *t; - - t = line+5; - t += strspn(t, WHITESPACE); - - if (!(*product = strdup(t))) { - fprintf(stderr, "Out of memory.\n"); - goto finish; - } - - break; - } - } - } - - ret = 0; - -finish: - free(line); - fclose(f); - - if (ret < 0) { - free(*product); - free(*vendor); - - *product = *vendor = NULL; - } - - return ret; -} - -static struct udev_device *find_device(struct udev_device *dev, const char *subsys, const char *devtype) -{ - const char *str; - - str = udev_device_get_subsystem(dev); - if (str == NULL) - goto try_parent; - if (strcmp(str, subsys) != 0) - goto try_parent; - - if (devtype != NULL) { - str = udev_device_get_devtype(dev); - if (str == NULL) - goto try_parent; - if (strcmp(str, devtype) != 0) - goto try_parent; - } - return dev; -try_parent: - return udev_device_get_parent_with_subsystem_devtype(dev, subsys, devtype); -} - - -static int builtin_db(struct udev_device *dev, bool test, - const char *database, - const char *vendor_attr, const char *product_attr, - const char *subsys, const char *devtype) -{ - struct udev_device *parent; - uint16_t vid = 0, pid = 0; - char *vendor = NULL, *product = NULL; - - parent = find_device(dev, subsys, devtype); - if (!parent) { - fprintf(stderr, "Failed to find device.\n"); - goto finish; - } - - if (get_vid_pid(parent, vendor_attr, product_attr, &vid, &pid) < 0) - goto finish; - - if (lookup_vid_pid(database, vid, pid, &vendor, &product) < 0) - goto finish; - - if (vendor) - udev_builtin_add_property(dev, test, "ID_VENDOR_FROM_DATABASE", vendor); - if (product) - udev_builtin_add_property(dev, test, "ID_MODEL_FROM_DATABASE", product); - -finish: - free(vendor); - free(product); - return 0; -} - -static int builtin_usb_db(struct udev_device *dev, int argc, char *argv[], bool test) -{ - return builtin_db(dev, test, USB_DATABASE, "idVendor", "idProduct", "usb", "usb_device"); -} - -static int builtin_pci_db(struct udev_device *dev, int argc, char *argv[], bool test) -{ - return builtin_db(dev, test, PCI_DATABASE, "vendor", "device", "pci", NULL); -} - -const struct udev_builtin udev_builtin_usb_db = { - .name = "usb-db", - .cmd = builtin_usb_db, - .help = "USB vendor/product database", - .run_once = true, -}; - -const struct udev_builtin udev_builtin_pci_db = { - .name = "pci-db", - .cmd = builtin_pci_db, - .help = "PCI vendor/product database", - .run_once = true, -}; diff --git a/src/udev/src/udev-builtin-input_id.c b/src/udev/src/udev-builtin-input_id.c deleted file mode 100644 index a062ef7c7a..0000000000 --- a/src/udev/src/udev-builtin-input_id.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - * compose persistent device path - * - * Copyright (C) 2009 Martin Pitt - * Portions Copyright (C) 2004 David Zeuthen, - * Copyright (C) 2011 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -/* we must use this kernel-compatible implementation */ -#define BITS_PER_LONG (sizeof(unsigned long) * 8) -#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) -#define OFF(x) ((x)%BITS_PER_LONG) -#define BIT(x) (1UL<> OFF(bit)) & 1) - -/* - * Read a capability attribute and return bitmask. - * @param dev udev_device - * @param attr sysfs attribute name (e. g. "capabilities/key") - * @param bitmask: Output array which has a sizeof of bitmask_size - */ -static void get_cap_mask(struct udev_device *dev, - struct udev_device *pdev, const char* attr, - unsigned long *bitmask, size_t bitmask_size, - bool test) -{ - struct udev *udev = udev_device_get_udev(dev); - char text[4096]; - unsigned i; - char* word; - unsigned long val; - - snprintf(text, sizeof(text), "%s", udev_device_get_sysattr_value(pdev, attr)); - info(udev, "%s raw kernel attribute: %s\n", attr, text); - - memset (bitmask, 0, bitmask_size); - i = 0; - while ((word = strrchr(text, ' ')) != NULL) { - val = strtoul (word+1, NULL, 16); - if (i < bitmask_size/sizeof(unsigned long)) - bitmask[i] = val; - else - info(udev, "ignoring %s block %lX which is larger than maximum size\n", attr, val); - *word = '\0'; - ++i; - } - val = strtoul (text, NULL, 16); - if (i < bitmask_size / sizeof(unsigned long)) - bitmask[i] = val; - else - info(udev, "ignoring %s block %lX which is larger than maximum size\n", attr, val); - - if (test) { - /* printf pattern with the right unsigned long number of hex chars */ - snprintf(text, sizeof(text), " bit %%4u: %%0%zilX\n", 2 * sizeof(unsigned long)); - info(udev, "%s decoded bit map:\n", attr); - val = bitmask_size / sizeof (unsigned long); - /* skip over leading zeros */ - while (bitmask[val-1] == 0 && val > 0) - --val; - for (i = 0; i < val; ++i) - info(udev, text, i * BITS_PER_LONG, bitmask[i]); - } -} - -/* pointer devices */ -static void test_pointers (struct udev_device *dev, - const unsigned long* bitmask_ev, - const unsigned long* bitmask_abs, - const unsigned long* bitmask_key, - const unsigned long* bitmask_rel, - bool test) -{ - int is_mouse = 0; - int is_touchpad = 0; - - if (!test_bit (EV_KEY, bitmask_ev)) { - if (test_bit (EV_ABS, bitmask_ev) && - test_bit (ABS_X, bitmask_abs) && - test_bit (ABS_Y, bitmask_abs) && - test_bit (ABS_Z, bitmask_abs)) - udev_builtin_add_property(dev, test, "ID_INPUT_ACCELEROMETER", "1"); - return; - } - - if (test_bit (EV_ABS, bitmask_ev) && - test_bit (ABS_X, bitmask_abs) && test_bit (ABS_Y, bitmask_abs)) { - if (test_bit (BTN_STYLUS, bitmask_key) || test_bit (BTN_TOOL_PEN, bitmask_key)) - udev_builtin_add_property(dev, test, "ID_INPUT_TABLET", "1"); - else if (test_bit (BTN_TOOL_FINGER, bitmask_key) && !test_bit (BTN_TOOL_PEN, bitmask_key)) - is_touchpad = 1; - else if (test_bit (BTN_TRIGGER, bitmask_key) || - test_bit (BTN_A, bitmask_key) || - test_bit (BTN_1, bitmask_key)) - udev_builtin_add_property(dev, test, "ID_INPUT_JOYSTICK", "1"); - else if (test_bit (BTN_MOUSE, bitmask_key)) - /* This path is taken by VMware's USB mouse, which has - * absolute axes, but no touch/pressure button. */ - is_mouse = 1; - else if (test_bit (BTN_TOUCH, bitmask_key)) - udev_builtin_add_property(dev, test, "ID_INPUT_TOUCHSCREEN", "1"); - } - - if (test_bit (EV_REL, bitmask_ev) && - test_bit (REL_X, bitmask_rel) && test_bit (REL_Y, bitmask_rel) && - test_bit (BTN_MOUSE, bitmask_key)) - is_mouse = 1; - - if (is_mouse) - udev_builtin_add_property(dev, test, "ID_INPUT_MOUSE", "1"); - if (is_touchpad) - udev_builtin_add_property(dev, test, "ID_INPUT_TOUCHPAD", "1"); -} - -/* key like devices */ -static void test_key (struct udev_device *dev, - const unsigned long* bitmask_ev, - const unsigned long* bitmask_key, - bool test) -{ - struct udev *udev = udev_device_get_udev(dev); - unsigned i; - unsigned long found; - unsigned long mask; - - /* do we have any KEY_* capability? */ - if (!test_bit (EV_KEY, bitmask_ev)) { - info(udev, "test_key: no EV_KEY capability\n"); - return; - } - - /* only consider KEY_* here, not BTN_* */ - found = 0; - for (i = 0; i < BTN_MISC/BITS_PER_LONG; ++i) { - found |= bitmask_key[i]; - info(udev, "test_key: checking bit block %lu for any keys; found=%i\n", i*BITS_PER_LONG, found > 0); - } - /* If there are no keys in the lower block, check the higher block */ - if (!found) { - for (i = KEY_OK; i < BTN_TRIGGER_HAPPY; ++i) { - if (test_bit (i, bitmask_key)) { - info(udev, "test_key: Found key %x in high block\n", i); - found = 1; - break; - } - } - } - - if (found > 0) - udev_builtin_add_property(dev, test, "ID_INPUT_KEY", "1"); - - /* the first 32 bits are ESC, numbers, and Q to D; if we have all of - * those, consider it a full keyboard; do not test KEY_RESERVED, though */ - mask = 0xFFFFFFFE; - if ((bitmask_key[0] & mask) == mask) - udev_builtin_add_property(dev, test, "ID_INPUT_KEYBOARD", "1"); -} - -static int builtin_input_id(struct udev_device *dev, int argc, char *argv[], bool test) -{ - struct udev_device *pdev; - unsigned long bitmask_ev[NBITS(EV_MAX)]; - unsigned long bitmask_abs[NBITS(ABS_MAX)]; - unsigned long bitmask_key[NBITS(KEY_MAX)]; - unsigned long bitmask_rel[NBITS(REL_MAX)]; - - /* walk up the parental chain until we find the real input device; the - * argument is very likely a subdevice of this, like eventN */ - pdev = dev; - while (pdev != NULL && udev_device_get_sysattr_value(pdev, "capabilities/ev") == NULL) - pdev = udev_device_get_parent_with_subsystem_devtype(pdev, "input", NULL); - - /* not an "input" class device */ - if (pdev == NULL) - return EXIT_SUCCESS; - - /* Use this as a flag that input devices were detected, so that this - * program doesn't need to be called more than once per device */ - udev_builtin_add_property(dev, test, "ID_INPUT", "1"); - get_cap_mask(dev, pdev, "capabilities/ev", bitmask_ev, sizeof(bitmask_ev), test); - get_cap_mask(dev, pdev, "capabilities/abs", bitmask_abs, sizeof(bitmask_abs), test); - get_cap_mask(dev, pdev, "capabilities/rel", bitmask_rel, sizeof(bitmask_rel), test); - get_cap_mask(dev, pdev, "capabilities/key", bitmask_key, sizeof(bitmask_key), test); - test_pointers(dev, bitmask_ev, bitmask_abs, bitmask_key, bitmask_rel, test); - test_key(dev, bitmask_ev, bitmask_key, test); - return EXIT_SUCCESS; -} - -const struct udev_builtin udev_builtin_input_id = { - .name = "input_id", - .cmd = builtin_input_id, - .help = "input device properties", -}; diff --git a/src/udev/src/udev-builtin-kmod.c b/src/udev/src/udev-builtin-kmod.c deleted file mode 100644 index 57e813f863..0000000000 --- a/src/udev/src/udev-builtin-kmod.c +++ /dev/null @@ -1,142 +0,0 @@ -/* - * load kernel modules - * - * Copyright (C) 2011 Kay Sievers - * Copyright (C) 2011 ProFUSION embedded systems - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -static struct kmod_ctx *ctx; - -static int load_module(struct udev *udev, const char *alias) -{ - struct kmod_list *list = NULL; - struct kmod_list *l; - int err; - - err = kmod_module_new_from_lookup(ctx, alias, &list); - if (err < 0) - return err; - - if (list == NULL) - info(udev, "no module matches '%s'\n", alias); - - kmod_list_foreach(l, list) { - struct kmod_module *mod = kmod_module_get_module(l); - - err = kmod_module_probe_insert_module(mod, KMOD_PROBE_APPLY_BLACKLIST, NULL, NULL, NULL, NULL); - if (err == KMOD_PROBE_APPLY_BLACKLIST) - info(udev, "module '%s' is blacklisted\n", kmod_module_get_name(mod)); - else if (err == 0) - info(udev, "inserted '%s'\n", kmod_module_get_name(mod)); - else - info(udev, "failed to insert '%s'\n", kmod_module_get_name(mod)); - - kmod_module_unref(mod); - } - - kmod_module_unref_list(list); - return err; -} - -static void udev_kmod_log(void *data, int priority, const char *file, int line, - const char *fn, const char *format, va_list args) -{ - udev_main_log(data, priority, file, line, fn, format, args); -} - -/* needs to re-instantiate the context after a reload */ -static int builtin_kmod(struct udev_device *dev, int argc, char *argv[], bool test) -{ - struct udev *udev = udev_device_get_udev(dev); - int i; - - if (!ctx) { - ctx = kmod_new(NULL, NULL); - if (!ctx) - return -ENOMEM; - - info(udev, "load module index\n"); - kmod_set_log_fn(ctx, udev_kmod_log, udev); - kmod_load_resources(ctx); - } - - if (argc < 3 || strcmp(argv[1], "load")) { - err(udev, "expect: %s load \n", argv[0]); - return EXIT_FAILURE; - } - - for (i = 2; argv[i]; i++) { - info(udev, "execute '%s' '%s'\n", argv[1], argv[i]); - load_module(udev, argv[i]); - } - - return EXIT_SUCCESS; -} - -/* called at udev startup */ -static int builtin_kmod_init(struct udev *udev) -{ - if (ctx) - return 0; - - ctx = kmod_new(NULL, NULL); - if (!ctx) - return -ENOMEM; - - info(udev, "load module index\n"); - kmod_set_log_fn(ctx, udev_kmod_log, udev); - kmod_load_resources(ctx); - return 0; -} - -/* called on udev shutdown and reload request */ -static void builtin_kmod_exit(struct udev *udev) -{ - info(udev, "unload module index\n"); - ctx = kmod_unref(ctx); -} - -/* called every couple of seconds during event activity; 'true' if config has changed */ -static bool builtin_kmod_validate(struct udev *udev) -{ - info(udev, "validate module index\n"); - if (kmod_validate_resources(ctx) != KMOD_RESOURCES_OK) - return true; - return false; -} - -const struct udev_builtin udev_builtin_kmod = { - .name = "kmod", - .cmd = builtin_kmod, - .init = builtin_kmod_init, - .exit = builtin_kmod_exit, - .validate = builtin_kmod_validate, - .help = "kernel module loader", - .run_once = false, -}; diff --git a/src/udev/src/udev-builtin-path_id.c b/src/udev/src/udev-builtin-path_id.c deleted file mode 100644 index a8559d2dd4..0000000000 --- a/src/udev/src/udev-builtin-path_id.c +++ /dev/null @@ -1,498 +0,0 @@ -/* - * compose persistent device path - * - * Copyright (C) 2009-2011 Kay Sievers - * - * Logic based on Hannes Reinecke's shell script. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -static int path_prepend(char **path, const char *fmt, ...) -{ - va_list va; - char *pre; - int err = 0; - - va_start(va, fmt); - err = vasprintf(&pre, fmt, va); - va_end(va); - if (err < 0) - goto out; - - if (*path != NULL) { - char *new; - - err = asprintf(&new, "%s-%s", pre, *path); - free(pre); - if (err < 0) - goto out; - free(*path); - *path = new; - } else { - *path = pre; - } -out: - return err; -} - -/* -** Linux only supports 32 bit luns. -** See drivers/scsi/scsi_scan.c::scsilun_to_int() for more details. -*/ -static int format_lun_number(struct udev_device *dev, char **path) -{ - unsigned long lun = strtoul(udev_device_get_sysnum(dev), NULL, 10); - - /* address method 0, peripheral device addressing with bus id of zero */ - if (lun < 256) - return path_prepend(path, "lun-%d", lun); - /* handle all other lun addressing methods by using a variant of the original lun format */ - return path_prepend(path, "lun-0x%04x%04x00000000", (lun & 0xffff), (lun >> 16) & 0xffff); -} - -static struct udev_device *skip_subsystem(struct udev_device *dev, const char *subsys) -{ - struct udev_device *parent = dev; - - while (parent != NULL) { - const char *subsystem; - - subsystem = udev_device_get_subsystem(parent); - if (subsystem == NULL || strcmp(subsystem, subsys) != 0) - break; - dev = parent; - parent = udev_device_get_parent(parent); - } - return dev; -} - -static struct udev_device *handle_scsi_fibre_channel(struct udev_device *parent, char **path) -{ - struct udev *udev = udev_device_get_udev(parent); - struct udev_device *targetdev; - struct udev_device *fcdev = NULL; - const char *port; - char *lun = NULL;; - - targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target"); - if (targetdev == NULL) - return NULL; - - fcdev = udev_device_new_from_subsystem_sysname(udev, "fc_transport", udev_device_get_sysname(targetdev)); - if (fcdev == NULL) - return NULL; - port = udev_device_get_sysattr_value(fcdev, "port_name"); - if (port == NULL) { - parent = NULL; - goto out; - } - - format_lun_number(parent, &lun); - path_prepend(path, "fc-%s-%s", port, lun); - if (lun) - free(lun); -out: - udev_device_unref(fcdev); - return parent; -} - -static struct udev_device *handle_scsi_sas(struct udev_device *parent, char **path) -{ - struct udev *udev = udev_device_get_udev(parent); - struct udev_device *targetdev; - struct udev_device *target_parent; - struct udev_device *sasdev; - const char *sas_address; - char *lun = NULL; - - targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target"); - if (targetdev == NULL) - return NULL; - - target_parent = udev_device_get_parent(targetdev); - if (target_parent == NULL) - return NULL; - - sasdev = udev_device_new_from_subsystem_sysname(udev, "sas_device", - udev_device_get_sysname(target_parent)); - if (sasdev == NULL) - return NULL; - - sas_address = udev_device_get_sysattr_value(sasdev, "sas_address"); - if (sas_address == NULL) { - parent = NULL; - goto out; - } - - format_lun_number(parent, &lun); - path_prepend(path, "sas-%s-%s", sas_address, lun); - if (lun) - free(lun); -out: - udev_device_unref(sasdev); - return parent; -} - -static struct udev_device *handle_scsi_iscsi(struct udev_device *parent, char **path) -{ - struct udev *udev = udev_device_get_udev(parent); - struct udev_device *transportdev; - struct udev_device *sessiondev = NULL; - const char *target; - char *connname; - struct udev_device *conndev = NULL; - const char *addr; - const char *port; - char *lun = NULL; - - /* find iscsi session */ - transportdev = parent; - for (;;) { - transportdev = udev_device_get_parent(transportdev); - if (transportdev == NULL) - return NULL; - if (strncmp(udev_device_get_sysname(transportdev), "session", 7) == 0) - break; - } - - /* find iscsi session device */ - sessiondev = udev_device_new_from_subsystem_sysname(udev, "iscsi_session", udev_device_get_sysname(transportdev)); - if (sessiondev == NULL) - return NULL; - target = udev_device_get_sysattr_value(sessiondev, "targetname"); - if (target == NULL) { - parent = NULL; - goto out; - } - - if (asprintf(&connname, "connection%s:0", udev_device_get_sysnum(transportdev)) < 0) { - parent = NULL; - goto out; - } - conndev = udev_device_new_from_subsystem_sysname(udev, "iscsi_connection", connname); - free(connname); - if (conndev == NULL) { - parent = NULL; - goto out; - } - addr = udev_device_get_sysattr_value(conndev, "persistent_address"); - port = udev_device_get_sysattr_value(conndev, "persistent_port"); - if (addr == NULL || port == NULL) { - parent = NULL; - goto out; - } - - format_lun_number(parent, &lun); - path_prepend(path, "ip-%s:%s-iscsi-%s-%s", addr, port, target, lun); - if (lun) - free(lun); -out: - udev_device_unref(sessiondev); - udev_device_unref(conndev); - return parent; -} - -static struct udev_device *handle_scsi_default(struct udev_device *parent, char **path) -{ - struct udev_device *hostdev; - int host, bus, target, lun; - const char *name; - char *base; - char *pos; - DIR *dir; - struct dirent *dent; - int basenum; - - hostdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host"); - if (hostdev == NULL) - return NULL; - - name = udev_device_get_sysname(parent); - if (sscanf(name, "%d:%d:%d:%d", &host, &bus, &target, &lun) != 4) - return NULL; - - /* rebase host offset to get the local relative number */ - basenum = -1; - base = strdup(udev_device_get_syspath(hostdev)); - if (base == NULL) - return NULL; - pos = strrchr(base, '/'); - if (pos == NULL) { - parent = NULL; - goto out; - } - pos[0] = '\0'; - dir = opendir(base); - if (dir == NULL) { - parent = NULL; - goto out; - } - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - char *rest; - int i; - - if (dent->d_name[0] == '.') - continue; - if (dent->d_type != DT_DIR && dent->d_type != DT_LNK) - continue; - if (strncmp(dent->d_name, "host", 4) != 0) - continue; - i = strtoul(&dent->d_name[4], &rest, 10); - if (rest[0] != '\0') - continue; - /* - * find the smallest number; the host really needs to export its - * own instance number per parent device; relying on the global host - * enumeration and plainly rebasing the numbers sounds unreliable - */ - if (basenum == -1 || i < basenum) - basenum = i; - } - closedir(dir); - if (basenum == -1) { - parent = NULL; - goto out; - } - host -= basenum; - - path_prepend(path, "scsi-%u:%u:%u:%u", host, bus, target, lun); -out: - free(base); - return hostdev; -} - -static struct udev_device *handle_scsi(struct udev_device *parent, char **path) -{ - const char *devtype; - const char *name; - const char *id; - - devtype = udev_device_get_devtype(parent); - if (devtype == NULL || strcmp(devtype, "scsi_device") != 0) - return parent; - - /* firewire */ - id = udev_device_get_sysattr_value(parent, "ieee1394_id"); - if (id != NULL) { - parent = skip_subsystem(parent, "scsi"); - path_prepend(path, "ieee1394-0x%s", id); - goto out; - } - - /* lousy scsi sysfs does not have a "subsystem" for the transport */ - name = udev_device_get_syspath(parent); - - if (strstr(name, "/rport-") != NULL) { - parent = handle_scsi_fibre_channel(parent, path); - goto out; - } - - if (strstr(name, "/end_device-") != NULL) { - parent = handle_scsi_sas(parent, path); - goto out; - } - - if (strstr(name, "/session") != NULL) { - parent = handle_scsi_iscsi(parent, path); - goto out; - } - - /* - * We do not support the ATA transport class, it creates duplicated link - * names as the fake SCSI host adapters are all separated, they are all - * re-based as host == 0. ATA should just stop faking two duplicated - * hierarchies for a single topology and leave the SCSI stuff alone; - * until that happens, there are no by-path/ links for ATA devices behind - * an ATA transport class. - */ - if (strstr(name, "/ata") != NULL) { - parent = NULL; - goto out; - } - - parent = handle_scsi_default(parent, path); -out: - return parent; -} - -static void handle_scsi_tape(struct udev_device *dev, char **path) -{ - const char *name; - - /* must be the last device in the syspath */ - if (*path != NULL) - return; - - name = udev_device_get_sysname(dev); - if (strncmp(name, "nst", 3) == 0 && strchr("lma", name[3]) != NULL) - path_prepend(path, "nst%c", name[3]); - else if (strncmp(name, "st", 2) == 0 && strchr("lma", name[2]) != NULL) - path_prepend(path, "st%c", name[2]); -} - -static struct udev_device *handle_usb(struct udev_device *parent, char **path) -{ - const char *devtype; - const char *str; - const char *port; - - devtype = udev_device_get_devtype(parent); - if (devtype == NULL) - return parent; - if (strcmp(devtype, "usb_interface") != 0 && strcmp(devtype, "usb_device") != 0) - return parent; - - str = udev_device_get_sysname(parent); - port = strchr(str, '-'); - if (port == NULL) - return parent; - port++; - - parent = skip_subsystem(parent, "usb"); - path_prepend(path, "usb-0:%s", port); - return parent; -} - -static struct udev_device *handle_ccw(struct udev_device *parent, struct udev_device *dev, char **path) -{ - struct udev_device *scsi_dev; - - scsi_dev = udev_device_get_parent_with_subsystem_devtype(dev, "scsi", "scsi_device"); - if (scsi_dev != NULL) { - const char *wwpn; - const char *lun; - const char *hba_id; - - hba_id = udev_device_get_sysattr_value(scsi_dev, "hba_id"); - wwpn = udev_device_get_sysattr_value(scsi_dev, "wwpn"); - lun = udev_device_get_sysattr_value(scsi_dev, "fcp_lun"); - if (hba_id != NULL && lun != NULL && wwpn != NULL) { - path_prepend(path, "ccw-%s-zfcp-%s:%s", hba_id, wwpn, lun); - goto out; - } - } - - path_prepend(path, "ccw-%s", udev_device_get_sysname(parent)); -out: - parent = skip_subsystem(parent, "ccw"); - return parent; -} - -static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool test) -{ - struct udev_device *parent; - char *path = NULL; - - /* S390 ccw bus */ - parent = udev_device_get_parent_with_subsystem_devtype(dev, "ccw", NULL); - if (parent != NULL) { - handle_ccw(parent, dev, &path); - goto out; - } - - /* walk up the chain of devices and compose path */ - parent = dev; - while (parent != NULL) { - const char *subsys; - - subsys = udev_device_get_subsystem(parent); - if (subsys == NULL) { - ; - } else if (strcmp(subsys, "scsi_tape") == 0) { - handle_scsi_tape(parent, &path); - } else if (strcmp(subsys, "scsi") == 0) { - parent = handle_scsi(parent, &path); - } else if (strcmp(subsys, "usb") == 0) { - parent = handle_usb(parent, &path); - } else if (strcmp(subsys, "serio") == 0) { - path_prepend(&path, "serio-%s", udev_device_get_sysnum(parent)); - parent = skip_subsystem(parent, "serio"); - } else if (strcmp(subsys, "pci") == 0) { - path_prepend(&path, "pci-%s", udev_device_get_sysname(parent)); - parent = skip_subsystem(parent, "pci"); - } else if (strcmp(subsys, "platform") == 0) { - path_prepend(&path, "platform-%s", udev_device_get_sysname(parent)); - parent = skip_subsystem(parent, "platform"); - } else if (strcmp(subsys, "acpi") == 0) { - path_prepend(&path, "acpi-%s", udev_device_get_sysname(parent)); - parent = skip_subsystem(parent, "acpi"); - } else if (strcmp(subsys, "xen") == 0) { - path_prepend(&path, "xen-%s", udev_device_get_sysname(parent)); - parent = skip_subsystem(parent, "xen"); - } else if (strcmp(subsys, "virtio") == 0) { - path_prepend(&path, "virtio-pci-%s", udev_device_get_sysname(parent)); - parent = skip_subsystem(parent, "virtio"); - } - - parent = udev_device_get_parent(parent); - } -out: - if (path != NULL) { - char tag[UTIL_NAME_SIZE]; - size_t i; - const char *p; - - /* compose valid udev tag name */ - for (p = path, i = 0; *p; p++) { - if ((*p >= '0' && *p <= '9') || - (*p >= 'A' && *p <= 'Z') || - (*p >= 'a' && *p <= 'z') || - *p == '-') { - tag[i++] = *p; - continue; - } - - /* skip all leading '_' */ - if (i == 0) - continue; - - /* avoid second '_' */ - if (tag[i-1] == '_') - continue; - - tag[i++] = '_'; - } - /* strip trailing '_' */ - while (i > 0 && tag[i-1] == '_') - i--; - tag[i] = '\0'; - - udev_builtin_add_property(dev, test, "ID_PATH", path); - udev_builtin_add_property(dev, test, "ID_PATH_TAG", tag); - free(path); - return EXIT_SUCCESS; - } - return EXIT_FAILURE; -} - -const struct udev_builtin udev_builtin_path_id = { - .name = "path_id", - .cmd = builtin_path_id, - .help = "compose persistent device path", - .run_once = true, -}; diff --git a/src/udev/src/udev-builtin-usb_id.c b/src/udev/src/udev-builtin-usb_id.c deleted file mode 100644 index 85828e32d7..0000000000 --- a/src/udev/src/udev-builtin-usb_id.c +++ /dev/null @@ -1,482 +0,0 @@ -/* - * USB device properties and persistent device path - * - * Copyright (c) 2005 SUSE Linux Products GmbH, Germany - * Author: Hannes Reinecke - * - * Copyright (C) 2005-2011 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -static void set_usb_iftype(char *to, int if_class_num, size_t len) -{ - char *type = "generic"; - - switch (if_class_num) { - case 1: - type = "audio"; - break; - case 2: /* CDC-Control */ - break; - case 3: - type = "hid"; - break; - case 5: /* Physical */ - break; - case 6: - type = "media"; - break; - case 7: - type = "printer"; - break; - case 8: - type = "storage"; - break; - case 9: - type = "hub"; - break; - case 0x0a: /* CDC-Data */ - break; - case 0x0b: /* Chip/Smart Card */ - break; - case 0x0d: /* Content Security */ - break; - case 0x0e: - type = "video"; - break; - case 0xdc: /* Diagnostic Device */ - break; - case 0xe0: /* Wireless Controller */ - break; - case 0xfe: /* Application-specific */ - break; - case 0xff: /* Vendor-specific */ - break; - default: - break; - } - strncpy(to, type, len); - to[len-1] = '\0'; -} - -static int set_usb_mass_storage_ifsubtype(char *to, const char *from, size_t len) -{ - int type_num = 0; - char *eptr; - char *type = "generic"; - - type_num = strtoul(from, &eptr, 0); - if (eptr != from) { - switch (type_num) { - case 2: - type = "atapi"; - break; - case 3: - type = "tape"; - break; - case 4: /* UFI */ - case 5: /* SFF-8070i */ - type = "floppy"; - break; - case 1: /* RBC devices */ - type = "rbc"; - break; - case 6: /* Transparent SPC-2 devices */ - type = "scsi"; - break; - default: - break; - } - } - util_strscpy(to, len, type); - return type_num; -} - -static void set_scsi_type(char *to, const char *from, size_t len) -{ - int type_num; - char *eptr; - char *type = "generic"; - - type_num = strtoul(from, &eptr, 0); - if (eptr != from) { - switch (type_num) { - case 0: - case 0xe: - type = "disk"; - break; - case 1: - type = "tape"; - break; - case 4: - case 7: - case 0xf: - type = "optical"; - break; - case 5: - type = "cd"; - break; - default: - break; - } - } - util_strscpy(to, len, type); -} - -#define USB_DT_DEVICE 0x01 -#define USB_DT_INTERFACE 0x04 - -static int dev_if_packed_info(struct udev_device *dev, char *ifs_str, size_t len) -{ - char *filename = NULL; - int fd; - ssize_t size; - unsigned char buf[18 + 65535]; - unsigned int pos, strpos; - struct usb_interface_descriptor { - u_int8_t bLength; - u_int8_t bDescriptorType; - u_int8_t bInterfaceNumber; - u_int8_t bAlternateSetting; - u_int8_t bNumEndpoints; - u_int8_t bInterfaceClass; - u_int8_t bInterfaceSubClass; - u_int8_t bInterfaceProtocol; - u_int8_t iInterface; - } __attribute__((packed)); - int err = 0; - - if (asprintf(&filename, "%s/descriptors", udev_device_get_syspath(dev)) < 0) { - err = -1; - goto out; - } - fd = open(filename, O_RDONLY|O_CLOEXEC); - if (fd < 0) { - fprintf(stderr, "error opening USB device 'descriptors' file\n"); - err = -1; - goto out; - } - size = read(fd, buf, sizeof(buf)); - close(fd); - if (size < 18 || size == sizeof(buf)) { - err = -1; - goto out; - } - - pos = 0; - strpos = 0; - ifs_str[0] = '\0'; - while (pos < sizeof(buf) && strpos+7 < len-2) { - struct usb_interface_descriptor *desc; - char if_str[8]; - - desc = (struct usb_interface_descriptor *) &buf[pos]; - if (desc->bLength < 3) - break; - pos += desc->bLength; - - if (desc->bDescriptorType != USB_DT_INTERFACE) - continue; - - if (snprintf(if_str, 8, ":%02x%02x%02x", - desc->bInterfaceClass, - desc->bInterfaceSubClass, - desc->bInterfaceProtocol) != 7) - continue; - - if (strstr(ifs_str, if_str) != NULL) - continue; - - memcpy(&ifs_str[strpos], if_str, 8), - strpos += 7; - } - if (strpos > 0) { - ifs_str[strpos++] = ':'; - ifs_str[strpos++] = '\0'; - } -out: - free(filename); - return err; -} - -/* - * A unique USB identification is generated like this: - * - * 1.) Get the USB device type from InterfaceClass and InterfaceSubClass - * 2.) If the device type is 'Mass-Storage/SPC-2' or 'Mass-Storage/RBC' - * use the SCSI vendor and model as USB-Vendor and USB-model. - * 3.) Otherwise use the USB manufacturer and product as - * USB-Vendor and USB-model. Any non-printable characters - * in those strings will be skipped; a slash '/' will be converted - * into a full stop '.'. - * 4.) If that fails, too, we will use idVendor and idProduct - * as USB-Vendor and USB-model. - * 5.) The USB identification is the USB-vendor and USB-model - * string concatenated with an underscore '_'. - * 6.) If the device supplies a serial number, this number - * is concatenated with the identification with an underscore '_'. - */ -static int builtin_usb_id(struct udev_device *dev, int argc, char *argv[], bool test) -{ - char vendor_str[64]; - char vendor_str_enc[256]; - const char *vendor_id; - char model_str[64]; - char model_str_enc[256]; - const char *product_id; - char serial_str[UTIL_NAME_SIZE]; - char packed_if_str[UTIL_NAME_SIZE]; - char revision_str[64]; - char type_str[64]; - char instance_str[64]; - const char *ifnum = NULL; - const char *driver = NULL; - char serial[256]; - - struct udev *udev = udev_device_get_udev(dev); - struct udev_device *dev_interface = NULL; - struct udev_device *dev_usb = NULL; - const char *if_class, *if_subclass; - int if_class_num; - int protocol = 0; - size_t l; - char *s; - - vendor_str[0] = '\0'; - model_str[0] = '\0'; - serial_str[0] = '\0'; - packed_if_str[0] = '\0'; - revision_str[0] = '\0'; - type_str[0] = '\0'; - instance_str[0] = '\0'; - - dbg(udev, "syspath %s\n", udev_device_get_syspath(dev)); - - /* shortcut, if we are called directly for a "usb_device" type */ - if (udev_device_get_devtype(dev) != NULL && strcmp(udev_device_get_devtype(dev), "usb_device") == 0) { - dev_if_packed_info(dev, packed_if_str, sizeof(packed_if_str)); - dev_usb = dev; - goto fallback; - } - - /* usb interface directory */ - dev_interface = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_interface"); - if (dev_interface == NULL) { - info(udev, "unable to access usb_interface device of '%s'\n", - udev_device_get_syspath(dev)); - return EXIT_FAILURE; - } - - ifnum = udev_device_get_sysattr_value(dev_interface, "bInterfaceNumber"); - driver = udev_device_get_sysattr_value(dev_interface, "driver"); - - if_class = udev_device_get_sysattr_value(dev_interface, "bInterfaceClass"); - if (!if_class) { - info(udev, "%s: cannot get bInterfaceClass attribute\n", - udev_device_get_sysname(dev)); - return EXIT_FAILURE; - } - - if_class_num = strtoul(if_class, NULL, 16); - if (if_class_num == 8) { - /* mass storage */ - if_subclass = udev_device_get_sysattr_value(dev_interface, "bInterfaceSubClass"); - if (if_subclass != NULL) - protocol = set_usb_mass_storage_ifsubtype(type_str, if_subclass, sizeof(type_str)-1); - } else { - set_usb_iftype(type_str, if_class_num, sizeof(type_str)-1); - } - - info(udev, "%s: if_class %d protocol %d\n", - udev_device_get_syspath(dev_interface), if_class_num, protocol); - - /* usb device directory */ - dev_usb = udev_device_get_parent_with_subsystem_devtype(dev_interface, "usb", "usb_device"); - if (!dev_usb) { - info(udev, "unable to find parent 'usb' device of '%s'\n", - udev_device_get_syspath(dev)); - return EXIT_FAILURE; - } - - /* all interfaces of the device in a single string */ - dev_if_packed_info(dev_usb, packed_if_str, sizeof(packed_if_str)); - - /* mass storage : SCSI or ATAPI */ - if ((protocol == 6 || protocol == 2)) { - struct udev_device *dev_scsi; - const char *scsi_model, *scsi_vendor, *scsi_type, *scsi_rev; - int host, bus, target, lun; - - /* get scsi device */ - dev_scsi = udev_device_get_parent_with_subsystem_devtype(dev, "scsi", "scsi_device"); - if (dev_scsi == NULL) { - info(udev, "unable to find parent 'scsi' device of '%s'\n", - udev_device_get_syspath(dev)); - goto fallback; - } - if (sscanf(udev_device_get_sysname(dev_scsi), "%d:%d:%d:%d", &host, &bus, &target, &lun) != 4) { - info(udev, "invalid scsi device '%s'\n", udev_device_get_sysname(dev_scsi)); - goto fallback; - } - - /* Generic SPC-2 device */ - scsi_vendor = udev_device_get_sysattr_value(dev_scsi, "vendor"); - if (!scsi_vendor) { - info(udev, "%s: cannot get SCSI vendor attribute\n", - udev_device_get_sysname(dev_scsi)); - goto fallback; - } - udev_util_encode_string(scsi_vendor, vendor_str_enc, sizeof(vendor_str_enc)); - util_replace_whitespace(scsi_vendor, vendor_str, sizeof(vendor_str)-1); - util_replace_chars(vendor_str, NULL); - - scsi_model = udev_device_get_sysattr_value(dev_scsi, "model"); - if (!scsi_model) { - info(udev, "%s: cannot get SCSI model attribute\n", - udev_device_get_sysname(dev_scsi)); - goto fallback; - } - udev_util_encode_string(scsi_model, model_str_enc, sizeof(model_str_enc)); - util_replace_whitespace(scsi_model, model_str, sizeof(model_str)-1); - util_replace_chars(model_str, NULL); - - scsi_type = udev_device_get_sysattr_value(dev_scsi, "type"); - if (!scsi_type) { - info(udev, "%s: cannot get SCSI type attribute\n", - udev_device_get_sysname(dev_scsi)); - goto fallback; - } - set_scsi_type(type_str, scsi_type, sizeof(type_str)-1); - - scsi_rev = udev_device_get_sysattr_value(dev_scsi, "rev"); - if (!scsi_rev) { - info(udev, "%s: cannot get SCSI revision attribute\n", - udev_device_get_sysname(dev_scsi)); - goto fallback; - } - util_replace_whitespace(scsi_rev, revision_str, sizeof(revision_str)-1); - util_replace_chars(revision_str, NULL); - - /* - * some broken devices have the same identifiers - * for all luns, export the target:lun number - */ - sprintf(instance_str, "%d:%d", target, lun); - } - -fallback: - vendor_id = udev_device_get_sysattr_value(dev_usb, "idVendor"); - product_id = udev_device_get_sysattr_value(dev_usb, "idProduct"); - - /* fallback to USB vendor & device */ - if (vendor_str[0] == '\0') { - const char *usb_vendor = NULL; - - usb_vendor = udev_device_get_sysattr_value(dev_usb, "manufacturer"); - if (!usb_vendor) - usb_vendor = vendor_id; - if (!usb_vendor) { - info(udev, "No USB vendor information available\n"); - return EXIT_FAILURE; - } - udev_util_encode_string(usb_vendor, vendor_str_enc, sizeof(vendor_str_enc)); - util_replace_whitespace(usb_vendor, vendor_str, sizeof(vendor_str)-1); - util_replace_chars(vendor_str, NULL); - } - - if (model_str[0] == '\0') { - const char *usb_model = NULL; - - usb_model = udev_device_get_sysattr_value(dev_usb, "product"); - if (!usb_model) - usb_model = product_id; - if (!usb_model) { - dbg(udev, "No USB model information available\n"); - return EXIT_FAILURE; - } - udev_util_encode_string(usb_model, model_str_enc, sizeof(model_str_enc)); - util_replace_whitespace(usb_model, model_str, sizeof(model_str)-1); - util_replace_chars(model_str, NULL); - } - - if (revision_str[0] == '\0') { - const char *usb_rev; - - usb_rev = udev_device_get_sysattr_value(dev_usb, "bcdDevice"); - if (usb_rev) { - util_replace_whitespace(usb_rev, revision_str, sizeof(revision_str)-1); - util_replace_chars(revision_str, NULL); - } - } - - if (serial_str[0] == '\0') { - const char *usb_serial; - - usb_serial = udev_device_get_sysattr_value(dev_usb, "serial"); - if (usb_serial) { - util_replace_whitespace(usb_serial, serial_str, sizeof(serial_str)-1); - util_replace_chars(serial_str, NULL); - } - } - - s = serial; - l = util_strpcpyl(&s, sizeof(serial), vendor_str, "_", model_str, NULL); - if (serial_str[0] != '\0') - l = util_strpcpyl(&s, l, "_", serial_str, NULL); - - if (instance_str[0] != '\0') - util_strpcpyl(&s, l, "-", instance_str, NULL); - - udev_builtin_add_property(dev, test, "ID_VENDOR", vendor_str); - udev_builtin_add_property(dev, test, "ID_VENDOR_ENC", vendor_str_enc); - udev_builtin_add_property(dev, test, "ID_VENDOR_ID", vendor_id); - udev_builtin_add_property(dev, test, "ID_MODEL", model_str); - udev_builtin_add_property(dev, test, "ID_MODEL_ENC", model_str_enc); - udev_builtin_add_property(dev, test, "ID_MODEL_ID", product_id); - udev_builtin_add_property(dev, test, "ID_REVISION", revision_str); - udev_builtin_add_property(dev, test, "ID_SERIAL", serial); - if (serial_str[0] != '\0') - udev_builtin_add_property(dev, test, "ID_SERIAL_SHORT", serial_str); - if (type_str[0] != '\0') - udev_builtin_add_property(dev, test, "ID_TYPE", type_str); - if (instance_str[0] != '\0') - udev_builtin_add_property(dev, test, "ID_INSTANCE", instance_str); - udev_builtin_add_property(dev, test, "ID_BUS", "usb"); - if (packed_if_str[0] != '\0') - udev_builtin_add_property(dev, test, "ID_USB_INTERFACES", packed_if_str); - if (ifnum != NULL) - udev_builtin_add_property(dev, test, "ID_USB_INTERFACE_NUM", ifnum); - if (driver != NULL) - udev_builtin_add_property(dev, test, "ID_USB_DRIVER", driver); - return EXIT_SUCCESS; -} - -const struct udev_builtin udev_builtin_usb_id = { - .name = "usb_id", - .cmd = builtin_usb_id, - .help = "usb device properties", - .run_once = true, -}; diff --git a/src/udev/src/udev-builtin.c b/src/udev/src/udev-builtin.c deleted file mode 100644 index 5bc5fa68f6..0000000000 --- a/src/udev/src/udev-builtin.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2007-2009 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -static const struct udev_builtin *builtins[] = { - [UDEV_BUILTIN_BLKID] = &udev_builtin_blkid, - [UDEV_BUILTIN_FIRMWARE] = &udev_builtin_firmware, - [UDEV_BUILTIN_INPUT_ID] = &udev_builtin_input_id, - [UDEV_BUILTIN_KMOD] = &udev_builtin_kmod, - [UDEV_BUILTIN_PATH_ID] = &udev_builtin_path_id, - [UDEV_BUILTIN_PCI_DB] = &udev_builtin_pci_db, - [UDEV_BUILTIN_USB_DB] = &udev_builtin_usb_db, - [UDEV_BUILTIN_USB_ID] = &udev_builtin_usb_id, -}; - -int udev_builtin_init(struct udev *udev) -{ - unsigned int i; - int err; - - for (i = 0; i < ARRAY_SIZE(builtins); i++) { - if (builtins[i]->init) { - err = builtins[i]->init(udev); - if (err < 0) - break; - } - } - return err; -} - -void udev_builtin_exit(struct udev *udev) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(builtins); i++) - if (builtins[i]->exit) - builtins[i]->exit(udev); -} - -bool udev_builtin_validate(struct udev *udev) -{ - unsigned int i; - bool change = false; - - for (i = 0; i < ARRAY_SIZE(builtins); i++) - if (builtins[i]->validate) - if (builtins[i]->validate(udev)) - change = true; - return change; -} - -void udev_builtin_list(struct udev *udev) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(builtins); i++) - fprintf(stderr, " %-12s %s\n", builtins[i]->name, builtins[i]->help); -} - -const char *udev_builtin_name(enum udev_builtin_cmd cmd) -{ - return builtins[cmd]->name; -} - -bool udev_builtin_run_once(enum udev_builtin_cmd cmd) -{ - return builtins[cmd]->run_once; -} - -enum udev_builtin_cmd udev_builtin_lookup(const char *command) -{ - char name[UTIL_PATH_SIZE]; - enum udev_builtin_cmd i; - char *pos; - - util_strscpy(name, sizeof(name), command); - pos = strchr(name, ' '); - if (pos) - pos[0] = '\0'; - for (i = 0; i < ARRAY_SIZE(builtins); i++) - if (strcmp(builtins[i]->name, name) == 0) - return i; - return UDEV_BUILTIN_MAX; -} - -int udev_builtin_run(struct udev_device *dev, enum udev_builtin_cmd cmd, const char *command, bool test) -{ - char arg[UTIL_PATH_SIZE]; - int argc; - char *argv[128]; - - optind = 0; - util_strscpy(arg, sizeof(arg), command); - udev_build_argv(udev_device_get_udev(dev), arg, &argc, argv); - return builtins[cmd]->cmd(dev, argc, argv, test); -} - -int udev_builtin_add_property(struct udev_device *dev, bool test, const char *key, const char *val) -{ - struct udev_list_entry *entry; - - entry = udev_device_add_property(dev, key, val); - /* store in db, skip private keys */ - if (key[0] != '.') - udev_list_entry_set_num(entry, true); - - info(udev_device_get_udev(dev), "%s=%s\n", key, val); - if (test) - printf("%s=%s\n", key, val); - return 0; -} diff --git a/src/udev/src/udev-control.socket b/src/udev/src/udev-control.socket deleted file mode 100644 index f80f774427..0000000000 --- a/src/udev/src/udev-control.socket +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=udev Control Socket -DefaultDependencies=no -ConditionCapability=CAP_MKNOD - -[Socket] -Service=udev.service -ListenSequentialPacket=/run/udev/control -SocketMode=0600 -PassCredentials=yes diff --git a/src/udev/src/udev-ctrl.c b/src/udev/src/udev-ctrl.c deleted file mode 100644 index 5556f1a77c..0000000000 --- a/src/udev/src/udev-ctrl.c +++ /dev/null @@ -1,494 +0,0 @@ -/* - * libudev - interface to udev device information - * - * Copyright (C) 2008 Kay Sievers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -/* wire protocol magic must match */ -#define UDEV_CTRL_MAGIC 0xdead1dea - -enum udev_ctrl_msg_type { - UDEV_CTRL_UNKNOWN, - UDEV_CTRL_SET_LOG_LEVEL, - UDEV_CTRL_STOP_EXEC_QUEUE, - UDEV_CTRL_START_EXEC_QUEUE, - UDEV_CTRL_RELOAD, - UDEV_CTRL_SET_ENV, - UDEV_CTRL_SET_CHILDREN_MAX, - UDEV_CTRL_PING, - UDEV_CTRL_EXIT, -}; - -struct udev_ctrl_msg_wire { - char version[16]; - unsigned int magic; - enum udev_ctrl_msg_type type; - union { - int intval; - char buf[256]; - }; -}; - -struct udev_ctrl_msg { - int refcount; - struct udev_ctrl_connection *conn; - struct udev_ctrl_msg_wire ctrl_msg_wire; -}; - -struct udev_ctrl { - int refcount; - struct udev *udev; - int sock; - struct sockaddr_un saddr; - socklen_t addrlen; - bool bound; - bool cleanup_socket; - bool connected; -}; - -struct udev_ctrl_connection { - int refcount; - struct udev_ctrl *uctrl; - int sock; -}; - -struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd) -{ - struct udev_ctrl *uctrl; - - uctrl = calloc(1, sizeof(struct udev_ctrl)); - if (uctrl == NULL) - return NULL; - uctrl->refcount = 1; - uctrl->udev = udev; - - if (fd < 0) { - uctrl->sock = socket(AF_LOCAL, SOCK_SEQPACKET|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); - if (uctrl->sock < 0) { - err(udev, "error getting socket: %m\n"); - udev_ctrl_unref(uctrl); - return NULL; - } - } else { - uctrl->bound = true; - uctrl->sock = fd; - } - - uctrl->saddr.sun_family = AF_LOCAL; - util_strscpyl(uctrl->saddr.sun_path, sizeof(uctrl->saddr.sun_path), - udev_get_run_path(udev), "/control", NULL); - uctrl->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(uctrl->saddr.sun_path); - return uctrl; -} - -struct udev_ctrl *udev_ctrl_new(struct udev *udev) -{ - return udev_ctrl_new_from_fd(udev, -1); -} - -int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl) -{ - int err; - - if (!uctrl->bound) { - err = bind(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen); - if (err < 0 && errno == EADDRINUSE) { - unlink(uctrl->saddr.sun_path); - err = bind(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen); - } - - if (err < 0) { - err = -errno; - err(uctrl->udev, "bind failed: %m\n"); - return err; - } - - err = listen(uctrl->sock, 0); - if (err < 0) { - err = -errno; - err(uctrl->udev, "listen failed: %m\n"); - return err; - } - - uctrl->bound = true; - uctrl->cleanup_socket = true; - } - return 0; -} - -struct udev *udev_ctrl_get_udev(struct udev_ctrl *uctrl) -{ - return uctrl->udev; -} - -struct udev_ctrl *udev_ctrl_ref(struct udev_ctrl *uctrl) -{ - if (uctrl == NULL) - return NULL; - uctrl->refcount++; - return uctrl; -} - -struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl) -{ - if (uctrl == NULL) - return NULL; - uctrl->refcount--; - if (uctrl->refcount > 0) - return uctrl; - if (uctrl->sock >= 0) - close(uctrl->sock); - free(uctrl); - return NULL; -} - -int udev_ctrl_cleanup(struct udev_ctrl *uctrl) -{ - if (uctrl == NULL) - return 0; - if (uctrl->cleanup_socket) - unlink(uctrl->saddr.sun_path); - return 0; -} - -int udev_ctrl_get_fd(struct udev_ctrl *uctrl) -{ - if (uctrl == NULL) - return -EINVAL; - return uctrl->sock; -} - -struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl) -{ - struct udev_ctrl_connection *conn; - struct ucred ucred; - socklen_t slen; - const int on = 1; - - conn = calloc(1, sizeof(struct udev_ctrl_connection)); - if (conn == NULL) - return NULL; - conn->refcount = 1; - conn->uctrl = uctrl; - - conn->sock = accept4(uctrl->sock, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK); - if (conn->sock < 0) { - if (errno != EINTR) - err(uctrl->udev, "unable to receive ctrl connection: %m\n"); - goto err; - } - - /* check peer credential of connection */ - slen = sizeof(ucred); - if (getsockopt(conn->sock, SOL_SOCKET, SO_PEERCRED, &ucred, &slen) < 0) { - err(uctrl->udev, "unable to receive credentials of ctrl connection: %m\n"); - goto err; - } - if (ucred.uid > 0) { - err(uctrl->udev, "sender uid=%i, message ignored\n", ucred.uid); - goto err; - } - - /* enable receiving of the sender credentials in the messages */ - setsockopt(conn->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); - udev_ctrl_ref(uctrl); - return conn; -err: - if (conn->sock >= 0) - close(conn->sock); - free(conn); - return NULL; -} - -struct udev_ctrl_connection *udev_ctrl_connection_ref(struct udev_ctrl_connection *conn) -{ - if (conn == NULL) - return NULL; - conn->refcount++; - return conn; -} - -struct udev_ctrl_connection *udev_ctrl_connection_unref(struct udev_ctrl_connection *conn) -{ - if (conn == NULL) - return NULL; - conn->refcount--; - if (conn->refcount > 0) - return conn; - if (conn->sock >= 0) - close(conn->sock); - udev_ctrl_unref(conn->uctrl); - free(conn); - return NULL; -} - -static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf, int timeout) -{ - struct udev_ctrl_msg_wire ctrl_msg_wire; - int err = 0; - - memset(&ctrl_msg_wire, 0x00, sizeof(struct udev_ctrl_msg_wire)); - strcpy(ctrl_msg_wire.version, "udev-" VERSION); - ctrl_msg_wire.magic = UDEV_CTRL_MAGIC; - ctrl_msg_wire.type = type; - - if (buf != NULL) - util_strscpy(ctrl_msg_wire.buf, sizeof(ctrl_msg_wire.buf), buf); - else - ctrl_msg_wire.intval = intval; - - if (!uctrl->connected) { - if (connect(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen) < 0) { - err = -errno; - goto out; - } - uctrl->connected = true; - } - if (send(uctrl->sock, &ctrl_msg_wire, sizeof(ctrl_msg_wire), 0) < 0) { - err = -errno; - goto out; - } - - /* wait for peer message handling or disconnect */ - for (;;) { - struct pollfd pfd[1]; - int r; - - pfd[0].fd = uctrl->sock; - pfd[0].events = POLLIN; - r = poll(pfd, 1, timeout * 1000); - if (r < 0) { - if (errno == EINTR) - continue; - err = -errno; - break; - } - - if (r > 0 && pfd[0].revents & POLLERR) { - err = -EIO; - break; - } - - if (r == 0) - err = -ETIMEDOUT; - break; - } -out: - return err; -} - -int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, int timeout) -{ - return ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, priority, NULL, timeout); -} - -int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, int timeout) -{ - return ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, 0, NULL, timeout); -} - -int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, int timeout) -{ - return ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL, timeout); -} - -int udev_ctrl_send_reload(struct udev_ctrl *uctrl, int timeout) -{ - return ctrl_send(uctrl, UDEV_CTRL_RELOAD, 0, NULL, timeout); -} - -int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, int timeout) -{ - return ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key, timeout); -} - -int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, int timeout) -{ - return ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL, timeout); -} - -int udev_ctrl_send_ping(struct udev_ctrl *uctrl, int timeout) -{ - return ctrl_send(uctrl, UDEV_CTRL_PING, 0, NULL, timeout); -} - -int udev_ctrl_send_exit(struct udev_ctrl *uctrl, int timeout) -{ - return ctrl_send(uctrl, UDEV_CTRL_EXIT, 0, NULL, timeout); -} - -struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) -{ - struct udev *udev = conn->uctrl->udev; - struct udev_ctrl_msg *uctrl_msg; - ssize_t size; - struct msghdr smsg; - struct cmsghdr *cmsg; - struct iovec iov; - struct ucred *cred; - char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; - - uctrl_msg = calloc(1, sizeof(struct udev_ctrl_msg)); - if (uctrl_msg == NULL) - return NULL; - uctrl_msg->refcount = 1; - uctrl_msg->conn = conn; - udev_ctrl_connection_ref(conn); - - /* wait for the incoming message */ - for(;;) { - struct pollfd pfd[1]; - int r; - - pfd[0].fd = conn->sock; - pfd[0].events = POLLIN; - - r = poll(pfd, 1, 10000); - if (r < 0) { - if (errno == EINTR) - continue; - goto err; - } else if (r == 0) { - err(udev, "timeout waiting for ctrl message\n"); - goto err; - } else { - if (!(pfd[0].revents & POLLIN)) { - err(udev, "ctrl connection error: %m\n"); - goto err; - } - } - - break; - } - - iov.iov_base = &uctrl_msg->ctrl_msg_wire; - iov.iov_len = sizeof(struct udev_ctrl_msg_wire); - memset(&smsg, 0x00, sizeof(struct msghdr)); - smsg.msg_iov = &iov; - smsg.msg_iovlen = 1; - smsg.msg_control = cred_msg; - smsg.msg_controllen = sizeof(cred_msg); - size = recvmsg(conn->sock, &smsg, 0); - if (size < 0) { - err(udev, "unable to receive ctrl message: %m\n"); - goto err; - } - cmsg = CMSG_FIRSTHDR(&smsg); - cred = (struct ucred *) CMSG_DATA(cmsg); - - if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { - err(udev, "no sender credentials received, message ignored\n"); - goto err; - } - - if (cred->uid != 0) { - err(udev, "sender uid=%i, message ignored\n", cred->uid); - goto err; - } - - if (uctrl_msg->ctrl_msg_wire.magic != UDEV_CTRL_MAGIC) { - err(udev, "message magic 0x%08x doesn't match, ignore it\n", uctrl_msg->ctrl_msg_wire.magic); - goto err; - } - - dbg(udev, "created ctrl_msg %p (%i)\n", uctrl_msg, uctrl_msg->ctrl_msg_wire.type); - return uctrl_msg; -err: - udev_ctrl_msg_unref(uctrl_msg); - return NULL; -} - -struct udev_ctrl_msg *udev_ctrl_msg_ref(struct udev_ctrl_msg *ctrl_msg) -{ - if (ctrl_msg == NULL) - return NULL; - ctrl_msg->refcount++; - return ctrl_msg; -} - -struct udev_ctrl_msg *udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg) -{ - if (ctrl_msg == NULL) - return NULL; - ctrl_msg->refcount--; - if (ctrl_msg->refcount > 0) - return ctrl_msg; - dbg(ctrl_msg->conn->uctrl->udev, "release ctrl_msg %p\n", ctrl_msg); - udev_ctrl_connection_unref(ctrl_msg->conn); - free(ctrl_msg); - return NULL; -} - -int udev_ctrl_get_set_log_level(struct udev_ctrl_msg *ctrl_msg) -{ - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_LOG_LEVEL) - return ctrl_msg->ctrl_msg_wire.intval; - return -1; -} - -int udev_ctrl_get_stop_exec_queue(struct udev_ctrl_msg *ctrl_msg) -{ - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_STOP_EXEC_QUEUE) - return 1; - return -1; -} - -int udev_ctrl_get_start_exec_queue(struct udev_ctrl_msg *ctrl_msg) -{ - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_START_EXEC_QUEUE) - return 1; - return -1; -} - -int udev_ctrl_get_reload(struct udev_ctrl_msg *ctrl_msg) -{ - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_RELOAD) - return 1; - return -1; -} - -const char *udev_ctrl_get_set_env(struct udev_ctrl_msg *ctrl_msg) -{ - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_ENV) - return ctrl_msg->ctrl_msg_wire.buf; - return NULL; -} - -int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg) -{ - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_CHILDREN_MAX) - return ctrl_msg->ctrl_msg_wire.intval; - return -1; -} - -int udev_ctrl_get_ping(struct udev_ctrl_msg *ctrl_msg) -{ - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_PING) - return 1; - return -1; -} - -int udev_ctrl_get_exit(struct udev_ctrl_msg *ctrl_msg) -{ - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_EXIT) - return 1; - return -1; -} diff --git a/src/udev/src/udev-event.c b/src/udev/src/udev-event.c deleted file mode 100644 index 45dd77ba2e..0000000000 --- a/src/udev/src/udev-event.c +++ /dev/null @@ -1,1011 +0,0 @@ -/* - * Copyright (C) 2003-2010 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -struct udev_event *udev_event_new(struct udev_device *dev) -{ - struct udev *udev = udev_device_get_udev(dev); - struct udev_event *event; - - event = calloc(1, sizeof(struct udev_event)); - if (event == NULL) - return NULL; - event->dev = dev; - event->udev = udev; - udev_list_init(udev, &event->run_list, false); - event->fd_signal = -1; - event->birth_usec = now_usec(); - event->timeout_usec = 30 * 1000 * 1000; - dbg(event->udev, "allocated event %p\n", event); - return event; -} - -void udev_event_unref(struct udev_event *event) -{ - if (event == NULL) - return; - udev_list_cleanup(&event->run_list); - free(event->program_result); - free(event->name); - dbg(event->udev, "free event %p\n", event); - free(event); -} - -size_t udev_event_apply_format(struct udev_event *event, const char *src, char *dest, size_t size) -{ - struct udev_device *dev = event->dev; - enum subst_type { - SUBST_UNKNOWN, - SUBST_DEVNODE, - SUBST_ATTR, - SUBST_ENV, - SUBST_KERNEL, - SUBST_KERNEL_NUMBER, - SUBST_DRIVER, - SUBST_DEVPATH, - SUBST_ID, - SUBST_MAJOR, - SUBST_MINOR, - SUBST_RESULT, - SUBST_PARENT, - SUBST_NAME, - SUBST_LINKS, - SUBST_ROOT, - SUBST_SYS, - }; - static const struct subst_map { - char *name; - char fmt; - enum subst_type type; - } map[] = { - { .name = "devnode", .fmt = 'N', .type = SUBST_DEVNODE }, - { .name = "tempnode", .fmt = 'N', .type = SUBST_DEVNODE }, - { .name = "attr", .fmt = 's', .type = SUBST_ATTR }, - { .name = "sysfs", .fmt = 's', .type = SUBST_ATTR }, - { .name = "env", .fmt = 'E', .type = SUBST_ENV }, - { .name = "kernel", .fmt = 'k', .type = SUBST_KERNEL }, - { .name = "number", .fmt = 'n', .type = SUBST_KERNEL_NUMBER }, - { .name = "driver", .fmt = 'd', .type = SUBST_DRIVER }, - { .name = "devpath", .fmt = 'p', .type = SUBST_DEVPATH }, - { .name = "id", .fmt = 'b', .type = SUBST_ID }, - { .name = "major", .fmt = 'M', .type = SUBST_MAJOR }, - { .name = "minor", .fmt = 'm', .type = SUBST_MINOR }, - { .name = "result", .fmt = 'c', .type = SUBST_RESULT }, - { .name = "parent", .fmt = 'P', .type = SUBST_PARENT }, - { .name = "name", .fmt = 'D', .type = SUBST_NAME }, - { .name = "links", .fmt = 'L', .type = SUBST_LINKS }, - { .name = "root", .fmt = 'r', .type = SUBST_ROOT }, - { .name = "sys", .fmt = 'S', .type = SUBST_SYS }, - }; - const char *from; - char *s; - size_t l; - - from = src; - s = dest; - l = size; - - for (;;) { - enum subst_type type = SUBST_UNKNOWN; - char attrbuf[UTIL_PATH_SIZE]; - char *attr = NULL; - - while (from[0] != '\0') { - if (from[0] == '$') { - /* substitute named variable */ - unsigned int i; - - if (from[1] == '$') { - from++; - goto copy; - } - - for (i = 0; i < ARRAY_SIZE(map); i++) { - if (strncmp(&from[1], map[i].name, strlen(map[i].name)) == 0) { - type = map[i].type; - from += strlen(map[i].name)+1; - dbg(event->udev, "will substitute format name '%s'\n", map[i].name); - goto subst; - } - } - } else if (from[0] == '%') { - /* substitute format char */ - unsigned int i; - - if (from[1] == '%') { - from++; - goto copy; - } - - for (i = 0; i < ARRAY_SIZE(map); i++) { - if (from[1] == map[i].fmt) { - type = map[i].type; - from += 2; - dbg(event->udev, "will substitute format char '%c'\n", map[i].fmt); - goto subst; - } - } - } -copy: - /* copy char */ - if (l == 0) - goto out; - s[0] = from[0]; - from++; - s++; - l--; - } - - goto out; -subst: - /* extract possible $format{attr} */ - if (from[0] == '{') { - unsigned int i; - - from++; - for (i = 0; from[i] != '}'; i++) { - if (from[i] == '\0') { - err(event->udev, "missing closing brace for format '%s'\n", src); - goto out; - } - } - if (i >= sizeof(attrbuf)) - goto out; - memcpy(attrbuf, from, i); - attrbuf[i] = '\0'; - from += i+1; - attr = attrbuf; - } else { - attr = NULL; - } - - switch (type) { - case SUBST_DEVPATH: - l = util_strpcpy(&s, l, udev_device_get_devpath(dev)); - dbg(event->udev, "substitute devpath '%s'\n", udev_device_get_devpath(dev)); - break; - case SUBST_KERNEL: - l = util_strpcpy(&s, l, udev_device_get_sysname(dev)); - dbg(event->udev, "substitute kernel name '%s'\n", udev_device_get_sysname(dev)); - break; - case SUBST_KERNEL_NUMBER: - if (udev_device_get_sysnum(dev) == NULL) - break; - l = util_strpcpy(&s, l, udev_device_get_sysnum(dev)); - dbg(event->udev, "substitute kernel number '%s'\n", udev_device_get_sysnum(dev)); - break; - case SUBST_ID: - if (event->dev_parent == NULL) - break; - l = util_strpcpy(&s, l, udev_device_get_sysname(event->dev_parent)); - dbg(event->udev, "substitute id '%s'\n", udev_device_get_sysname(event->dev_parent)); - break; - case SUBST_DRIVER: { - const char *driver; - - if (event->dev_parent == NULL) - break; - - driver = udev_device_get_driver(event->dev_parent); - if (driver == NULL) - break; - l = util_strpcpy(&s, l, driver); - dbg(event->udev, "substitute driver '%s'\n", driver); - break; - } - case SUBST_MAJOR: { - char num[UTIL_PATH_SIZE]; - - sprintf(num, "%d", major(udev_device_get_devnum(dev))); - l = util_strpcpy(&s, l, num); - dbg(event->udev, "substitute major number '%s'\n", num); - break; - } - case SUBST_MINOR: { - char num[UTIL_PATH_SIZE]; - - sprintf(num, "%d", minor(udev_device_get_devnum(dev))); - l = util_strpcpy(&s, l, num); - dbg(event->udev, "substitute minor number '%s'\n", num); - break; - } - case SUBST_RESULT: { - char *rest; - int i; - - if (event->program_result == NULL) - break; - /* get part part of the result string */ - i = 0; - if (attr != NULL) - i = strtoul(attr, &rest, 10); - if (i > 0) { - char result[UTIL_PATH_SIZE]; - char tmp[UTIL_PATH_SIZE]; - char *cpos; - - dbg(event->udev, "request part #%d of result string\n", i); - util_strscpy(result, sizeof(result), event->program_result); - cpos = result; - while (--i) { - while (cpos[0] != '\0' && !isspace(cpos[0])) - cpos++; - while (isspace(cpos[0])) - cpos++; - } - if (i > 0) { - err(event->udev, "requested part of result string not found\n"); - break; - } - util_strscpy(tmp, sizeof(tmp), cpos); - /* %{2+}c copies the whole string from the second part on */ - if (rest[0] != '+') { - cpos = strchr(tmp, ' '); - if (cpos) - cpos[0] = '\0'; - } - l = util_strpcpy(&s, l, tmp); - dbg(event->udev, "substitute part of result string '%s'\n", tmp); - } else { - l = util_strpcpy(&s, l, event->program_result); - dbg(event->udev, "substitute result string '%s'\n", event->program_result); - } - break; - } - case SUBST_ATTR: { - const char *value = NULL; - char vbuf[UTIL_NAME_SIZE]; - size_t len; - int count; - - if (attr == NULL) { - err(event->udev, "missing file parameter for attr\n"); - break; - } - - /* try to read the value specified by "[dmi/id]product_name" */ - if (util_resolve_subsys_kernel(event->udev, attr, vbuf, sizeof(vbuf), 1) == 0) - value = vbuf; - - /* try to read the attribute the device */ - if (value == NULL) - value = udev_device_get_sysattr_value(event->dev, attr); - - /* try to read the attribute of the parent device, other matches have selected */ - if (value == NULL && event->dev_parent != NULL && event->dev_parent != event->dev) - value = udev_device_get_sysattr_value(event->dev_parent, attr); - - if (value == NULL) - break; - - /* strip trailing whitespace, and replace unwanted characters */ - if (value != vbuf) - util_strscpy(vbuf, sizeof(vbuf), value); - len = strlen(vbuf); - while (len > 0 && isspace(vbuf[--len])) - vbuf[len] = '\0'; - count = util_replace_chars(vbuf, UDEV_ALLOWED_CHARS_INPUT); - if (count > 0) - info(event->udev, "%i character(s) replaced\n" , count); - l = util_strpcpy(&s, l, vbuf); - dbg(event->udev, "substitute sysfs value '%s'\n", vbuf); - break; - } - case SUBST_PARENT: { - struct udev_device *dev_parent; - const char *devnode; - - dev_parent = udev_device_get_parent(event->dev); - if (dev_parent == NULL) - break; - devnode = udev_device_get_devnode(dev_parent); - if (devnode != NULL) { - size_t devlen = strlen(udev_get_dev_path(event->udev))+1; - - l = util_strpcpy(&s, l, &devnode[devlen]); - dbg(event->udev, "found parent '%s', got node name '%s'\n", - udev_device_get_syspath(dev_parent), &devnode[devlen]); - } - break; - } - case SUBST_DEVNODE: - if (udev_device_get_devnode(dev) != NULL) - l = util_strpcpy(&s, l, udev_device_get_devnode(dev)); - break; - case SUBST_NAME: { - if (event->name != NULL) { - l = util_strpcpy(&s, l, event->name); - dbg(event->udev, "substitute custom node name '%s'\n", event->name); - } else if (udev_device_get_devnode(dev) != NULL) { - size_t devlen = strlen(udev_get_dev_path(event->udev))+1; - - l = util_strpcpy(&s, l, &udev_device_get_devnode(dev)[devlen]); - dbg(event->udev, "substitute node name'%s'\n", &udev_device_get_devnode(dev)[devlen]); - } else { - l = util_strpcpy(&s, l, udev_device_get_sysname(dev)); - dbg(event->udev, "substitute device name'%s'\n", udev_device_get_sysname(dev)); - } - break; - } - case SUBST_LINKS: { - size_t devlen = strlen(udev_get_dev_path(event->udev))+1; - struct udev_list_entry *list_entry; - - list_entry = udev_device_get_devlinks_list_entry(dev); - if (list_entry == NULL) - break; - l = util_strpcpy(&s, l, &udev_list_entry_get_name(list_entry)[devlen]); - udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry)) - l = util_strpcpyl(&s, l, " ", &udev_list_entry_get_name(list_entry)[devlen], NULL); - break; - } - case SUBST_ROOT: - l = util_strpcpy(&s, l, udev_get_dev_path(event->udev)); - dbg(event->udev, "substitute udev_root '%s'\n", udev_get_dev_path(event->udev)); - break; - case SUBST_SYS: - l = util_strpcpy(&s, l, udev_get_sys_path(event->udev)); - dbg(event->udev, "substitute sys_path '%s'\n", udev_get_sys_path(event->udev)); - break; - case SUBST_ENV: - if (attr == NULL) { - dbg(event->udev, "missing attribute\n"); - break; - } else { - const char *value; - - value = udev_device_get_property_value(event->dev, attr); - if (value == NULL) - break; - dbg(event->udev, "substitute env '%s=%s'\n", attr, value); - l = util_strpcpy(&s, l, value); - break; - } - default: - err(event->udev, "unknown substitution type=%i\n", type); - break; - } - } - -out: - s[0] = '\0'; - dbg(event->udev, "'%s' -> '%s' (%zu)\n", src, dest, l); - return l; -} - -static int spawn_exec(struct udev_event *event, - const char *cmd, char *const argv[], char **envp, const sigset_t *sigmask, - int fd_stdout, int fd_stderr) -{ - struct udev *udev = event->udev; - int err; - int fd; - - /* discard child output or connect to pipe */ - fd = open("/dev/null", O_RDWR); - if (fd >= 0) { - dup2(fd, STDIN_FILENO); - if (fd_stdout < 0) - dup2(fd, STDOUT_FILENO); - if (fd_stderr < 0) - dup2(fd, STDERR_FILENO); - close(fd); - } else { - err(udev, "open /dev/null failed: %m\n"); - } - - /* connect pipes to std{out,err} */ - if (fd_stdout >= 0) { - dup2(fd_stdout, STDOUT_FILENO); - close(fd_stdout); - } - if (fd_stderr >= 0) { - dup2(fd_stderr, STDERR_FILENO); - close(fd_stderr); - } - - /* terminate child in case parent goes away */ - prctl(PR_SET_PDEATHSIG, SIGTERM); - - /* restore original udev sigmask before exec */ - if (sigmask) - sigprocmask(SIG_SETMASK, sigmask, NULL); - - execve(argv[0], argv, envp); - - /* exec failed */ - err = -errno; - err(udev, "failed to execute '%s' '%s': %m\n", argv[0], cmd); - return err; -} - -static void spawn_read(struct udev_event *event, - const char *cmd, - int fd_stdout, int fd_stderr, - char *result, size_t ressize) -{ - struct udev *udev = event->udev; - size_t respos = 0; - int fd_ep = -1; - struct epoll_event ep_outpipe, ep_errpipe; - - /* read from child if requested */ - if (fd_stdout < 0 && fd_stderr < 0) - return; - - fd_ep = epoll_create1(EPOLL_CLOEXEC); - if (fd_ep < 0) { - err(udev, "error creating epoll fd: %m\n"); - goto out; - } - - if (fd_stdout >= 0) { - memset(&ep_outpipe, 0, sizeof(struct epoll_event)); - ep_outpipe.events = EPOLLIN; - ep_outpipe.data.ptr = &fd_stdout; - if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stdout, &ep_outpipe) < 0) { - err(udev, "fail to add fd to epoll: %m\n"); - goto out; - } - } - - if (fd_stderr >= 0) { - memset(&ep_errpipe, 0, sizeof(struct epoll_event)); - ep_errpipe.events = EPOLLIN; - ep_errpipe.data.ptr = &fd_stderr; - if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stderr, &ep_errpipe) < 0) { - err(udev, "fail to add fd to epoll: %m\n"); - goto out; - } - } - - /* read child output */ - while (fd_stdout >= 0 || fd_stderr >= 0) { - int timeout; - int fdcount; - struct epoll_event ev[4]; - int i; - - if (event->timeout_usec > 0) { - unsigned long long age_usec; - - age_usec = now_usec() - event->birth_usec; - if (age_usec >= event->timeout_usec) { - err(udev, "timeout '%s'\n", cmd); - goto out; - } - timeout = ((event->timeout_usec - age_usec) / 1000) + 1000; - } else { - timeout = -1; - } - - fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), timeout); - if (fdcount < 0) { - if (errno == EINTR) - continue; - err(udev, "failed to poll: %m\n"); - goto out; - } - if (fdcount == 0) { - err(udev, "timeout '%s'\n", cmd); - goto out; - } - - for (i = 0; i < fdcount; i++) { - int *fd = (int *)ev[i].data.ptr; - - if (ev[i].events & EPOLLIN) { - ssize_t count; - char buf[4096]; - - count = read(*fd, buf, sizeof(buf)-1); - if (count <= 0) - continue; - buf[count] = '\0'; - - /* store stdout result */ - if (result != NULL && *fd == fd_stdout) { - if (respos + count < ressize) { - memcpy(&result[respos], buf, count); - respos += count; - } else { - err(udev, "'%s' ressize %zd too short\n", cmd, ressize); - } - } - - /* log debug output only if we watch stderr */ - if (fd_stderr >= 0) { - char *pos; - char *line; - - pos = buf; - while ((line = strsep(&pos, "\n"))) { - if (pos != NULL || line[0] != '\0') - info(udev, "'%s'(%s) '%s'\n", cmd, *fd == fd_stdout ? "out" : "err" , line); - } - } - } else if (ev[i].events & EPOLLHUP) { - if (epoll_ctl(fd_ep, EPOLL_CTL_DEL, *fd, NULL) < 0) { - err(udev, "failed to remove fd from epoll: %m\n"); - goto out; - } - *fd = -1; - } - } - } - - /* return the child's stdout string */ - if (result != NULL) { - result[respos] = '\0'; - dbg(udev, "result='%s'\n", result); - } -out: - if (fd_ep >= 0) - close(fd_ep); -} - -static int spawn_wait(struct udev_event *event, const char *cmd, pid_t pid) -{ - struct udev *udev = event->udev; - struct pollfd pfd[1]; - int err = 0; - - pfd[0].events = POLLIN; - pfd[0].fd = event->fd_signal; - - while (pid > 0) { - int timeout; - int fdcount; - - if (event->timeout_usec > 0) { - unsigned long long age_usec; - - age_usec = now_usec() - event->birth_usec; - if (age_usec >= event->timeout_usec) - timeout = 1000; - else - timeout = ((event->timeout_usec - age_usec) / 1000) + 1000; - } else { - timeout = -1; - } - - fdcount = poll(pfd, 1, timeout); - if (fdcount < 0) { - if (errno == EINTR) - continue; - err = -errno; - err(udev, "failed to poll: %m\n"); - goto out; - } - if (fdcount == 0) { - err(udev, "timeout: killing '%s' [%u]\n", cmd, pid); - kill(pid, SIGKILL); - } - - if (pfd[0].revents & POLLIN) { - struct signalfd_siginfo fdsi; - int status; - ssize_t size; - - size = read(event->fd_signal, &fdsi, sizeof(struct signalfd_siginfo)); - if (size != sizeof(struct signalfd_siginfo)) - continue; - - switch (fdsi.ssi_signo) { - case SIGTERM: - event->sigterm = true; - break; - case SIGCHLD: - if (waitpid(pid, &status, WNOHANG) < 0) - break; - if (WIFEXITED(status)) { - info(udev, "'%s' [%u] exit with return code %i\n", cmd, pid, WEXITSTATUS(status)); - if (WEXITSTATUS(status) != 0) - err = -1; - } else if (WIFSIGNALED(status)) { - err(udev, "'%s' [%u] terminated by signal %i (%s)\n", cmd, pid, WTERMSIG(status), strsignal(WTERMSIG(status))); - err = -1; - } else if (WIFSTOPPED(status)) { - err(udev, "'%s' [%u] stopped\n", cmd, pid); - err = -1; - } else if (WIFCONTINUED(status)) { - err(udev, "'%s' [%u] continued\n", cmd, pid); - err = -1; - } else { - err(udev, "'%s' [%u] exit with status 0x%04x\n", cmd, pid, status); - err = -1; - } - pid = 0; - break; - } - } - } -out: - return err; -} - -int udev_build_argv(struct udev *udev, char *cmd, int *argc, char *argv[]) -{ - int i = 0; - char *pos; - - if (strchr(cmd, ' ') == NULL) { - argv[i++] = cmd; - goto out; - } - - pos = cmd; - while (pos != NULL && pos[0] != '\0') { - if (pos[0] == '\'') { - /* do not separate quotes */ - pos++; - argv[i] = strsep(&pos, "\'"); - if (pos != NULL) - while (pos[0] == ' ') - pos++; - } else { - argv[i] = strsep(&pos, " "); - if (pos != NULL) - while (pos[0] == ' ') - pos++; - } - dbg(udev, "argv[%i] '%s'\n", i, argv[i]); - i++; - } -out: - argv[i] = NULL; - if (argc) - *argc = i; - return 0; -} - -int udev_event_spawn(struct udev_event *event, - const char *cmd, char **envp, const sigset_t *sigmask, - char *result, size_t ressize) -{ - struct udev *udev = event->udev; - int outpipe[2] = {-1, -1}; - int errpipe[2] = {-1, -1}; - pid_t pid; - char arg[UTIL_PATH_SIZE]; - char *argv[128]; - char program[UTIL_PATH_SIZE]; - int err = 0; - - util_strscpy(arg, sizeof(arg), cmd); - udev_build_argv(event->udev, arg, NULL, argv); - - /* pipes from child to parent */ - if (result != NULL || udev_get_log_priority(udev) >= LOG_INFO) { - if (pipe2(outpipe, O_NONBLOCK) != 0) { - err = -errno; - err(udev, "pipe failed: %m\n"); - goto out; - } - } - if (udev_get_log_priority(udev) >= LOG_INFO) { - if (pipe2(errpipe, O_NONBLOCK) != 0) { - err = -errno; - err(udev, "pipe failed: %m\n"); - goto out; - } - } - - /* allow programs in /usr/lib/udev/ to be called without the path */ - if (argv[0][0] != '/') { - util_strscpyl(program, sizeof(program), PKGLIBEXECDIR "/", argv[0], NULL); - argv[0] = program; - } - - pid = fork(); - switch(pid) { - case 0: - /* child closes parent's ends of pipes */ - if (outpipe[READ_END] >= 0) { - close(outpipe[READ_END]); - outpipe[READ_END] = -1; - } - if (errpipe[READ_END] >= 0) { - close(errpipe[READ_END]); - errpipe[READ_END] = -1; - } - - info(udev, "starting '%s'\n", cmd); - - err = spawn_exec(event, cmd, argv, envp, sigmask, - outpipe[WRITE_END], errpipe[WRITE_END]); - - _exit(2 ); - case -1: - err(udev, "fork of '%s' failed: %m\n", cmd); - err = -1; - goto out; - default: - /* parent closed child's ends of pipes */ - if (outpipe[WRITE_END] >= 0) { - close(outpipe[WRITE_END]); - outpipe[WRITE_END] = -1; - } - if (errpipe[WRITE_END] >= 0) { - close(errpipe[WRITE_END]); - errpipe[WRITE_END] = -1; - } - - spawn_read(event, cmd, - outpipe[READ_END], errpipe[READ_END], - result, ressize); - - err = spawn_wait(event, cmd, pid); - } - -out: - if (outpipe[READ_END] >= 0) - close(outpipe[READ_END]); - if (outpipe[WRITE_END] >= 0) - close(outpipe[WRITE_END]); - if (errpipe[READ_END] >= 0) - close(errpipe[READ_END]); - if (errpipe[WRITE_END] >= 0) - close(errpipe[WRITE_END]); - return err; -} - -static void rename_netif_kernel_log(struct ifreq ifr) -{ - int klog; - FILE *f; - - klog = open("/dev/kmsg", O_WRONLY); - if (klog < 0) - return; - - f = fdopen(klog, "w"); - if (f == NULL) { - close(klog); - return; - } - - fprintf(f, "<30>udevd[%u]: renamed network interface %s to %s\n", - getpid(), ifr.ifr_name, ifr.ifr_newname); - fclose(f); -} - -static int rename_netif(struct udev_event *event) -{ - struct udev_device *dev = event->dev; - int sk; - struct ifreq ifr; - int loop; - int err; - - info(event->udev, "changing net interface name from '%s' to '%s'\n", - udev_device_get_sysname(dev), event->name); - - sk = socket(PF_INET, SOCK_DGRAM, 0); - if (sk < 0) { - err = -errno; - err(event->udev, "error opening socket: %m\n"); - return err; - } - - memset(&ifr, 0x00, sizeof(struct ifreq)); - util_strscpy(ifr.ifr_name, IFNAMSIZ, udev_device_get_sysname(dev)); - util_strscpy(ifr.ifr_newname, IFNAMSIZ, event->name); - err = ioctl(sk, SIOCSIFNAME, &ifr); - if (err == 0) { - rename_netif_kernel_log(ifr); - goto out; - } - - /* keep trying if the destination interface name already exists */ - err = -errno; - if (err != -EEXIST) - goto out; - - /* free our own name, another process may wait for us */ - snprintf(ifr.ifr_newname, IFNAMSIZ, "rename%u", udev_device_get_ifindex(dev)); - err = ioctl(sk, SIOCSIFNAME, &ifr); - if (err < 0) { - err = -errno; - goto out; - } - - /* log temporary name */ - rename_netif_kernel_log(ifr); - - /* wait a maximum of 90 seconds for our target to become available */ - util_strscpy(ifr.ifr_name, IFNAMSIZ, ifr.ifr_newname); - util_strscpy(ifr.ifr_newname, IFNAMSIZ, event->name); - loop = 90 * 20; - while (loop--) { - const struct timespec duration = { 0, 1000 * 1000 * 1000 / 20 }; - - dbg(event->udev, "wait for netif '%s' to become free, loop=%i\n", - event->name, (90 * 20) - loop); - nanosleep(&duration, NULL); - - err = ioctl(sk, SIOCSIFNAME, &ifr); - if (err == 0) { - rename_netif_kernel_log(ifr); - break; - } - err = -errno; - if (err != -EEXIST) - break; - } - -out: - if (err < 0) - err(event->udev, "error changing net interface name %s to %s: %m\n", ifr.ifr_name, ifr.ifr_newname); - close(sk); - return err; -} - -int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules, const sigset_t *sigmask) -{ - struct udev_device *dev = event->dev; - int err = 0; - - if (udev_device_get_subsystem(dev) == NULL) - return -1; - - if (strcmp(udev_device_get_action(dev), "remove") == 0) { - udev_device_read_db(dev, NULL); - udev_device_delete_db(dev); - udev_device_tag_index(dev, NULL, false); - - if (major(udev_device_get_devnum(dev)) != 0) - udev_watch_end(event->udev, dev); - - udev_rules_apply_to_event(rules, event, sigmask); - - if (major(udev_device_get_devnum(dev)) != 0) - udev_node_remove(dev); - } else { - event->dev_db = udev_device_new_from_syspath(event->udev, udev_device_get_syspath(dev)); - if (event->dev_db != NULL) { - udev_device_read_db(event->dev_db, NULL); - udev_device_set_info_loaded(event->dev_db); - - /* disable watch during event processing */ - if (major(udev_device_get_devnum(dev)) != 0) - udev_watch_end(event->udev, event->dev_db); - } - - udev_rules_apply_to_event(rules, event, sigmask); - - /* rename a new network interface, if needed */ - if (udev_device_get_ifindex(dev) > 0 && strcmp(udev_device_get_action(dev), "add") == 0 && - event->name != NULL && strcmp(event->name, udev_device_get_sysname(dev)) != 0) { - char syspath[UTIL_PATH_SIZE]; - char *pos; - - err = rename_netif(event); - if (err == 0) { - info(event->udev, "renamed netif to '%s'\n", event->name); - - /* remember old name */ - udev_device_add_property(dev, "INTERFACE_OLD", udev_device_get_sysname(dev)); - - /* now change the devpath, because the kernel device name has changed */ - util_strscpy(syspath, sizeof(syspath), udev_device_get_syspath(dev)); - pos = strrchr(syspath, '/'); - if (pos != NULL) { - pos++; - util_strscpy(pos, sizeof(syspath) - (pos - syspath), event->name); - udev_device_set_syspath(event->dev, syspath); - udev_device_add_property(dev, "INTERFACE", udev_device_get_sysname(dev)); - info(event->udev, "changed devpath to '%s'\n", udev_device_get_devpath(dev)); - } - } - } - - if (major(udev_device_get_devnum(dev)) > 0) { - /* remove/update possible left-over symlinks from old database entry */ - if (event->dev_db != NULL) - udev_node_update_old_links(dev, event->dev_db); - - if (!event->mode_set) { - if (udev_device_get_devnode_mode(dev) > 0) { - /* kernel supplied value */ - event->mode = udev_device_get_devnode_mode(dev); - } else if (event->gid > 0) { - /* default 0660 if a group is assigned */ - event->mode = 0660; - } else { - /* default 0600 */ - event->mode = 0600; - } - } - - udev_node_add(dev, event->mode, event->uid, event->gid); - } - - /* preserve old, or get new initialization timestamp */ - if (event->dev_db != NULL && udev_device_get_usec_initialized(event->dev_db) > 0) - udev_device_set_usec_initialized(event->dev, udev_device_get_usec_initialized(event->dev_db)); - else if (udev_device_get_usec_initialized(event->dev) == 0) - udev_device_set_usec_initialized(event->dev, now_usec()); - - /* (re)write database file */ - udev_device_update_db(dev); - udev_device_tag_index(dev, event->dev_db, true); - udev_device_set_is_initialized(dev); - - udev_device_unref(event->dev_db); - event->dev_db = NULL; - } -out: - return err; -} - -int udev_event_execute_run(struct udev_event *event, const sigset_t *sigmask) -{ - struct udev_list_entry *list_entry; - int err = 0; - - dbg(event->udev, "executing run list\n"); - udev_list_entry_foreach(list_entry, udev_list_get_entry(&event->run_list)) { - const char *cmd = udev_list_entry_get_name(list_entry); - - if (strncmp(cmd, "socket:", strlen("socket:")) == 0) { - struct udev_monitor *monitor; - - monitor = udev_monitor_new_from_socket(event->udev, &cmd[strlen("socket:")]); - if (monitor == NULL) - continue; - udev_monitor_send_device(monitor, NULL, event->dev); - udev_monitor_unref(monitor); - } else { - char program[UTIL_PATH_SIZE]; - char **envp; - - if (event->exec_delay > 0) { - info(event->udev, "delay execution of '%s'\n", program); - sleep(event->exec_delay); - } - - udev_event_apply_format(event, cmd, program, sizeof(program)); - envp = udev_device_get_properties_envp(event->dev); - if (udev_event_spawn(event, program, envp, sigmask, NULL, 0) < 0) { - if (udev_list_entry_get_num(list_entry)) - err = -1; - } - } - } - return err; -} diff --git a/src/udev/src/udev-kernel.socket b/src/udev/src/udev-kernel.socket deleted file mode 100644 index 23fa9d5e11..0000000000 --- a/src/udev/src/udev-kernel.socket +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=udev Kernel Socket -DefaultDependencies=no -ConditionCapability=CAP_MKNOD - -[Socket] -Service=udev.service -ReceiveBuffer=134217728 -ListenNetlink=kobject-uevent 1 -PassCredentials=yes diff --git a/src/udev/src/udev-node.c b/src/udev/src/udev-node.c deleted file mode 100644 index 7a01a479ee..0000000000 --- a/src/udev/src/udev-node.c +++ /dev/null @@ -1,379 +0,0 @@ -/* - * Copyright (C) 2003-2010 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -#define TMP_FILE_EXT ".udev-tmp" - -static int node_symlink(struct udev *udev, const char *node, const char *slink) -{ - struct stat stats; - char target[UTIL_PATH_SIZE]; - char *s; - size_t l; - char slink_tmp[UTIL_PATH_SIZE + sizeof(TMP_FILE_EXT)]; - int i = 0; - int tail = 0; - int err = 0; - - /* use relative link */ - target[0] = '\0'; - while (node[i] && (node[i] == slink[i])) { - if (node[i] == '/') - tail = i+1; - i++; - } - s = target; - l = sizeof(target); - while (slink[i] != '\0') { - if (slink[i] == '/') - l = util_strpcpy(&s, l, "../"); - i++; - } - l = util_strscpy(s, l, &node[tail]); - if (l == 0) { - err = -EINVAL; - goto exit; - } - - /* preserve link with correct target, do not replace node of other device */ - if (lstat(slink, &stats) == 0) { - if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) { - struct stat stats2; - - info(udev, "found existing node instead of symlink '%s'\n", slink); - if (lstat(node, &stats2) == 0) { - if ((stats.st_mode & S_IFMT) == (stats2.st_mode & S_IFMT) && - stats.st_rdev == stats2.st_rdev && stats.st_ino != stats2.st_ino) { - info(udev, "replace device node '%s' with symlink to our node '%s'\n", - slink, node); - } else { - err(udev, "device node '%s' already exists, " - "link to '%s' will not overwrite it\n", - slink, node); - goto exit; - } - } - } else if (S_ISLNK(stats.st_mode)) { - char buf[UTIL_PATH_SIZE]; - int len; - - dbg(udev, "found existing symlink '%s'\n", slink); - len = readlink(slink, buf, sizeof(buf)); - if (len > 0 && len < (int)sizeof(buf)) { - buf[len] = '\0'; - if (strcmp(target, buf) == 0) { - info(udev, "preserve already existing symlink '%s' to '%s'\n", - slink, target); - udev_selinux_lsetfilecon(udev, slink, S_IFLNK); - utimensat(AT_FDCWD, slink, NULL, AT_SYMLINK_NOFOLLOW); - goto exit; - } - } - } - } else { - info(udev, "creating symlink '%s' to '%s'\n", slink, target); - do { - err = util_create_path_selinux(udev, slink); - if (err != 0 && err != -ENOENT) - break; - udev_selinux_setfscreatecon(udev, slink, S_IFLNK); - err = symlink(target, slink); - if (err != 0) - err = -errno; - udev_selinux_resetfscreatecon(udev); - } while (err == -ENOENT); - if (err == 0) - goto exit; - } - - info(udev, "atomically replace '%s'\n", slink); - util_strscpyl(slink_tmp, sizeof(slink_tmp), slink, TMP_FILE_EXT, NULL); - unlink(slink_tmp); - do { - err = util_create_path_selinux(udev, slink_tmp); - if (err != 0 && err != -ENOENT) - break; - udev_selinux_setfscreatecon(udev, slink_tmp, S_IFLNK); - err = symlink(target, slink_tmp); - if (err != 0) - err = -errno; - udev_selinux_resetfscreatecon(udev); - } while (err == -ENOENT); - if (err != 0) { - err(udev, "symlink '%s' '%s' failed: %m\n", target, slink_tmp); - goto exit; - } - err = rename(slink_tmp, slink); - if (err != 0) { - err(udev, "rename '%s' '%s' failed: %m\n", slink_tmp, slink); - unlink(slink_tmp); - } -exit: - return err; -} - -/* find device node of device with highest priority */ -static const char *link_find_prioritized(struct udev_device *dev, bool add, const char *stackdir, char *buf, size_t bufsize) -{ - struct udev *udev = udev_device_get_udev(dev); - DIR *dir; - int priority = 0; - const char *target = NULL; - - if (add) { - priority = udev_device_get_devlink_priority(dev); - util_strscpy(buf, bufsize, udev_device_get_devnode(dev)); - target = buf; - } - - dir = opendir(stackdir); - if (dir == NULL) - return target; - for (;;) { - struct udev_device *dev_db; - struct dirent *dent; - - dent = readdir(dir); - if (dent == NULL || dent->d_name[0] == '\0') - break; - if (dent->d_name[0] == '.') - continue; - - info(udev, "found '%s' claiming '%s'\n", dent->d_name, stackdir); - - /* did we find ourself? */ - if (strcmp(dent->d_name, udev_device_get_id_filename(dev)) == 0) - continue; - - dev_db = udev_device_new_from_id_filename(udev, dent->d_name); - if (dev_db != NULL) { - const char *devnode; - - devnode = udev_device_get_devnode(dev_db); - if (devnode != NULL) { - dbg(udev, "compare priority of '%s'(%i) > '%s'(%i)\n", target, priority, - udev_device_get_devnode(dev_db), udev_device_get_devlink_priority(dev_db)); - if (target == NULL || udev_device_get_devlink_priority(dev_db) > priority) { - info(udev, "'%s' claims priority %i for '%s'\n", - udev_device_get_syspath(dev_db), udev_device_get_devlink_priority(dev_db), stackdir); - priority = udev_device_get_devlink_priority(dev_db); - util_strscpy(buf, bufsize, devnode); - target = buf; - } - } - udev_device_unref(dev_db); - } - } - closedir(dir); - return target; -} - -/* manage "stack of names" with possibly specified device priorities */ -static void link_update(struct udev_device *dev, const char *slink, bool add) -{ - struct udev *udev = udev_device_get_udev(dev); - char name_enc[UTIL_PATH_SIZE]; - char filename[UTIL_PATH_SIZE * 2]; - char dirname[UTIL_PATH_SIZE]; - const char *target; - char buf[UTIL_PATH_SIZE]; - - dbg(udev, "update symlink '%s' of '%s'\n", slink, udev_device_get_syspath(dev)); - - util_path_encode(&slink[strlen(udev_get_dev_path(udev))+1], name_enc, sizeof(name_enc)); - util_strscpyl(dirname, sizeof(dirname), udev_get_run_path(udev), "/links/", name_enc, NULL); - util_strscpyl(filename, sizeof(filename), dirname, "/", udev_device_get_id_filename(dev), NULL); - - if (!add) { - dbg(udev, "removing index: '%s'\n", filename); - if (unlink(filename) == 0) - rmdir(dirname); - } - - target = link_find_prioritized(dev, add, dirname, buf, sizeof(buf)); - if (target == NULL) { - info(udev, "no reference left, remove '%s'\n", slink); - if (unlink(slink) == 0) - util_delete_path(udev, slink); - } else { - info(udev, "creating link '%s' to '%s'\n", slink, target); - node_symlink(udev, target, slink); - } - - if (add) { - int err; - - dbg(udev, "creating index: '%s'\n", filename); - do { - int fd; - - err = util_create_path(udev, filename); - if (err != 0 && err != -ENOENT) - break; - fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444); - if (fd >= 0) - close(fd); - else - err = -errno; - } while (err == -ENOENT); - } -} - -void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old) -{ - struct udev *udev = udev_device_get_udev(dev); - struct udev_list_entry *list_entry; - - /* update possible left-over symlinks */ - udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev_old)) { - const char *name = udev_list_entry_get_name(list_entry); - struct udev_list_entry *list_entry_current; - int found; - - /* check if old link name still belongs to this device */ - found = 0; - udev_list_entry_foreach(list_entry_current, udev_device_get_devlinks_list_entry(dev)) { - const char *name_current = udev_list_entry_get_name(list_entry_current); - - if (strcmp(name, name_current) == 0) { - found = 1; - break; - } - } - if (found) - continue; - - info(udev, "update old name, '%s' no longer belonging to '%s'\n", - name, udev_device_get_devpath(dev)); - link_update(dev, name, 0); - } -} - -static int node_fixup(struct udev_device *dev, mode_t mode, uid_t uid, gid_t gid) -{ - struct udev *udev = udev_device_get_udev(dev); - const char *devnode = udev_device_get_devnode(dev); - dev_t devnum = udev_device_get_devnum(dev); - struct stat stats; - int err = 0; - - if (strcmp(udev_device_get_subsystem(dev), "block") == 0) - mode |= S_IFBLK; - else - mode |= S_IFCHR; - - if (lstat(devnode, &stats) != 0) { - err = -errno; - info(udev, "can not stat() node '%s' (%m)\n", devnode); - goto out; - } - - if (((stats.st_mode & S_IFMT) != (mode & S_IFMT)) || (stats.st_rdev != devnum)) { - err = -EEXIST; - info(udev, "found node '%s' with non-matching devnum %s, skip handling\n", - udev_device_get_devnode(dev), udev_device_get_id_filename(dev)); - goto out; - } - - if ((stats.st_mode & 0777) != (mode & 0777) || stats.st_uid != uid || stats.st_gid != gid) { - info(udev, "set permissions %s, %#o, uid=%u, gid=%u\n", devnode, mode, uid, gid); - chmod(devnode, mode); - chown(devnode, uid, gid); - } else { - info(udev, "preserve permissions %s, %#o, uid=%u, gid=%u\n", devnode, mode, uid, gid); - } - - /* - * Set initial selinux file context only on add events. - * We set the proper context on bootup (triger) or for newly - * added devices, but we don't change it later, in case - * something else has set a custom context in the meantime. - */ - if (strcmp(udev_device_get_action(dev), "add") == 0) - udev_selinux_lsetfilecon(udev, devnode, mode); - - /* always update timestamp when we re-use the node, like on media change events */ - utimensat(AT_FDCWD, devnode, NULL, 0); -out: - return err; -} - -void udev_node_add(struct udev_device *dev, mode_t mode, uid_t uid, gid_t gid) -{ - struct udev *udev = udev_device_get_udev(dev); - char filename[UTIL_PATH_SIZE]; - struct udev_list_entry *list_entry; - int err = 0; - - info(udev, "handling device node '%s', devnum=%s, mode=%#o, uid=%d, gid=%d\n", - udev_device_get_devnode(dev), udev_device_get_id_filename(dev), mode, uid, gid); - - if (node_fixup(dev, mode, uid, gid) < 0) - return; - - /* always add /dev/{block,char}/$major:$minor */ - snprintf(filename, sizeof(filename), "%s/%s/%u:%u", - udev_get_dev_path(udev), - strcmp(udev_device_get_subsystem(dev), "block") == 0 ? "block" : "char", - major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev))); - node_symlink(udev, udev_device_get_devnode(dev), filename); - - /* create/update symlinks, add symlinks to name index */ - udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) { - if (udev_list_entry_get_num(list_entry)) - /* simple unmanaged link name */ - node_symlink(udev, udev_device_get_devnode(dev), udev_list_entry_get_name(list_entry)); - else - link_update(dev, udev_list_entry_get_name(list_entry), 1); - } -} - -void udev_node_remove(struct udev_device *dev) -{ - struct udev *udev = udev_device_get_udev(dev); - struct udev_list_entry *list_entry; - const char *devnode; - struct stat stats; - struct udev_device *dev_check; - char filename[UTIL_PATH_SIZE]; - - /* remove/update symlinks, remove symlinks from name index */ - udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) - link_update(dev, udev_list_entry_get_name(list_entry), 0); - - /* remove /dev/{block,char}/$major:$minor */ - snprintf(filename, sizeof(filename), "%s/%s/%u:%u", - udev_get_dev_path(udev), - strcmp(udev_device_get_subsystem(dev), "block") == 0 ? "block" : "char", - major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev))); - unlink(filename); -} diff --git a/src/udev/src/udev-rules.c b/src/udev/src/udev-rules.c deleted file mode 100644 index 8a85eae717..0000000000 --- a/src/udev/src/udev-rules.c +++ /dev/null @@ -1,2767 +0,0 @@ -/* - * Copyright (C) 2003-2010 Kay Sievers - * Copyright (C) 2008 Alan Jenkins - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -#define PREALLOC_TOKEN 2048 -#define PREALLOC_STRBUF 32 * 1024 -#define PREALLOC_TRIE 256 - -struct uid_gid { - unsigned int name_off; - union { - uid_t uid; - gid_t gid; - }; -}; - -struct trie_node { - /* this node's first child */ - unsigned int child_idx; - /* the next child of our parent node's child list */ - unsigned int next_child_idx; - /* this node's last child (shortcut for append) */ - unsigned int last_child_idx; - unsigned int value_off; - unsigned short value_len; - unsigned char key; -}; - -struct udev_rules { - struct udev *udev; - int resolve_names; - - /* every key in the rules file becomes a token */ - struct token *tokens; - unsigned int token_cur; - unsigned int token_max; - - /* all key strings are copied to a single string buffer */ - char *buf; - size_t buf_cur; - size_t buf_max; - unsigned int buf_count; - - /* during rule parsing, strings are indexed to find duplicates */ - struct trie_node *trie_nodes; - unsigned int trie_nodes_cur; - unsigned int trie_nodes_max; - - /* during rule parsing, uid/gid lookup results are cached */ - struct uid_gid *uids; - unsigned int uids_cur; - unsigned int uids_max; - struct uid_gid *gids; - unsigned int gids_cur; - unsigned int gids_max; -}; - -/* KEY=="", KEY!="", KEY+="", KEY="", KEY:="" */ -enum operation_type { - OP_UNSET, - - OP_MATCH, - OP_NOMATCH, - OP_MATCH_MAX, - - OP_ADD, - OP_ASSIGN, - OP_ASSIGN_FINAL, -}; - -enum string_glob_type { - GL_UNSET, - GL_PLAIN, /* no special chars */ - GL_GLOB, /* shell globs ?,*,[] */ - GL_SPLIT, /* multi-value A|B */ - GL_SPLIT_GLOB, /* multi-value with glob A*|B* */ - GL_SOMETHING, /* commonly used "?*" */ -}; - -enum string_subst_type { - SB_UNSET, - SB_NONE, - SB_FORMAT, - SB_SUBSYS, -}; - -/* tokens of a rule are sorted/handled in this order */ -enum token_type { - TK_UNSET, - TK_RULE, - - TK_M_ACTION, /* val */ - TK_M_DEVPATH, /* val */ - TK_M_KERNEL, /* val */ - TK_M_DEVLINK, /* val */ - TK_M_NAME, /* val */ - TK_M_ENV, /* val, attr */ - TK_M_TAG, /* val */ - TK_M_SUBSYSTEM, /* val */ - TK_M_DRIVER, /* val */ - TK_M_WAITFOR, /* val */ - TK_M_ATTR, /* val, attr */ - - TK_M_PARENTS_MIN, - TK_M_KERNELS, /* val */ - TK_M_SUBSYSTEMS, /* val */ - TK_M_DRIVERS, /* val */ - TK_M_ATTRS, /* val, attr */ - TK_M_TAGS, /* val */ - TK_M_PARENTS_MAX, - - TK_M_TEST, /* val, mode_t */ - TK_M_EVENT_TIMEOUT, /* int */ - TK_M_PROGRAM, /* val */ - TK_M_IMPORT_FILE, /* val */ - TK_M_IMPORT_PROG, /* val */ - TK_M_IMPORT_BUILTIN, /* val */ - TK_M_IMPORT_DB, /* val */ - TK_M_IMPORT_CMDLINE, /* val */ - TK_M_IMPORT_PARENT, /* val */ - TK_M_RESULT, /* val */ - TK_M_MAX, - - TK_A_STRING_ESCAPE_NONE, - TK_A_STRING_ESCAPE_REPLACE, - TK_A_DB_PERSIST, - TK_A_INOTIFY_WATCH, /* int */ - TK_A_DEVLINK_PRIO, /* int */ - TK_A_OWNER, /* val */ - TK_A_GROUP, /* val */ - TK_A_MODE, /* val */ - TK_A_OWNER_ID, /* uid_t */ - TK_A_GROUP_ID, /* gid_t */ - TK_A_MODE_ID, /* mode_t */ - TK_A_STATIC_NODE, /* val */ - TK_A_ENV, /* val, attr */ - TK_A_TAG, /* val */ - TK_A_NAME, /* val */ - TK_A_DEVLINK, /* val */ - TK_A_ATTR, /* val, attr */ - TK_A_RUN, /* val, bool */ - TK_A_GOTO, /* size_t */ - - TK_END, -}; - -/* we try to pack stuff in a way that we take only 12 bytes per token */ -struct token { - union { - unsigned char type; /* same in rule and key */ - struct { - enum token_type type:8; - bool can_set_name:1; - bool has_static_node:1; - unsigned int unused:6; - unsigned short token_count; - unsigned int label_off; - unsigned short filename_off; - unsigned short filename_line; - } rule; - struct { - enum token_type type:8; - enum operation_type op:8; - enum string_glob_type glob:8; - enum string_subst_type subst:4; - enum string_subst_type attrsubst:4; - unsigned int value_off; - union { - unsigned int attr_off; - int devlink_unique; - unsigned int rule_goto; - mode_t mode; - uid_t uid; - gid_t gid; - int devlink_prio; - int event_timeout; - int watch; - enum udev_builtin_cmd builtin_cmd; - }; - } key; - }; -}; - -#define MAX_TK 64 -struct rule_tmp { - struct udev_rules *rules; - struct token rule; - struct token token[MAX_TK]; - unsigned int token_cur; -}; - -#ifdef ENABLE_DEBUG -static const char *operation_str(enum operation_type type) -{ - static const char *operation_strs[] = { - [OP_UNSET] = "UNSET", - [OP_MATCH] = "match", - [OP_NOMATCH] = "nomatch", - [OP_MATCH_MAX] = "MATCH_MAX", - - [OP_ADD] = "add", - [OP_ASSIGN] = "assign", - [OP_ASSIGN_FINAL] = "assign-final", -} ; - - return operation_strs[type]; -} - -static const char *string_glob_str(enum string_glob_type type) -{ - static const char *string_glob_strs[] = { - [GL_UNSET] = "UNSET", - [GL_PLAIN] = "plain", - [GL_GLOB] = "glob", - [GL_SPLIT] = "split", - [GL_SPLIT_GLOB] = "split-glob", - [GL_SOMETHING] = "split-glob", - }; - - return string_glob_strs[type]; -} - -static const char *token_str(enum token_type type) -{ - static const char *token_strs[] = { - [TK_UNSET] = "UNSET", - [TK_RULE] = "RULE", - - [TK_M_ACTION] = "M ACTION", - [TK_M_DEVPATH] = "M DEVPATH", - [TK_M_KERNEL] = "M KERNEL", - [TK_M_DEVLINK] = "M DEVLINK", - [TK_M_NAME] = "M NAME", - [TK_M_ENV] = "M ENV", - [TK_M_TAG] = "M TAG", - [TK_M_SUBSYSTEM] = "M SUBSYSTEM", - [TK_M_DRIVER] = "M DRIVER", - [TK_M_WAITFOR] = "M WAITFOR", - [TK_M_ATTR] = "M ATTR", - - [TK_M_PARENTS_MIN] = "M PARENTS_MIN", - [TK_M_KERNELS] = "M KERNELS", - [TK_M_SUBSYSTEMS] = "M SUBSYSTEMS", - [TK_M_DRIVERS] = "M DRIVERS", - [TK_M_ATTRS] = "M ATTRS", - [TK_M_TAGS] = "M TAGS", - [TK_M_PARENTS_MAX] = "M PARENTS_MAX", - - [TK_M_TEST] = "M TEST", - [TK_M_EVENT_TIMEOUT] = "M EVENT_TIMEOUT", - [TK_M_PROGRAM] = "M PROGRAM", - [TK_M_IMPORT_FILE] = "M IMPORT_FILE", - [TK_M_IMPORT_PROG] = "M IMPORT_PROG", - [TK_M_IMPORT_BUILTIN] = "M IMPORT_BUILTIN", - [TK_M_IMPORT_DB] = "M IMPORT_DB", - [TK_M_IMPORT_CMDLINE] = "M IMPORT_CMDLINE", - [TK_M_IMPORT_PARENT] = "M IMPORT_PARENT", - [TK_M_RESULT] = "M RESULT", - [TK_M_MAX] = "M MAX", - - [TK_A_STRING_ESCAPE_NONE] = "A STRING_ESCAPE_NONE", - [TK_A_STRING_ESCAPE_REPLACE] = "A STRING_ESCAPE_REPLACE", - [TK_A_DB_PERSIST] = "A DB_PERSIST", - [TK_A_INOTIFY_WATCH] = "A INOTIFY_WATCH", - [TK_A_DEVLINK_PRIO] = "A DEVLINK_PRIO", - [TK_A_OWNER] = "A OWNER", - [TK_A_GROUP] = "A GROUP", - [TK_A_MODE] = "A MODE", - [TK_A_OWNER_ID] = "A OWNER_ID", - [TK_A_GROUP_ID] = "A GROUP_ID", - [TK_A_STATIC_NODE] = "A STATIC_NODE", - [TK_A_MODE_ID] = "A MODE_ID", - [TK_A_ENV] = "A ENV", - [TK_A_TAG] = "A ENV", - [TK_A_NAME] = "A NAME", - [TK_A_DEVLINK] = "A DEVLINK", - [TK_A_ATTR] = "A ATTR", - [TK_A_RUN] = "A RUN", - [TK_A_GOTO] = "A GOTO", - - [TK_END] = "END", - }; - - return token_strs[type]; -} - -static void dump_token(struct udev_rules *rules, struct token *token) -{ - enum token_type type = token->type; - enum operation_type op = token->key.op; - enum string_glob_type glob = token->key.glob; - const char *value = &rules->buf[token->key.value_off]; - const char *attr = &rules->buf[token->key.attr_off]; - - switch (type) { - case TK_RULE: - { - const char *tks_ptr = (char *)rules->tokens; - const char *tk_ptr = (char *)token; - unsigned int idx = (tk_ptr - tks_ptr) / sizeof(struct token); - - dbg(rules->udev, "* RULE %s:%u, token: %u, count: %u, label: '%s'\n", - &rules->buf[token->rule.filename_off], token->rule.filename_line, - idx, token->rule.token_count, - &rules->buf[token->rule.label_off]); - break; - } - case TK_M_ACTION: - case TK_M_DEVPATH: - case TK_M_KERNEL: - case TK_M_SUBSYSTEM: - case TK_M_DRIVER: - case TK_M_WAITFOR: - case TK_M_DEVLINK: - case TK_M_NAME: - case TK_M_KERNELS: - case TK_M_SUBSYSTEMS: - case TK_M_DRIVERS: - case TK_M_TAGS: - case TK_M_PROGRAM: - case TK_M_IMPORT_FILE: - case TK_M_IMPORT_PROG: - case TK_M_IMPORT_DB: - case TK_M_IMPORT_CMDLINE: - case TK_M_IMPORT_PARENT: - case TK_M_RESULT: - case TK_A_NAME: - case TK_A_DEVLINK: - case TK_A_OWNER: - case TK_A_GROUP: - case TK_A_MODE: - case TK_A_RUN: - dbg(rules->udev, "%s %s '%s'(%s)\n", - token_str(type), operation_str(op), value, string_glob_str(glob)); - break; - case TK_M_IMPORT_BUILTIN: - dbg(rules->udev, "%s %i '%s'\n", token_str(type), token->key.builtin_cmd, value); - break; - case TK_M_ATTR: - case TK_M_ATTRS: - case TK_M_ENV: - case TK_A_ATTR: - case TK_A_ENV: - dbg(rules->udev, "%s %s '%s' '%s'(%s)\n", - token_str(type), operation_str(op), attr, value, string_glob_str(glob)); - break; - case TK_M_TAG: - case TK_A_TAG: - dbg(rules->udev, "%s %s '%s'\n", token_str(type), operation_str(op), value); - break; - case TK_A_STRING_ESCAPE_NONE: - case TK_A_STRING_ESCAPE_REPLACE: - case TK_A_DB_PERSIST: - dbg(rules->udev, "%s\n", token_str(type)); - break; - case TK_M_TEST: - dbg(rules->udev, "%s %s '%s'(%s) %#o\n", - token_str(type), operation_str(op), value, string_glob_str(glob), token->key.mode); - break; - case TK_A_INOTIFY_WATCH: - dbg(rules->udev, "%s %u\n", token_str(type), token->key.watch); - break; - case TK_A_DEVLINK_PRIO: - dbg(rules->udev, "%s %u\n", token_str(type), token->key.devlink_prio); - break; - case TK_A_OWNER_ID: - dbg(rules->udev, "%s %s %u\n", token_str(type), operation_str(op), token->key.uid); - break; - case TK_A_GROUP_ID: - dbg(rules->udev, "%s %s %u\n", token_str(type), operation_str(op), token->key.gid); - break; - case TK_A_MODE_ID: - dbg(rules->udev, "%s %s %#o\n", token_str(type), operation_str(op), token->key.mode); - break; - case TK_A_STATIC_NODE: - dbg(rules->udev, "%s '%s'\n", token_str(type), value); - break; - case TK_M_EVENT_TIMEOUT: - dbg(rules->udev, "%s %u\n", token_str(type), token->key.event_timeout); - break; - case TK_A_GOTO: - dbg(rules->udev, "%s '%s' %u\n", token_str(type), value, token->key.rule_goto); - break; - case TK_END: - dbg(rules->udev, "* %s\n", token_str(type)); - break; - case TK_M_PARENTS_MIN: - case TK_M_PARENTS_MAX: - case TK_M_MAX: - case TK_UNSET: - dbg(rules->udev, "unknown type %u\n", type); - break; - } -} - -static void dump_rules(struct udev_rules *rules) -{ - unsigned int i; - - dbg(rules->udev, "dumping %u (%zu bytes) tokens, %u (%zu bytes) strings\n", - rules->token_cur, - rules->token_cur * sizeof(struct token), - rules->buf_count, - rules->buf_cur); - for(i = 0; i < rules->token_cur; i++) - dump_token(rules, &rules->tokens[i]); -} -#else -static inline const char *operation_str(enum operation_type type) { return NULL; } -static inline const char *token_str(enum token_type type) { return NULL; } -static inline void dump_token(struct udev_rules *rules, struct token *token) {} -static inline void dump_rules(struct udev_rules *rules) {} -#endif /* ENABLE_DEBUG */ - -static int add_new_string(struct udev_rules *rules, const char *str, size_t bytes) -{ - int off; - - /* grow buffer if needed */ - if (rules->buf_cur + bytes+1 >= rules->buf_max) { - char *buf; - unsigned int add; - - /* double the buffer size */ - add = rules->buf_max; - if (add < bytes * 8) - add = bytes * 8; - - buf = realloc(rules->buf, rules->buf_max + add); - if (buf == NULL) - return -1; - dbg(rules->udev, "extend buffer from %zu to %zu\n", rules->buf_max, rules->buf_max + add); - rules->buf = buf; - rules->buf_max += add; - } - off = rules->buf_cur; - memcpy(&rules->buf[rules->buf_cur], str, bytes); - rules->buf_cur += bytes; - rules->buf_count++; - return off; -} - -static int add_string(struct udev_rules *rules, const char *str) -{ - unsigned int node_idx; - struct trie_node *new_node; - unsigned int new_node_idx; - unsigned char key; - unsigned short len; - unsigned int depth; - unsigned int off; - struct trie_node *parent; - - /* walk trie, start from last character of str to find matching tails */ - len = strlen(str); - key = str[len-1]; - node_idx = 0; - for (depth = 0; depth <= len; depth++) { - struct trie_node *node; - unsigned int child_idx; - - node = &rules->trie_nodes[node_idx]; - off = node->value_off + node->value_len - len; - - /* match against current node */ - if (depth == len || (node->value_len >= len && memcmp(&rules->buf[off], str, len) == 0)) - return off; - - /* lookup child node */ - key = str[len - 1 - depth]; - child_idx = node->child_idx; - while (child_idx > 0) { - struct trie_node *child; - - child = &rules->trie_nodes[child_idx]; - if (child->key == key) - break; - child_idx = child->next_child_idx; - } - if (child_idx == 0) - break; - node_idx = child_idx; - } - - /* string not found, add it */ - off = add_new_string(rules, str, len + 1); - - /* grow trie nodes if needed */ - if (rules->trie_nodes_cur >= rules->trie_nodes_max) { - struct trie_node *nodes; - unsigned int add; - - /* double the buffer size */ - add = rules->trie_nodes_max; - if (add < 8) - add = 8; - - nodes = realloc(rules->trie_nodes, (rules->trie_nodes_max + add) * sizeof(struct trie_node)); - if (nodes == NULL) - return -1; - dbg(rules->udev, "extend trie nodes from %u to %u\n", - rules->trie_nodes_max, rules->trie_nodes_max + add); - rules->trie_nodes = nodes; - rules->trie_nodes_max += add; - } - - /* get a new node */ - new_node_idx = rules->trie_nodes_cur; - rules->trie_nodes_cur++; - new_node = &rules->trie_nodes[new_node_idx]; - memset(new_node, 0x00, sizeof(struct trie_node)); - new_node->value_off = off; - new_node->value_len = len; - new_node->key = key; - - /* join the parent's child list */ - parent = &rules->trie_nodes[node_idx]; - if (parent->child_idx == 0) { - parent->child_idx = new_node_idx; - } else { - struct trie_node *last_child; - - last_child = &rules->trie_nodes[parent->last_child_idx]; - last_child->next_child_idx = new_node_idx; - } - parent->last_child_idx = new_node_idx; - return off; -} - -static int add_token(struct udev_rules *rules, struct token *token) -{ - /* grow buffer if needed */ - if (rules->token_cur+1 >= rules->token_max) { - struct token *tokens; - unsigned int add; - - /* double the buffer size */ - add = rules->token_max; - if (add < 8) - add = 8; - - tokens = realloc(rules->tokens, (rules->token_max + add ) * sizeof(struct token)); - if (tokens == NULL) - return -1; - dbg(rules->udev, "extend tokens from %u to %u\n", rules->token_max, rules->token_max + add); - rules->tokens = tokens; - rules->token_max += add; - } - memcpy(&rules->tokens[rules->token_cur], token, sizeof(struct token)); - rules->token_cur++; - return 0; -} - -static uid_t add_uid(struct udev_rules *rules, const char *owner) -{ - unsigned int i; - uid_t uid; - unsigned int off; - - /* lookup, if we know it already */ - for (i = 0; i < rules->uids_cur; i++) { - off = rules->uids[i].name_off; - if (strcmp(&rules->buf[off], owner) == 0) { - uid = rules->uids[i].uid; - dbg(rules->udev, "return existing %u for '%s'\n", uid, owner); - return uid; - } - } - uid = util_lookup_user(rules->udev, owner); - - /* grow buffer if needed */ - if (rules->uids_cur+1 >= rules->uids_max) { - struct uid_gid *uids; - unsigned int add; - - /* double the buffer size */ - add = rules->uids_max; - if (add < 1) - add = 8; - - uids = realloc(rules->uids, (rules->uids_max + add ) * sizeof(struct uid_gid)); - if (uids == NULL) - return uid; - dbg(rules->udev, "extend uids from %u to %u\n", rules->uids_max, rules->uids_max + add); - rules->uids = uids; - rules->uids_max += add; - } - rules->uids[rules->uids_cur].uid = uid; - off = add_string(rules, owner); - if (off <= 0) - return uid; - rules->uids[rules->uids_cur].name_off = off; - rules->uids_cur++; - return uid; -} - -static gid_t add_gid(struct udev_rules *rules, const char *group) -{ - unsigned int i; - gid_t gid; - unsigned int off; - - /* lookup, if we know it already */ - for (i = 0; i < rules->gids_cur; i++) { - off = rules->gids[i].name_off; - if (strcmp(&rules->buf[off], group) == 0) { - gid = rules->gids[i].gid; - dbg(rules->udev, "return existing %u for '%s'\n", gid, group); - return gid; - } - } - gid = util_lookup_group(rules->udev, group); - - /* grow buffer if needed */ - if (rules->gids_cur+1 >= rules->gids_max) { - struct uid_gid *gids; - unsigned int add; - - /* double the buffer size */ - add = rules->gids_max; - if (add < 1) - add = 8; - - gids = realloc(rules->gids, (rules->gids_max + add ) * sizeof(struct uid_gid)); - if (gids == NULL) - return gid; - dbg(rules->udev, "extend gids from %u to %u\n", rules->gids_max, rules->gids_max + add); - rules->gids = gids; - rules->gids_max += add; - } - rules->gids[rules->gids_cur].gid = gid; - off = add_string(rules, group); - if (off <= 0) - return gid; - rules->gids[rules->gids_cur].name_off = off; - rules->gids_cur++; - return gid; -} - -static int import_property_from_string(struct udev_device *dev, char *line) -{ - struct udev *udev = udev_device_get_udev(dev); - char *key; - char *val; - size_t len; - - /* find key */ - key = line; - while (isspace(key[0])) - key++; - - /* comment or empty line */ - if (key[0] == '#' || key[0] == '\0') - return -1; - - /* split key/value */ - val = strchr(key, '='); - if (val == NULL) - return -1; - val[0] = '\0'; - val++; - - /* find value */ - while (isspace(val[0])) - val++; - - /* terminate key */ - len = strlen(key); - if (len == 0) - return -1; - while (isspace(key[len-1])) - len--; - key[len] = '\0'; - - /* terminate value */ - len = strlen(val); - if (len == 0) - return -1; - while (isspace(val[len-1])) - len--; - val[len] = '\0'; - - if (len == 0) - return -1; - - /* unquote */ - if (val[0] == '"' || val[0] == '\'') { - if (val[len-1] != val[0]) { - info(udev, "inconsistent quoting: '%s', skip\n", line); - return -1; - } - val[len-1] = '\0'; - val++; - } - - dbg(udev, "adding '%s'='%s'\n", key, val); - - /* handle device, renamed by external tool, returning new path */ - if (strcmp(key, "DEVPATH") == 0) { - char syspath[UTIL_PATH_SIZE]; - - info(udev, "updating devpath from '%s' to '%s'\n", - udev_device_get_devpath(dev), val); - util_strscpyl(syspath, sizeof(syspath), udev_get_sys_path(udev), val, NULL); - udev_device_set_syspath(dev, syspath); - } else { - struct udev_list_entry *entry; - - entry = udev_device_add_property(dev, key, val); - /* store in db, skip private keys */ - if (key[0] != '.') - udev_list_entry_set_num(entry, true); - } - return 0; -} - -static int import_file_into_properties(struct udev_device *dev, const char *filename) -{ - FILE *f; - char line[UTIL_LINE_SIZE]; - - f = fopen(filename, "r"); - if (f == NULL) - return -1; - while (fgets(line, sizeof(line), f) != NULL) - import_property_from_string(dev, line); - fclose(f); - return 0; -} - -static int import_program_into_properties(struct udev_event *event, const char *program, const sigset_t *sigmask) -{ - struct udev_device *dev = event->dev; - char **envp; - char result[UTIL_LINE_SIZE]; - char *line; - int err; - - envp = udev_device_get_properties_envp(dev); - err = udev_event_spawn(event, program, envp, sigmask, result, sizeof(result)); - if (err < 0) - return err; - - line = result; - while (line != NULL) { - char *pos; - - pos = strchr(line, '\n'); - if (pos != NULL) { - pos[0] = '\0'; - pos = &pos[1]; - } - import_property_from_string(dev, line); - line = pos; - } - return 0; -} - -static int import_parent_into_properties(struct udev_device *dev, const char *filter) -{ - struct udev *udev = udev_device_get_udev(dev); - struct udev_device *dev_parent; - struct udev_list_entry *list_entry; - - dev_parent = udev_device_get_parent(dev); - if (dev_parent == NULL) - return -1; - - dbg(udev, "found parent '%s', get the node name\n", udev_device_get_syspath(dev_parent)); - udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(dev_parent)) { - const char *key = udev_list_entry_get_name(list_entry); - const char *val = udev_list_entry_get_value(list_entry); - - if (fnmatch(filter, key, 0) == 0) { - struct udev_list_entry *entry; - - dbg(udev, "import key '%s=%s'\n", key, val); - entry = udev_device_add_property(dev, key, val); - /* store in db, skip private keys */ - if (key[0] != '.') - udev_list_entry_set_num(entry, true); - } - } - return 0; -} - -#define WAIT_LOOP_PER_SECOND 50 -static int wait_for_file(struct udev_device *dev, const char *file, int timeout) -{ - struct udev *udev = udev_device_get_udev(dev); - char filepath[UTIL_PATH_SIZE]; - char devicepath[UTIL_PATH_SIZE]; - struct stat stats; - int loop = timeout * WAIT_LOOP_PER_SECOND; - - /* a relative path is a device attribute */ - devicepath[0] = '\0'; - if (file[0] != '/') { - util_strscpyl(devicepath, sizeof(devicepath), - udev_get_sys_path(udev), udev_device_get_devpath(dev), NULL); - util_strscpyl(filepath, sizeof(filepath), devicepath, "/", file, NULL); - file = filepath; - } - - dbg(udev, "will wait %i sec for '%s'\n", timeout, file); - while (--loop) { - const struct timespec duration = { 0, 1000 * 1000 * 1000 / WAIT_LOOP_PER_SECOND }; - - /* lookup file */ - if (stat(file, &stats) == 0) { - info(udev, "file '%s' appeared after %i loops\n", file, (timeout * WAIT_LOOP_PER_SECOND) - loop-1); - return 0; - } - /* make sure, the device did not disappear in the meantime */ - if (devicepath[0] != '\0' && stat(devicepath, &stats) != 0) { - info(udev, "device disappeared while waiting for '%s'\n", file); - return -2; - } - info(udev, "wait for '%s' for %i mseconds\n", file, 1000 / WAIT_LOOP_PER_SECOND); - nanosleep(&duration, NULL); - } - info(udev, "waiting for '%s' failed\n", file); - return -1; -} - -static int attr_subst_subdir(char *attr, size_t len) -{ - bool found = false; - - if (strstr(attr, "/*/")) { - char *pos; - char dirname[UTIL_PATH_SIZE]; - const char *tail; - DIR *dir; - - util_strscpy(dirname, sizeof(dirname), attr); - pos = strstr(dirname, "/*/"); - if (pos == NULL) - return -1; - pos[0] = '\0'; - tail = &pos[2]; - dir = opendir(dirname); - if (dir != NULL) { - struct dirent *dent; - - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - struct stat stats; - - if (dent->d_name[0] == '.') - continue; - util_strscpyl(attr, len, dirname, "/", dent->d_name, tail, NULL); - if (stat(attr, &stats) == 0) { - found = true; - break; - } - } - closedir(dir); - } - } - - return found; -} - -static int get_key(struct udev *udev, char **line, char **key, enum operation_type *op, char **value) -{ - char *linepos; - char *temp; - - linepos = *line; - if (linepos == NULL || linepos[0] == '\0') - return -1; - - /* skip whitespace */ - while (isspace(linepos[0]) || linepos[0] == ',') - linepos++; - - /* get the key */ - if (linepos[0] == '\0') - return -1; - *key = linepos; - - for (;;) { - linepos++; - if (linepos[0] == '\0') - return -1; - if (isspace(linepos[0])) - break; - if (linepos[0] == '=') - break; - if ((linepos[0] == '+') || (linepos[0] == '!') || (linepos[0] == ':')) - if (linepos[1] == '=') - break; - } - - /* remember end of key */ - temp = linepos; - - /* skip whitespace after key */ - while (isspace(linepos[0])) - linepos++; - if (linepos[0] == '\0') - return -1; - - /* get operation type */ - if (linepos[0] == '=' && linepos[1] == '=') { - *op = OP_MATCH; - linepos += 2; - } else if (linepos[0] == '!' && linepos[1] == '=') { - *op = OP_NOMATCH; - linepos += 2; - } else if (linepos[0] == '+' && linepos[1] == '=') { - *op = OP_ADD; - linepos += 2; - } else if (linepos[0] == '=') { - *op = OP_ASSIGN; - linepos++; - } else if (linepos[0] == ':' && linepos[1] == '=') { - *op = OP_ASSIGN_FINAL; - linepos += 2; - } else - return -1; - - /* terminate key */ - temp[0] = '\0'; - - /* skip whitespace after operator */ - while (isspace(linepos[0])) - linepos++; - if (linepos[0] == '\0') - return -1; - - /* get the value */ - if (linepos[0] == '"') - linepos++; - else - return -1; - *value = linepos; - - /* terminate */ - temp = strchr(linepos, '"'); - if (!temp) - return -1; - temp[0] = '\0'; - temp++; - dbg(udev, "%s '%s'-'%s'\n", operation_str(*op), *key, *value); - - /* move line to next key */ - *line = temp; - return 0; -} - -/* extract possible KEY{attr} */ -static char *get_key_attribute(struct udev *udev, char *str) -{ - char *pos; - char *attr; - - attr = strchr(str, '{'); - if (attr != NULL) { - attr++; - pos = strchr(attr, '}'); - if (pos == NULL) { - err(udev, "missing closing brace for format\n"); - return NULL; - } - pos[0] = '\0'; - dbg(udev, "attribute='%s'\n", attr); - return attr; - } - return NULL; -} - -static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type, - enum operation_type op, - const char *value, const void *data) -{ - struct token *token = &rule_tmp->token[rule_tmp->token_cur]; - const char *attr = NULL; - - memset(token, 0x00, sizeof(struct token)); - - switch (type) { - case TK_M_ACTION: - case TK_M_DEVPATH: - case TK_M_KERNEL: - case TK_M_SUBSYSTEM: - case TK_M_DRIVER: - case TK_M_WAITFOR: - case TK_M_DEVLINK: - case TK_M_NAME: - case TK_M_KERNELS: - case TK_M_SUBSYSTEMS: - case TK_M_DRIVERS: - case TK_M_TAGS: - case TK_M_PROGRAM: - case TK_M_IMPORT_FILE: - case TK_M_IMPORT_PROG: - case TK_M_IMPORT_DB: - case TK_M_IMPORT_CMDLINE: - case TK_M_IMPORT_PARENT: - case TK_M_RESULT: - case TK_A_OWNER: - case TK_A_GROUP: - case TK_A_MODE: - case TK_A_NAME: - case TK_A_GOTO: - case TK_M_TAG: - case TK_A_TAG: - token->key.value_off = add_string(rule_tmp->rules, value); - break; - case TK_M_IMPORT_BUILTIN: - token->key.value_off = add_string(rule_tmp->rules, value); - token->key.builtin_cmd = *(enum udev_builtin_cmd *)data; - break; - case TK_M_ENV: - case TK_M_ATTR: - case TK_M_ATTRS: - case TK_A_ATTR: - case TK_A_ENV: - attr = data; - token->key.value_off = add_string(rule_tmp->rules, value); - token->key.attr_off = add_string(rule_tmp->rules, attr); - break; - case TK_A_DEVLINK: - token->key.value_off = add_string(rule_tmp->rules, value); - token->key.devlink_unique = *(int *)data; - break; - case TK_M_TEST: - token->key.value_off = add_string(rule_tmp->rules, value); - if (data != NULL) - token->key.mode = *(mode_t *)data; - break; - case TK_A_STRING_ESCAPE_NONE: - case TK_A_STRING_ESCAPE_REPLACE: - case TK_A_DB_PERSIST: - break; - case TK_A_RUN: - token->key.value_off = add_string(rule_tmp->rules, value); - break; - case TK_A_INOTIFY_WATCH: - case TK_A_DEVLINK_PRIO: - token->key.devlink_prio = *(int *)data; - break; - case TK_A_OWNER_ID: - token->key.uid = *(uid_t *)data; - break; - case TK_A_GROUP_ID: - token->key.gid = *(gid_t *)data; - break; - case TK_A_MODE_ID: - token->key.mode = *(mode_t *)data; - break; - case TK_A_STATIC_NODE: - token->key.value_off = add_string(rule_tmp->rules, value); - break; - case TK_M_EVENT_TIMEOUT: - token->key.event_timeout = *(int *)data; - break; - case TK_RULE: - case TK_M_PARENTS_MIN: - case TK_M_PARENTS_MAX: - case TK_M_MAX: - case TK_END: - case TK_UNSET: - err(rule_tmp->rules->udev, "wrong type %u\n", type); - return -1; - } - - if (value != NULL && type < TK_M_MAX) { - /* check if we need to split or call fnmatch() while matching rules */ - enum string_glob_type glob; - int has_split; - int has_glob; - - has_split = (strchr(value, '|') != NULL); - has_glob = (strchr(value, '*') != NULL || strchr(value, '?') != NULL || strchr(value, '[') != NULL); - if (has_split && has_glob) { - glob = GL_SPLIT_GLOB; - } else if (has_split) { - glob = GL_SPLIT; - } else if (has_glob) { - if (strcmp(value, "?*") == 0) - glob = GL_SOMETHING; - else - glob = GL_GLOB; - } else { - glob = GL_PLAIN; - } - token->key.glob = glob; - } - - if (value != NULL && type > TK_M_MAX) { - /* check if assigned value has substitution chars */ - if (value[0] == '[') - token->key.subst = SB_SUBSYS; - else if (strchr(value, '%') != NULL || strchr(value, '$') != NULL) - token->key.subst = SB_FORMAT; - else - token->key.subst = SB_NONE; - } - - if (attr != NULL) { - /* check if property/attribut name has substitution chars */ - if (attr[0] == '[') - token->key.attrsubst = SB_SUBSYS; - else if (strchr(attr, '%') != NULL || strchr(attr, '$') != NULL) - token->key.attrsubst = SB_FORMAT; - else - token->key.attrsubst = SB_NONE; - } - - token->key.type = type; - token->key.op = op; - rule_tmp->token_cur++; - if (rule_tmp->token_cur >= ARRAY_SIZE(rule_tmp->token)) { - err(rule_tmp->rules->udev, "temporary rule array too small\n"); - return -1; - } - return 0; -} - -static int sort_token(struct udev_rules *rules, struct rule_tmp *rule_tmp) -{ - unsigned int i; - unsigned int start = 0; - unsigned int end = rule_tmp->token_cur; - - for (i = 0; i < rule_tmp->token_cur; i++) { - enum token_type next_val = TK_UNSET; - unsigned int next_idx = 0; - unsigned int j; - - /* find smallest value */ - for (j = start; j < end; j++) { - if (rule_tmp->token[j].type == TK_UNSET) - continue; - if (next_val == TK_UNSET || rule_tmp->token[j].type < next_val) { - next_val = rule_tmp->token[j].type; - next_idx = j; - } - } - - /* add token and mark done */ - if (add_token(rules, &rule_tmp->token[next_idx]) != 0) - return -1; - rule_tmp->token[next_idx].type = TK_UNSET; - - /* shrink range */ - if (next_idx == start) - start++; - if (next_idx+1 == end) - end--; - } - return 0; -} - -static int add_rule(struct udev_rules *rules, char *line, - const char *filename, unsigned int filename_off, unsigned int lineno) -{ - char *linepos; - char *attr; - struct rule_tmp rule_tmp; - - memset(&rule_tmp, 0x00, sizeof(struct rule_tmp)); - rule_tmp.rules = rules; - rule_tmp.rule.type = TK_RULE; - rule_tmp.rule.rule.filename_off = filename_off; - rule_tmp.rule.rule.filename_line = lineno; - - linepos = line; - for (;;) { - char *key; - char *value; - enum operation_type op; - - if (get_key(rules->udev, &linepos, &key, &op, &value) != 0) - break; - - if (strcmp(key, "ACTION") == 0) { - if (op > OP_MATCH_MAX) { - err(rules->udev, "invalid ACTION operation\n"); - goto invalid; - } - rule_add_key(&rule_tmp, TK_M_ACTION, op, value, NULL); - continue; - } - - if (strcmp(key, "DEVPATH") == 0) { - if (op > OP_MATCH_MAX) { - err(rules->udev, "invalid DEVPATH operation\n"); - goto invalid; - } - rule_add_key(&rule_tmp, TK_M_DEVPATH, op, value, NULL); - continue; - } - - if (strcmp(key, "KERNEL") == 0) { - if (op > OP_MATCH_MAX) { - err(rules->udev, "invalid KERNEL operation\n"); - goto invalid; - } - rule_add_key(&rule_tmp, TK_M_KERNEL, op, value, NULL); - continue; - } - - if (strcmp(key, "SUBSYSTEM") == 0) { - if (op > OP_MATCH_MAX) { - err(rules->udev, "invalid SUBSYSTEM operation\n"); - goto invalid; - } - /* bus, class, subsystem events should all be the same */ - if (strcmp(value, "subsystem") == 0 || - strcmp(value, "bus") == 0 || - strcmp(value, "class") == 0) { - if (strcmp(value, "bus") == 0 || strcmp(value, "class") == 0) - err(rules->udev, "'%s' must be specified as 'subsystem' \n" - "please fix it in %s:%u", value, filename, lineno); - rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, "subsystem|class|bus", NULL); - } else - rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, value, NULL); - continue; - } - - if (strcmp(key, "DRIVER") == 0) { - if (op > OP_MATCH_MAX) { - err(rules->udev, "invalid DRIVER operation\n"); - goto invalid; - } - rule_add_key(&rule_tmp, TK_M_DRIVER, op, value, NULL); - continue; - } - - if (strncmp(key, "ATTR{", sizeof("ATTR{")-1) == 0) { - attr = get_key_attribute(rules->udev, key + sizeof("ATTR")-1); - if (attr == NULL) { - err(rules->udev, "error parsing ATTR attribute\n"); - goto invalid; - } - if (op < OP_MATCH_MAX) { - rule_add_key(&rule_tmp, TK_M_ATTR, op, value, attr); - } else { - rule_add_key(&rule_tmp, TK_A_ATTR, op, value, attr); - } - continue; - } - - if (strcmp(key, "KERNELS") == 0) { - if (op > OP_MATCH_MAX) { - err(rules->udev, "invalid KERNELS operation\n"); - goto invalid; - } - rule_add_key(&rule_tmp, TK_M_KERNELS, op, value, NULL); - continue; - } - - if (strcmp(key, "SUBSYSTEMS") == 0) { - if (op > OP_MATCH_MAX) { - err(rules->udev, "invalid SUBSYSTEMS operation\n"); - goto invalid; - } - rule_add_key(&rule_tmp, TK_M_SUBSYSTEMS, op, value, NULL); - continue; - } - - if (strcmp(key, "DRIVERS") == 0) { - if (op > OP_MATCH_MAX) { - err(rules->udev, "invalid DRIVERS operation\n"); - goto invalid; - } - rule_add_key(&rule_tmp, TK_M_DRIVERS, op, value, NULL); - continue; - } - - if (strncmp(key, "ATTRS{", sizeof("ATTRS{")-1) == 0) { - if (op > OP_MATCH_MAX) { - err(rules->udev, "invalid ATTRS operation\n"); - goto invalid; - } - attr = get_key_attribute(rules->udev, key + sizeof("ATTRS")-1); - if (attr == NULL) { - err(rules->udev, "error parsing ATTRS attribute\n"); - goto invalid; - } - if (strncmp(attr, "device/", 7) == 0) - err(rules->udev, "the 'device' link may not be available in a future kernel, " - "please fix it in %s:%u", filename, lineno); - else if (strstr(attr, "../") != NULL) - err(rules->udev, "do not reference parent sysfs directories directly, " - "it may break with a future kernel, please fix it in %s:%u", filename, lineno); - rule_add_key(&rule_tmp, TK_M_ATTRS, op, value, attr); - continue; - } - - if (strcmp(key, "TAGS") == 0) { - if (op > OP_MATCH_MAX) { - err(rules->udev, "invalid TAGS operation\n"); - goto invalid; - } - rule_add_key(&rule_tmp, TK_M_TAGS, op, value, NULL); - continue; - } - - if (strncmp(key, "ENV{", sizeof("ENV{")-1) == 0) { - attr = get_key_attribute(rules->udev, key + sizeof("ENV")-1); - if (attr == NULL) { - err(rules->udev, "error parsing ENV attribute\n"); - goto invalid; - } - if (op < OP_MATCH_MAX) { - if (rule_add_key(&rule_tmp, TK_M_ENV, op, value, attr) != 0) - goto invalid; - } else { - static const char *blacklist[] = { - "ACTION", - "SUBSYSTEM", - "DEVTYPE", - "MAJOR", - "MINOR", - "DRIVER", - "IFINDEX", - "DEVNAME", - "DEVLINKS", - "DEVPATH", - "TAGS", - }; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(blacklist); i++) - if (strcmp(attr, blacklist[i]) == 0) { - err(rules->udev, "invalid ENV attribute, '%s' can not be set %s:%u\n", attr, filename, lineno); - continue; - } - if (rule_add_key(&rule_tmp, TK_A_ENV, op, value, attr) != 0) - goto invalid; - } - continue; - } - - if (strcmp(key, "TAG") == 0) { - if (op < OP_MATCH_MAX) - rule_add_key(&rule_tmp, TK_M_TAG, op, value, NULL); - else - rule_add_key(&rule_tmp, TK_A_TAG, op, value, NULL); - continue; - } - - if (strcmp(key, "PROGRAM") == 0) { - rule_add_key(&rule_tmp, TK_M_PROGRAM, op, value, NULL); - continue; - } - - if (strcmp(key, "RESULT") == 0) { - if (op > OP_MATCH_MAX) { - err(rules->udev, "invalid RESULT operation\n"); - goto invalid; - } - rule_add_key(&rule_tmp, TK_M_RESULT, op, value, NULL); - continue; - } - - if (strncmp(key, "IMPORT", sizeof("IMPORT")-1) == 0) { - attr = get_key_attribute(rules->udev, key + sizeof("IMPORT")-1); - if (attr == NULL) { - err(rules->udev, "IMPORT{} type missing, ignoring IMPORT %s:%u\n", filename, lineno); - continue; - } - if (strstr(attr, "program")) { - /* find known built-in command */ - if (value[0] != '/') { - enum udev_builtin_cmd cmd; - - cmd = udev_builtin_lookup(value); - if (cmd < UDEV_BUILTIN_MAX) { - info(rules->udev, "IMPORT found builtin '%s', replacing %s:%u\n", - value, filename, lineno); - rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd); - continue; - } - } - dbg(rules->udev, "IMPORT will be executed\n"); - rule_add_key(&rule_tmp, TK_M_IMPORT_PROG, op, value, NULL); - } else if (strstr(attr, "builtin")) { - enum udev_builtin_cmd cmd = udev_builtin_lookup(value); - - dbg(rules->udev, "IMPORT execute builtin\n"); - if (cmd < UDEV_BUILTIN_MAX) - rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd); - else - err(rules->udev, "IMPORT{builtin}: '%s' unknown %s:%u\n", value, filename, lineno); - } else if (strstr(attr, "file")) { - dbg(rules->udev, "IMPORT will be included as file\n"); - rule_add_key(&rule_tmp, TK_M_IMPORT_FILE, op, value, NULL); - } else if (strstr(attr, "db")) { - dbg(rules->udev, "IMPORT will include db values\n"); - rule_add_key(&rule_tmp, TK_M_IMPORT_DB, op, value, NULL); - } else if (strstr(attr, "cmdline")) { - dbg(rules->udev, "IMPORT will include db values\n"); - rule_add_key(&rule_tmp, TK_M_IMPORT_CMDLINE, op, value, NULL); - } else if (strstr(attr, "parent")) { - dbg(rules->udev, "IMPORT will include the parent values\n"); - rule_add_key(&rule_tmp, TK_M_IMPORT_PARENT, op, value, NULL); - } - continue; - } - - if (strncmp(key, "TEST", sizeof("TEST")-1) == 0) { - mode_t mode = 0; - - if (op > OP_MATCH_MAX) { - err(rules->udev, "invalid TEST operation\n"); - goto invalid; - } - attr = get_key_attribute(rules->udev, key + sizeof("TEST")-1); - if (attr != NULL) { - mode = strtol(attr, NULL, 8); - rule_add_key(&rule_tmp, TK_M_TEST, op, value, &mode); - } else { - rule_add_key(&rule_tmp, TK_M_TEST, op, value, NULL); - } - continue; - } - - if (strcmp(key, "RUN") == 0) { - if (strncmp(value, "socket:", 7) == 0) - err(rules->udev, "RUN+=\"socket:...\" support will be removed from a future udev release. " - "Please remove it from: %s:%u and use libudev to subscribe to events.\n", filename, lineno); - rule_add_key(&rule_tmp, TK_A_RUN, op, value, NULL); - continue; - } - - if (strcmp(key, "WAIT_FOR") == 0 || strcmp(key, "WAIT_FOR_SYSFS") == 0) { - rule_add_key(&rule_tmp, TK_M_WAITFOR, 0, value, NULL); - continue; - } - - if (strcmp(key, "LABEL") == 0) { - rule_tmp.rule.rule.label_off = add_string(rules, value); - continue; - } - - if (strcmp(key, "GOTO") == 0) { - rule_add_key(&rule_tmp, TK_A_GOTO, 0, value, NULL); - continue; - } - - if (strncmp(key, "NAME", sizeof("NAME")-1) == 0) { - if (op < OP_MATCH_MAX) { - rule_add_key(&rule_tmp, TK_M_NAME, op, value, NULL); - } else { - if (strcmp(value, "%k") == 0) { - err(rules->udev, "NAME=\"%%k\" is ignored, because it breaks kernel supplied names, " - "please remove it from %s:%u\n", filename, lineno); - continue; - } - if (value[0] == '\0') { - info(rules->udev, "NAME=\"\" is ignored, because udev will not delete any device nodes, " - "please remove it from %s:%u\n", filename, lineno); - continue; - } - rule_add_key(&rule_tmp, TK_A_NAME, op, value, NULL); - } - rule_tmp.rule.rule.can_set_name = true; - continue; - } - - if (strncmp(key, "SYMLINK", sizeof("SYMLINK")-1) == 0) { - if (op < OP_MATCH_MAX) { - rule_add_key(&rule_tmp, TK_M_DEVLINK, op, value, NULL); - } else { - int flag = 0; - - attr = get_key_attribute(rules->udev, key + sizeof("SYMLINK")-1); - if (attr != NULL && strstr(attr, "unique") != NULL) - flag = 1; - rule_add_key(&rule_tmp, TK_A_DEVLINK, op, value, &flag); - } - rule_tmp.rule.rule.can_set_name = true; - continue; - } - - if (strcmp(key, "OWNER") == 0) { - uid_t uid; - char *endptr; - - uid = strtoul(value, &endptr, 10); - if (endptr[0] == '\0') { - rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid); - } else if ((rules->resolve_names > 0) && strchr("$%", value[0]) == NULL) { - uid = add_uid(rules, value); - rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid); - } else if (rules->resolve_names >= 0) { - rule_add_key(&rule_tmp, TK_A_OWNER, op, value, NULL); - } - rule_tmp.rule.rule.can_set_name = true; - continue; - } - - if (strcmp(key, "GROUP") == 0) { - gid_t gid; - char *endptr; - - gid = strtoul(value, &endptr, 10); - if (endptr[0] == '\0') { - rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid); - } else if ((rules->resolve_names > 0) && strchr("$%", value[0]) == NULL) { - gid = add_gid(rules, value); - rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid); - } else if (rules->resolve_names >= 0) { - rule_add_key(&rule_tmp, TK_A_GROUP, op, value, NULL); - } - rule_tmp.rule.rule.can_set_name = true; - continue; - } - - if (strcmp(key, "MODE") == 0) { - mode_t mode; - char *endptr; - - mode = strtol(value, &endptr, 8); - if (endptr[0] == '\0') - rule_add_key(&rule_tmp, TK_A_MODE_ID, op, NULL, &mode); - else - rule_add_key(&rule_tmp, TK_A_MODE, op, value, NULL); - rule_tmp.rule.rule.can_set_name = true; - continue; - } - - if (strcmp(key, "OPTIONS") == 0) { - const char *pos; - - pos = strstr(value, "link_priority="); - if (pos != NULL) { - int prio = atoi(&pos[strlen("link_priority=")]); - - rule_add_key(&rule_tmp, TK_A_DEVLINK_PRIO, op, NULL, &prio); - dbg(rules->udev, "link priority=%i\n", prio); - } - - pos = strstr(value, "event_timeout="); - if (pos != NULL) { - int tout = atoi(&pos[strlen("event_timeout=")]); - - rule_add_key(&rule_tmp, TK_M_EVENT_TIMEOUT, op, NULL, &tout); - dbg(rules->udev, "event timeout=%i\n", tout); - } - - pos = strstr(value, "string_escape="); - if (pos != NULL) { - pos = &pos[strlen("string_escape=")]; - if (strncmp(pos, "none", strlen("none")) == 0) - rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_NONE, op, NULL, NULL); - else if (strncmp(pos, "replace", strlen("replace")) == 0) - rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_REPLACE, op, NULL, NULL); - } - - pos = strstr(value, "db_persist"); - if (pos != NULL) - rule_add_key(&rule_tmp, TK_A_DB_PERSIST, op, NULL, NULL); - - pos = strstr(value, "nowatch"); - if (pos != NULL) { - const int off = 0; - - rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &off); - dbg(rules->udev, "inotify watch of device disabled\n"); - } else { - pos = strstr(value, "watch"); - if (pos != NULL) { - const int on = 1; - - rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &on); - dbg(rules->udev, "inotify watch of device requested\n"); - } - } - - pos = strstr(value, "static_node="); - if (pos != NULL) { - rule_add_key(&rule_tmp, TK_A_STATIC_NODE, op, &pos[strlen("static_node=")], NULL); - rule_tmp.rule.rule.has_static_node = true; - } - - continue; - } - - err(rules->udev, "unknown key '%s' in %s:%u\n", key, filename, lineno); - goto invalid; - } - - /* add rule token */ - rule_tmp.rule.rule.token_count = 1 + rule_tmp.token_cur; - if (add_token(rules, &rule_tmp.rule) != 0) - goto invalid; - - /* add tokens to list, sorted by type */ - if (sort_token(rules, &rule_tmp) != 0) - goto invalid; - - return 0; -invalid: - err(rules->udev, "invalid rule '%s:%u'\n", filename, lineno); - return -1; -} - -static int parse_file(struct udev_rules *rules, const char *filename, unsigned short filename_off) -{ - FILE *f; - unsigned int first_token; - char line[UTIL_LINE_SIZE]; - int line_nr = 0; - unsigned int i; - - info(rules->udev, "reading '%s' as rules file\n", filename); - - f = fopen(filename, "r"); - if (f == NULL) - return -1; - - first_token = rules->token_cur; - - while (fgets(line, sizeof(line), f) != NULL) { - char *key; - size_t len; - - /* skip whitespace */ - line_nr++; - key = line; - while (isspace(key[0])) - key++; - - /* comment */ - if (key[0] == '#') - continue; - - len = strlen(line); - if (len < 3) - continue; - - /* continue reading if backslash+newline is found */ - while (line[len-2] == '\\') { - if (fgets(&line[len-2], (sizeof(line)-len)+2, f) == NULL) - break; - if (strlen(&line[len-2]) < 2) - break; - line_nr++; - len = strlen(line); - } - - if (len+1 >= sizeof(line)) { - err(rules->udev, "line too long '%s':%u, ignored\n", filename, line_nr); - continue; - } - add_rule(rules, key, filename, filename_off, line_nr); - } - fclose(f); - - /* link GOTOs to LABEL rules in this file to be able to fast-forward */ - for (i = first_token+1; i < rules->token_cur; i++) { - if (rules->tokens[i].type == TK_A_GOTO) { - char *label = &rules->buf[rules->tokens[i].key.value_off]; - unsigned int j; - - for (j = i+1; j < rules->token_cur; j++) { - if (rules->tokens[j].type != TK_RULE) - continue; - if (rules->tokens[j].rule.label_off == 0) - continue; - if (strcmp(label, &rules->buf[rules->tokens[j].rule.label_off]) != 0) - continue; - rules->tokens[i].key.rule_goto = j; - break; - } - if (rules->tokens[i].key.rule_goto == 0) - err(rules->udev, "GOTO '%s' has no matching label in: '%s'\n", label, filename); - } - } - return 0; -} - -static int add_matching_files(struct udev *udev, struct udev_list *file_list, const char *dirname, const char *suffix) -{ - DIR *dir; - struct dirent *dent; - char filename[UTIL_PATH_SIZE]; - - dbg(udev, "open directory '%s'\n", dirname); - dir = opendir(dirname); - if (dir == NULL) { - info(udev, "unable to open '%s': %m\n", dirname); - return -1; - } - - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - if (dent->d_name[0] == '.') - continue; - - /* look for file matching with specified suffix */ - if (suffix != NULL) { - const char *ext; - - ext = strrchr(dent->d_name, '.'); - if (ext == NULL) - continue; - if (strcmp(ext, suffix) != 0) - continue; - } - util_strscpyl(filename, sizeof(filename), dirname, "/", dent->d_name, NULL); - dbg(udev, "put file '%s' into list\n", filename); - /* - * the basename is the key, the filename the value - * identical basenames from different directories override each other - * entries are sorted after basename - */ - udev_list_entry_add(file_list, dent->d_name, filename); - } - - closedir(dir); - return 0; -} - -struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names) -{ - struct udev_rules *rules; - struct udev_list file_list; - struct udev_list_entry *file_loop; - struct token end_token; - char **s; - - rules = calloc(1, sizeof(struct udev_rules)); - if (rules == NULL) - return NULL; - rules->udev = udev; - rules->resolve_names = resolve_names; - udev_list_init(udev, &file_list, true); - - /* init token array and string buffer */ - rules->tokens = malloc(PREALLOC_TOKEN * sizeof(struct token)); - if (rules->tokens == NULL) { - free(rules); - return NULL; - } - rules->token_max = PREALLOC_TOKEN; - - rules->buf = malloc(PREALLOC_STRBUF); - if (rules->buf == NULL) { - free(rules->tokens); - free(rules); - return NULL; - } - rules->buf_max = PREALLOC_STRBUF; - /* offset 0 is always '\0' */ - rules->buf[0] = '\0'; - rules->buf_cur = 1; - dbg(udev, "prealloc %zu bytes tokens (%u * %zu bytes), %zu bytes buffer\n", - rules->token_max * sizeof(struct token), rules->token_max, sizeof(struct token), rules->buf_max); - - rules->trie_nodes = malloc(PREALLOC_TRIE * sizeof(struct trie_node)); - if (rules->trie_nodes == NULL) { - free(rules->buf); - free(rules->tokens); - free(rules); - return NULL; - } - rules->trie_nodes_max = PREALLOC_TRIE; - /* offset 0 is the trie root, with an empty string */ - memset(rules->trie_nodes, 0x00, sizeof(struct trie_node)); - rules->trie_nodes_cur = 1; - - for (udev_get_rules_path(udev, &s, NULL); *s != NULL; s++) - add_matching_files(udev, &file_list, *s, ".rules"); - - /* add all filenames to the string buffer */ - udev_list_entry_foreach(file_loop, udev_list_get_entry(&file_list)) { - const char *filename = udev_list_entry_get_value(file_loop); - unsigned int filename_off; - - filename_off = add_string(rules, filename); - /* the offset in the rule is limited to unsigned short */ - if (filename_off < USHRT_MAX) - udev_list_entry_set_num(file_loop, filename_off); - } - - /* parse all rules files */ - udev_list_entry_foreach(file_loop, udev_list_get_entry(&file_list)) { - const char *filename = udev_list_entry_get_value(file_loop); - unsigned int filename_off = udev_list_entry_get_num(file_loop); - struct stat st; - - if (stat(filename, &st) != 0) { - err(udev, "can not find '%s': %m\n", filename); - continue; - } - if (S_ISREG(st.st_mode) && st.st_size <= 0) { - info(udev, "ignore empty '%s'\n", filename); - continue; - } - if (S_ISCHR(st.st_mode)) { - info(udev, "ignore masked '%s'\n", filename); - continue; - } - parse_file(rules, filename, filename_off); - } - udev_list_cleanup(&file_list); - - memset(&end_token, 0x00, sizeof(struct token)); - end_token.type = TK_END; - add_token(rules, &end_token); - - /* shrink allocated token and string buffer */ - if (rules->token_cur < rules->token_max) { - struct token *tokens; - - tokens = realloc(rules->tokens, rules->token_cur * sizeof(struct token)); - if (tokens != NULL || rules->token_cur == 0) { - rules->tokens = tokens; - rules->token_max = rules->token_cur; - } - } - if (rules->buf_cur < rules->buf_max) { - char *buf; - - buf = realloc(rules->buf, rules->buf_cur); - if (buf != NULL || rules->buf_cur == 0) { - rules->buf = buf; - rules->buf_max = rules->buf_cur; - } - } - info(udev, "rules use %zu bytes tokens (%u * %zu bytes), %zu bytes buffer\n", - rules->token_max * sizeof(struct token), rules->token_max, sizeof(struct token), rules->buf_max); - info(udev, "temporary index used %zu bytes (%u * %zu bytes)\n", - rules->trie_nodes_cur * sizeof(struct trie_node), - rules->trie_nodes_cur, sizeof(struct trie_node)); - - /* cleanup trie */ - free(rules->trie_nodes); - rules->trie_nodes = NULL; - rules->trie_nodes_cur = 0; - rules->trie_nodes_max = 0; - - /* cleanup uid/gid cache */ - free(rules->uids); - rules->uids = NULL; - rules->uids_cur = 0; - rules->uids_max = 0; - free(rules->gids); - rules->gids = NULL; - rules->gids_cur = 0; - rules->gids_max = 0; - - dump_rules(rules); - return rules; -} - -struct udev_rules *udev_rules_unref(struct udev_rules *rules) -{ - if (rules == NULL) - return NULL; - free(rules->tokens); - free(rules->buf); - free(rules->trie_nodes); - free(rules->uids); - free(rules->gids); - free(rules); - return NULL; -} - -static int match_key(struct udev_rules *rules, struct token *token, const char *val) -{ - char *key_value = &rules->buf[token->key.value_off]; - char *pos; - bool match = false; - - if (val == NULL) - val = ""; - - switch (token->key.glob) { - case GL_PLAIN: - match = (strcmp(key_value, val) == 0); - break; - case GL_GLOB: - match = (fnmatch(key_value, val, 0) == 0); - break; - case GL_SPLIT: - { - const char *split; - size_t len; - - split = &rules->buf[token->key.value_off]; - len = strlen(val); - for (;;) { - const char *next; - - next = strchr(split, '|'); - if (next != NULL) { - size_t matchlen = (size_t)(next - split); - - match = (matchlen == len && strncmp(split, val, matchlen) == 0); - if (match) - break; - } else { - match = (strcmp(split, val) == 0); - break; - } - split = &next[1]; - } - break; - } - case GL_SPLIT_GLOB: - { - char value[UTIL_PATH_SIZE]; - - util_strscpy(value, sizeof(value), &rules->buf[token->key.value_off]); - key_value = value; - while (key_value != NULL) { - pos = strchr(key_value, '|'); - if (pos != NULL) { - pos[0] = '\0'; - pos = &pos[1]; - } - dbg(rules->udev, "match %s '%s' <-> '%s'\n", token_str(token->type), key_value, val); - match = (fnmatch(key_value, val, 0) == 0); - if (match) - break; - key_value = pos; - } - break; - } - case GL_SOMETHING: - match = (val[0] != '\0'); - break; - case GL_UNSET: - return -1; - } - - if (match && (token->key.op == OP_MATCH)) { - dbg(rules->udev, "%s is true (matching value)\n", token_str(token->type)); - return 0; - } - if (!match && (token->key.op == OP_NOMATCH)) { - dbg(rules->udev, "%s is true (non-matching value)\n", token_str(token->type)); - return 0; - } - dbg(rules->udev, "%s is not true\n", token_str(token->type)); - return -1; -} - -static int match_attr(struct udev_rules *rules, struct udev_device *dev, struct udev_event *event, struct token *cur) -{ - const char *name; - char nbuf[UTIL_NAME_SIZE]; - const char *value; - char vbuf[UTIL_NAME_SIZE]; - size_t len; - - name = &rules->buf[cur->key.attr_off]; - switch (cur->key.attrsubst) { - case SB_FORMAT: - udev_event_apply_format(event, name, nbuf, sizeof(nbuf)); - name = nbuf; - /* fall through */ - case SB_NONE: - value = udev_device_get_sysattr_value(dev, name); - if (value == NULL) - return -1; - break; - case SB_SUBSYS: - if (util_resolve_subsys_kernel(event->udev, name, vbuf, sizeof(vbuf), 1) != 0) - return -1; - value = vbuf; - break; - default: - return -1; - } - - /* remove trailing whitespace, if not asked to match for it */ - len = strlen(value); - if (len > 0 && isspace(value[len-1])) { - const char *key_value; - size_t klen; - - key_value = &rules->buf[cur->key.value_off]; - klen = strlen(key_value); - if (klen > 0 && !isspace(key_value[klen-1])) { - if (value != vbuf) { - util_strscpy(vbuf, sizeof(vbuf), value); - value = vbuf; - } - while (len > 0 && isspace(vbuf[--len])) - vbuf[len] = '\0'; - dbg(rules->udev, "removed trailing whitespace from '%s'\n", value); - } - } - - return match_key(rules, cur, value); -} - -enum escape_type { - ESCAPE_UNSET, - ESCAPE_NONE, - ESCAPE_REPLACE, -}; - -int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event, const sigset_t *sigmask) -{ - struct token *cur; - struct token *rule; - enum escape_type esc = ESCAPE_UNSET; - bool can_set_name; - - if (rules->tokens == NULL) - return -1; - - can_set_name = ((strcmp(udev_device_get_action(event->dev), "remove") != 0) && - (major(udev_device_get_devnum(event->dev)) > 0 || - udev_device_get_ifindex(event->dev) > 0)); - - /* loop through token list, match, run actions or forward to next rule */ - cur = &rules->tokens[0]; - rule = cur; - for (;;) { - dump_token(rules, cur); - switch (cur->type) { - case TK_RULE: - /* current rule */ - rule = cur; - /* possibly skip rules which want to set NAME, SYMLINK, OWNER, GROUP, MODE */ - if (!can_set_name && rule->rule.can_set_name) - goto nomatch; - esc = ESCAPE_UNSET; - break; - case TK_M_ACTION: - if (match_key(rules, cur, udev_device_get_action(event->dev)) != 0) - goto nomatch; - break; - case TK_M_DEVPATH: - if (match_key(rules, cur, udev_device_get_devpath(event->dev)) != 0) - goto nomatch; - break; - case TK_M_KERNEL: - if (match_key(rules, cur, udev_device_get_sysname(event->dev)) != 0) - goto nomatch; - break; - case TK_M_DEVLINK: { - size_t devlen = strlen(udev_get_dev_path(event->udev))+1; - struct udev_list_entry *list_entry; - bool match = false; - - udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(event->dev)) { - const char *devlink; - - devlink = &udev_list_entry_get_name(list_entry)[devlen]; - if (match_key(rules, cur, devlink) == 0) { - match = true; - break; - } - } - if (!match) - goto nomatch; - break; - } - case TK_M_NAME: - if (match_key(rules, cur, event->name) != 0) - goto nomatch; - break; - case TK_M_ENV: { - const char *key_name = &rules->buf[cur->key.attr_off]; - const char *value; - - value = udev_device_get_property_value(event->dev, key_name); - if (value == NULL) { - dbg(event->udev, "ENV{%s} is not set, treat as empty\n", key_name); - value = ""; - } - if (match_key(rules, cur, value)) - goto nomatch; - break; - } - case TK_M_TAG: { - struct udev_list_entry *list_entry; - bool match = false; - - udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(event->dev)) { - if (strcmp(&rules->buf[cur->key.value_off], udev_list_entry_get_name(list_entry)) == 0) { - match = true; - break; - } - } - if (!match && (cur->key.op != OP_NOMATCH)) - goto nomatch; - break; - } - case TK_M_SUBSYSTEM: - if (match_key(rules, cur, udev_device_get_subsystem(event->dev)) != 0) - goto nomatch; - break; - case TK_M_DRIVER: - if (match_key(rules, cur, udev_device_get_driver(event->dev)) != 0) - goto nomatch; - break; - case TK_M_WAITFOR: { - char filename[UTIL_PATH_SIZE]; - int found; - - udev_event_apply_format(event, &rules->buf[cur->key.value_off], filename, sizeof(filename)); - found = (wait_for_file(event->dev, filename, 10) == 0); - if (!found && (cur->key.op != OP_NOMATCH)) - goto nomatch; - break; - } - case TK_M_ATTR: - if (match_attr(rules, event->dev, event, cur) != 0) - goto nomatch; - break; - case TK_M_KERNELS: - case TK_M_SUBSYSTEMS: - case TK_M_DRIVERS: - case TK_M_ATTRS: - case TK_M_TAGS: { - struct token *next; - - /* get whole sequence of parent matches */ - next = cur; - while (next->type > TK_M_PARENTS_MIN && next->type < TK_M_PARENTS_MAX) - next++; - - /* loop over parents */ - event->dev_parent = event->dev; - for (;;) { - struct token *key; - - dbg(event->udev, "parent: '%s'\n", udev_device_get_syspath(event->dev_parent)); - /* loop over sequence of parent match keys */ - for (key = cur; key < next; key++ ) { - dump_token(rules, key); - switch(key->type) { - case TK_M_KERNELS: - if (match_key(rules, key, udev_device_get_sysname(event->dev_parent)) != 0) - goto try_parent; - break; - case TK_M_SUBSYSTEMS: - if (match_key(rules, key, udev_device_get_subsystem(event->dev_parent)) != 0) - goto try_parent; - break; - case TK_M_DRIVERS: - if (match_key(rules, key, udev_device_get_driver(event->dev_parent)) != 0) - goto try_parent; - break; - case TK_M_ATTRS: - if (match_attr(rules, event->dev_parent, event, key) != 0) - goto try_parent; - break; - case TK_M_TAGS: { - bool match = udev_device_has_tag(event->dev_parent, &rules->buf[cur->key.value_off]); - - if (match && key->key.op == OP_NOMATCH) - goto try_parent; - if (!match && key->key.op == OP_MATCH) - goto try_parent; - break; - } - default: - goto nomatch; - } - dbg(event->udev, "parent key matched\n"); - } - dbg(event->udev, "all parent keys matched\n"); - break; - - try_parent: - event->dev_parent = udev_device_get_parent(event->dev_parent); - if (event->dev_parent == NULL) - goto nomatch; - } - /* move behind our sequence of parent match keys */ - cur = next; - continue; - } - case TK_M_TEST: { - char filename[UTIL_PATH_SIZE]; - struct stat statbuf; - int match; - - udev_event_apply_format(event, &rules->buf[cur->key.value_off], filename, sizeof(filename)); - if (util_resolve_subsys_kernel(event->udev, filename, filename, sizeof(filename), 0) != 0) { - if (filename[0] != '/') { - char tmp[UTIL_PATH_SIZE]; - - util_strscpy(tmp, sizeof(tmp), filename); - util_strscpyl(filename, sizeof(filename), - udev_device_get_syspath(event->dev), "/", tmp, NULL); - } - } - attr_subst_subdir(filename, sizeof(filename)); - - match = (stat(filename, &statbuf) == 0); - dbg(event->udev, "'%s' %s", filename, match ? "exists\n" : "does not exist\n"); - if (match && cur->key.mode > 0) { - match = ((statbuf.st_mode & cur->key.mode) > 0); - dbg(event->udev, "'%s' has mode=%#o and %s %#o\n", filename, statbuf.st_mode, - match ? "matches" : "does not match", cur->key.mode); - } - if (match && cur->key.op == OP_NOMATCH) - goto nomatch; - if (!match && cur->key.op == OP_MATCH) - goto nomatch; - break; - } - case TK_M_EVENT_TIMEOUT: - info(event->udev, "OPTIONS event_timeout=%u\n", cur->key.event_timeout); - event->timeout_usec = cur->key.event_timeout * 1000 * 1000; - break; - case TK_M_PROGRAM: { - char program[UTIL_PATH_SIZE]; - char **envp; - char result[UTIL_PATH_SIZE]; - - free(event->program_result); - event->program_result = NULL; - udev_event_apply_format(event, &rules->buf[cur->key.value_off], program, sizeof(program)); - envp = udev_device_get_properties_envp(event->dev); - info(event->udev, "PROGRAM '%s' %s:%u\n", - program, - &rules->buf[rule->rule.filename_off], - rule->rule.filename_line); - - if (udev_event_spawn(event, program, envp, sigmask, result, sizeof(result)) < 0) { - if (cur->key.op != OP_NOMATCH) - goto nomatch; - } else { - int count; - - util_remove_trailing_chars(result, '\n'); - if (esc == ESCAPE_UNSET || esc == ESCAPE_REPLACE) { - count = util_replace_chars(result, UDEV_ALLOWED_CHARS_INPUT); - if (count > 0) - info(event->udev, "%i character(s) replaced\n" , count); - } - event->program_result = strdup(result); - dbg(event->udev, "storing result '%s'\n", event->program_result); - if (cur->key.op == OP_NOMATCH) - goto nomatch; - } - break; - } - case TK_M_IMPORT_FILE: { - char import[UTIL_PATH_SIZE]; - - udev_event_apply_format(event, &rules->buf[cur->key.value_off], import, sizeof(import)); - if (import_file_into_properties(event->dev, import) != 0) - if (cur->key.op != OP_NOMATCH) - goto nomatch; - break; - } - case TK_M_IMPORT_PROG: { - char import[UTIL_PATH_SIZE]; - - udev_event_apply_format(event, &rules->buf[cur->key.value_off], import, sizeof(import)); - info(event->udev, "IMPORT '%s' %s:%u\n", - import, - &rules->buf[rule->rule.filename_off], - rule->rule.filename_line); - - if (import_program_into_properties(event, import, sigmask) != 0) - if (cur->key.op != OP_NOMATCH) - goto nomatch; - break; - } - case TK_M_IMPORT_BUILTIN: { - char command[UTIL_PATH_SIZE]; - - if (udev_builtin_run_once(cur->key.builtin_cmd)) { - /* check if we ran already */ - if (event->builtin_run & (1 << cur->key.builtin_cmd)) { - info(event->udev, "IMPORT builtin skip '%s' %s:%u\n", - udev_builtin_name(cur->key.builtin_cmd), - &rules->buf[rule->rule.filename_off], - rule->rule.filename_line); - /* return the result from earlier run */ - if (event->builtin_ret & (1 << cur->key.builtin_cmd)) - if (cur->key.op != OP_NOMATCH) - goto nomatch; - break; - } - /* mark as ran */ - event->builtin_run |= (1 << cur->key.builtin_cmd); - } - - udev_event_apply_format(event, &rules->buf[cur->key.value_off], command, sizeof(command)); - info(event->udev, "IMPORT builtin '%s' %s:%u\n", - udev_builtin_name(cur->key.builtin_cmd), - &rules->buf[rule->rule.filename_off], - rule->rule.filename_line); - - if (udev_builtin_run(event->dev, cur->key.builtin_cmd, command, false) != 0) { - /* remember failure */ - info(rules->udev, "IMPORT builtin '%s' returned non-zero\n", - udev_builtin_name(cur->key.builtin_cmd)); - event->builtin_ret |= (1 << cur->key.builtin_cmd); - if (cur->key.op != OP_NOMATCH) - goto nomatch; - } - break; - } - case TK_M_IMPORT_DB: { - const char *key = &rules->buf[cur->key.value_off]; - const char *value; - - value = udev_device_get_property_value(event->dev_db, key); - if (value != NULL) { - struct udev_list_entry *entry; - - entry = udev_device_add_property(event->dev, key, value); - udev_list_entry_set_num(entry, true); - } else { - if (cur->key.op != OP_NOMATCH) - goto nomatch; - } - break; - } - case TK_M_IMPORT_CMDLINE: { - FILE *f; - bool imported = false; - - f = fopen("/proc/cmdline", "r"); - if (f != NULL) { - char cmdline[4096]; - - if (fgets(cmdline, sizeof(cmdline), f) != NULL) { - const char *key = &rules->buf[cur->key.value_off]; - char *pos; - - pos = strstr(cmdline, key); - if (pos != NULL) { - struct udev_list_entry *entry; - - pos += strlen(key); - if (pos[0] == '\0' || isspace(pos[0])) { - /* we import simple flags as 'FLAG=1' */ - entry = udev_device_add_property(event->dev, key, "1"); - udev_list_entry_set_num(entry, true); - imported = true; - } else if (pos[0] == '=') { - const char *value; - - pos++; - value = pos; - while (pos[0] != '\0' && !isspace(pos[0])) - pos++; - pos[0] = '\0'; - entry = udev_device_add_property(event->dev, key, value); - udev_list_entry_set_num(entry, true); - imported = true; - } - } - } - fclose(f); - } - if (!imported && cur->key.op != OP_NOMATCH) - goto nomatch; - break; - } - case TK_M_IMPORT_PARENT: { - char import[UTIL_PATH_SIZE]; - - udev_event_apply_format(event, &rules->buf[cur->key.value_off], import, sizeof(import)); - if (import_parent_into_properties(event->dev, import) != 0) - if (cur->key.op != OP_NOMATCH) - goto nomatch; - break; - } - case TK_M_RESULT: - if (match_key(rules, cur, event->program_result) != 0) - goto nomatch; - break; - case TK_A_STRING_ESCAPE_NONE: - esc = ESCAPE_NONE; - break; - case TK_A_STRING_ESCAPE_REPLACE: - esc = ESCAPE_REPLACE; - break; - case TK_A_DB_PERSIST: - udev_device_set_db_persist(event->dev); - break; - case TK_A_INOTIFY_WATCH: - if (event->inotify_watch_final) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->inotify_watch_final = true; - event->inotify_watch = cur->key.watch; - break; - case TK_A_DEVLINK_PRIO: - udev_device_set_devlink_priority(event->dev, cur->key.devlink_prio); - break; - case TK_A_OWNER: { - char owner[UTIL_NAME_SIZE]; - - if (event->owner_final) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->owner_final = true; - udev_event_apply_format(event, &rules->buf[cur->key.value_off], owner, sizeof(owner)); - event->uid = util_lookup_user(event->udev, owner); - info(event->udev, "OWNER %u %s:%u\n", - event->uid, - &rules->buf[rule->rule.filename_off], - rule->rule.filename_line); - break; - } - case TK_A_GROUP: { - char group[UTIL_NAME_SIZE]; - - if (event->group_final) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->group_final = true; - udev_event_apply_format(event, &rules->buf[cur->key.value_off], group, sizeof(group)); - event->gid = util_lookup_group(event->udev, group); - info(event->udev, "GROUP %u %s:%u\n", - event->gid, - &rules->buf[rule->rule.filename_off], - rule->rule.filename_line); - break; - } - case TK_A_MODE: { - char mode_str[UTIL_NAME_SIZE]; - mode_t mode; - char *endptr; - - if (event->mode_final) - break; - udev_event_apply_format(event, &rules->buf[cur->key.value_off], mode_str, sizeof(mode_str)); - mode = strtol(mode_str, &endptr, 8); - if (endptr[0] != '\0') { - err(event->udev, "ignoring invalid mode '%s'\n", mode_str); - break; - } - if (cur->key.op == OP_ASSIGN_FINAL) - event->mode_final = true; - event->mode_set = true; - event->mode = mode; - info(event->udev, "MODE %#o %s:%u\n", - event->mode, - &rules->buf[rule->rule.filename_off], - rule->rule.filename_line); - break; - } - case TK_A_OWNER_ID: - if (event->owner_final) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->owner_final = true; - event->uid = cur->key.uid; - info(event->udev, "OWNER %u %s:%u\n", - event->uid, - &rules->buf[rule->rule.filename_off], - rule->rule.filename_line); - break; - case TK_A_GROUP_ID: - if (event->group_final) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->group_final = true; - event->gid = cur->key.gid; - info(event->udev, "GROUP %u %s:%u\n", - event->gid, - &rules->buf[rule->rule.filename_off], - rule->rule.filename_line); - break; - case TK_A_MODE_ID: - if (event->mode_final) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->mode_final = true; - event->mode_set = true; - event->mode = cur->key.mode; - info(event->udev, "MODE %#o %s:%u\n", - event->mode, - &rules->buf[rule->rule.filename_off], - rule->rule.filename_line); - break; - case TK_A_ENV: { - const char *name = &rules->buf[cur->key.attr_off]; - char *value = &rules->buf[cur->key.value_off]; - - if (value[0] != '\0') { - char temp_value[UTIL_NAME_SIZE]; - struct udev_list_entry *entry; - - udev_event_apply_format(event, value, temp_value, sizeof(temp_value)); - entry = udev_device_add_property(event->dev, name, temp_value); - /* store in db, skip private keys */ - if (name[0] != '.') - udev_list_entry_set_num(entry, true); - } else { - udev_device_add_property(event->dev, name, NULL); - } - break; - } - case TK_A_TAG: { - char tag[UTIL_PATH_SIZE]; - const char *p; - - udev_event_apply_format(event, &rules->buf[cur->key.value_off], tag, sizeof(tag)); - if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) - udev_device_cleanup_tags_list(event->dev); - for (p = tag; *p != '\0'; p++) { - if ((*p >= 'a' && *p <= 'z') || - (*p >= 'A' && *p <= 'Z') || - (*p >= '0' && *p <= '9') || - *p == '-' || *p == '_') - continue; - err(event->udev, "ignoring invalid tag name '%s'\n", tag); - break; - } - udev_device_add_tag(event->dev, tag); - break; - } - case TK_A_NAME: { - const char *name = &rules->buf[cur->key.value_off]; - - char name_str[UTIL_PATH_SIZE]; - int count; - - if (event->name_final) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->name_final = true; - udev_event_apply_format(event, name, name_str, sizeof(name_str)); - if (esc == ESCAPE_UNSET || esc == ESCAPE_REPLACE) { - count = util_replace_chars(name_str, "/"); - if (count > 0) - info(event->udev, "%i character(s) replaced\n", count); - } - if (major(udev_device_get_devnum(event->dev))) { - size_t devlen = strlen(udev_get_dev_path(event->udev))+1; - - if (strcmp(name_str, &udev_device_get_devnode(event->dev)[devlen]) != 0) { - err(event->udev, "NAME=\"%s\" ignored, kernel device nodes " - "can not be renamed; please fix it in %s:%u\n", name, - &rules->buf[rule->rule.filename_off], rule->rule.filename_line); - break; - } - } - free(event->name); - event->name = strdup(name_str); - info(event->udev, "NAME '%s' %s:%u\n", - event->name, - &rules->buf[rule->rule.filename_off], - rule->rule.filename_line); - break; - } - case TK_A_DEVLINK: { - char temp[UTIL_PATH_SIZE]; - char filename[UTIL_PATH_SIZE]; - char *pos, *next; - int count = 0; - - if (event->devlink_final) - break; - if (major(udev_device_get_devnum(event->dev)) == 0) - break; - if (cur->key.op == OP_ASSIGN_FINAL) - event->devlink_final = true; - if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) - udev_device_cleanup_devlinks_list(event->dev); - - /* allow multiple symlinks separated by spaces */ - udev_event_apply_format(event, &rules->buf[cur->key.value_off], temp, sizeof(temp)); - if (esc == ESCAPE_UNSET) - count = util_replace_chars(temp, "/ "); - else if (esc == ESCAPE_REPLACE) - count = util_replace_chars(temp, "/"); - if (count > 0) - info(event->udev, "%i character(s) replaced\n" , count); - dbg(event->udev, "rule applied, added symlink(s) '%s'\n", temp); - pos = temp; - while (isspace(pos[0])) - pos++; - next = strchr(pos, ' '); - while (next != NULL) { - next[0] = '\0'; - info(event->udev, "LINK '%s' %s:%u\n", pos, - &rules->buf[rule->rule.filename_off], rule->rule.filename_line); - util_strscpyl(filename, sizeof(filename), udev_get_dev_path(event->udev), "/", pos, NULL); - udev_device_add_devlink(event->dev, filename, cur->key.devlink_unique); - while (isspace(next[1])) - next++; - pos = &next[1]; - next = strchr(pos, ' '); - } - if (pos[0] != '\0') { - info(event->udev, "LINK '%s' %s:%u\n", pos, - &rules->buf[rule->rule.filename_off], rule->rule.filename_line); - util_strscpyl(filename, sizeof(filename), udev_get_dev_path(event->udev), "/", pos, NULL); - udev_device_add_devlink(event->dev, filename, cur->key.devlink_unique); - } - break; - } - case TK_A_ATTR: { - const char *key_name = &rules->buf[cur->key.attr_off]; - char attr[UTIL_PATH_SIZE]; - char value[UTIL_NAME_SIZE]; - FILE *f; - - if (util_resolve_subsys_kernel(event->udev, key_name, attr, sizeof(attr), 0) != 0) - util_strscpyl(attr, sizeof(attr), udev_device_get_syspath(event->dev), "/", key_name, NULL); - attr_subst_subdir(attr, sizeof(attr)); - - udev_event_apply_format(event, &rules->buf[cur->key.value_off], value, sizeof(value)); - info(event->udev, "ATTR '%s' writing '%s' %s:%u\n", attr, value, - &rules->buf[rule->rule.filename_off], - rule->rule.filename_line); - f = fopen(attr, "w"); - if (f != NULL) { - if (fprintf(f, "%s", value) <= 0) - err(event->udev, "error writing ATTR{%s}: %m\n", attr); - fclose(f); - } else { - err(event->udev, "error opening ATTR{%s} for writing: %m\n", attr); - } - break; - } - case TK_A_RUN: { - if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) - udev_list_cleanup(&event->run_list); - info(event->udev, "RUN '%s' %s:%u\n", - &rules->buf[cur->key.value_off], - &rules->buf[rule->rule.filename_off], - rule->rule.filename_line); - udev_list_entry_add(&event->run_list, &rules->buf[cur->key.value_off], NULL); - break; - } - case TK_A_GOTO: - if (cur->key.rule_goto == 0) - break; - cur = &rules->tokens[cur->key.rule_goto]; - continue; - case TK_END: - return 0; - - case TK_M_PARENTS_MIN: - case TK_M_PARENTS_MAX: - case TK_M_MAX: - case TK_UNSET: - err(rules->udev, "wrong type %u\n", cur->type); - goto nomatch; - } - - cur++; - continue; - nomatch: - /* fast-forward to next rule */ - cur = rule + rule->rule.token_count; - dbg(rules->udev, "forward to rule: %u\n", - (unsigned int) (cur - rules->tokens)); - } -} - -void udev_rules_apply_static_dev_perms(struct udev_rules *rules) -{ - struct token *cur; - struct token *rule; - uid_t uid = 0; - gid_t gid = 0; - mode_t mode = 0; - - if (rules->tokens == NULL) - return; - - cur = &rules->tokens[0]; - rule = cur; - for (;;) { - switch (cur->type) { - case TK_RULE: - /* current rule */ - rule = cur; - - /* skip rules without a static_node tag */ - if (!rule->rule.has_static_node) - goto next; - - uid = 0; - gid = 0; - mode = 0; - break; - case TK_A_OWNER_ID: - uid = cur->key.uid; - break; - case TK_A_GROUP_ID: - gid = cur->key.gid; - break; - case TK_A_MODE_ID: - mode = cur->key.mode; - break; - case TK_A_STATIC_NODE: { - char filename[UTIL_PATH_SIZE]; - struct stat stats; - - /* we assure, that the permissions tokens are sorted before the static token */ - if (mode == 0 && uid == 0 && gid == 0) - goto next; - util_strscpyl(filename, sizeof(filename), udev_get_dev_path(rules->udev), "/", - &rules->buf[cur->key.value_off], NULL); - if (stat(filename, &stats) != 0) - goto next; - if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode)) - goto next; - if (mode == 0) { - if (gid > 0) - mode = 0660; - else - mode = 0600; - } - if (mode != (stats.st_mode & 01777)) { - chmod(filename, mode); - info(rules->udev, "chmod '%s' %#o\n", filename, mode); - } - - if ((uid != 0 && uid != stats.st_uid) || (gid != 0 && gid != stats.st_gid)) { - chown(filename, uid, gid); - info(rules->udev, "chown '%s' %u %u\n", filename, uid, gid); - } - - utimensat(AT_FDCWD, filename, NULL, 0); - break; - } - case TK_END: - return; - } - - cur++; - continue; -next: - /* fast-forward to next rule */ - cur = rule + rule->rule.token_count; - continue; - } -} diff --git a/src/udev/src/udev-settle.service.in b/src/udev/src/udev-settle.service.in deleted file mode 100644 index b0a4964f76..0000000000 --- a/src/udev/src/udev-settle.service.in +++ /dev/null @@ -1,25 +0,0 @@ -# This service is usually not enabled by default. If enabled, it -# acts as a barrier for basic.target -- so all later services will -# wait for udev completely finishing its coldplug run. -# -# If needed, to work around broken or non-hotplug-aware services, -# it might be enabled unconditionally, or pulled-in on-demand by -# the services that assume a fully populated /dev at startup. It -# should not be used or pulled-in ever on systems without such -# legacy services running. - -[Unit] -Description=udev Wait for Complete Device Initialization -DefaultDependencies=no -Wants=udev.service -After=udev-trigger.service -Before=basic.target - -[Service] -Type=oneshot -TimeoutSec=180 -RemainAfterExit=yes -ExecStart=@bindir@/udevadm settle - -[Install] -WantedBy=basic.target diff --git a/src/udev/src/udev-trigger.service.in b/src/udev/src/udev-trigger.service.in deleted file mode 100644 index cd81945c88..0000000000 --- a/src/udev/src/udev-trigger.service.in +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=udev Coldplug all Devices -Wants=udev.service -After=udev-kernel.socket udev-control.socket -DefaultDependencies=no - -[Service] -Type=oneshot -RemainAfterExit=yes -ExecStart=@bindir@/udevadm trigger --type=subsystems --action=add ; @bindir@/udevadm trigger --type=devices --action=add diff --git a/src/udev/src/udev-watch.c b/src/udev/src/udev-watch.c deleted file mode 100644 index 228d18fedf..0000000000 --- a/src/udev/src/udev-watch.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) 2004-2010 Kay Sievers - * Copyright (C) 2009 Canonical Ltd. - * Copyright (C) 2009 Scott James Remnant - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -static int inotify_fd = -1; - -/* inotify descriptor, will be shared with rules directory; - * set to cloexec since we need our children to be able to add - * watches for us - */ -int udev_watch_init(struct udev *udev) -{ - inotify_fd = inotify_init1(IN_CLOEXEC); - if (inotify_fd < 0) - err(udev, "inotify_init failed: %m\n"); - return inotify_fd; -} - -/* move any old watches directory out of the way, and then restore - * the watches - */ -void udev_watch_restore(struct udev *udev) -{ - char filename[UTIL_PATH_SIZE], oldname[UTIL_PATH_SIZE]; - - if (inotify_fd < 0) - return; - - util_strscpyl(oldname, sizeof(oldname), udev_get_run_path(udev), "/watch.old", NULL); - util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/watch", NULL); - if (rename(filename, oldname) == 0) { - DIR *dir; - struct dirent *ent; - - dir = opendir(oldname); - if (dir == NULL) { - err(udev, "unable to open old watches dir '%s', old watches will not be restored: %m", oldname); - return; - } - - for (ent = readdir(dir); ent != NULL; ent = readdir(dir)) { - char device[UTIL_PATH_SIZE]; - char *s; - size_t l; - ssize_t len; - struct udev_device *dev; - - if (ent->d_name[0] == '.') - continue; - - s = device; - l = util_strpcpy(&s, sizeof(device), udev_get_sys_path(udev)); - len = readlinkat(dirfd(dir), ent->d_name, s, l); - if (len <= 0 || len == (ssize_t)l) - goto unlink; - s[len] = '\0'; - - dev = udev_device_new_from_id_filename(udev, s); - if (dev == NULL) - goto unlink; - - info(udev, "restoring old watch on '%s'\n", udev_device_get_devnode(dev)); - udev_watch_begin(udev, dev); - udev_device_unref(dev); -unlink: - unlinkat(dirfd(dir), ent->d_name, 0); - } - - closedir(dir); - rmdir(oldname); - - } else if (errno != ENOENT) { - err(udev, "unable to move watches dir '%s', old watches will not be restored: %m", filename); - } -} - -void udev_watch_begin(struct udev *udev, struct udev_device *dev) -{ - char filename[UTIL_PATH_SIZE]; - int wd; - - if (inotify_fd < 0) - return; - - info(udev, "adding watch on '%s'\n", udev_device_get_devnode(dev)); - wd = inotify_add_watch(inotify_fd, udev_device_get_devnode(dev), IN_CLOSE_WRITE); - if (wd < 0) { - err(udev, "inotify_add_watch(%d, %s, %o) failed: %m\n", - inotify_fd, udev_device_get_devnode(dev), IN_CLOSE_WRITE); - return; - } - - snprintf(filename, sizeof(filename), "%s/watch/%d", udev_get_run_path(udev), wd); - util_create_path(udev, filename); - unlink(filename); - symlink(udev_device_get_id_filename(dev), filename); - - udev_device_set_watch_handle(dev, wd); -} - -void udev_watch_end(struct udev *udev, struct udev_device *dev) -{ - int wd; - char filename[UTIL_PATH_SIZE]; - - if (inotify_fd < 0) - return; - - wd = udev_device_get_watch_handle(dev); - if (wd < 0) - return; - - info(udev, "removing watch on '%s'\n", udev_device_get_devnode(dev)); - inotify_rm_watch(inotify_fd, wd); - - snprintf(filename, sizeof(filename), "%s/watch/%d", udev_get_run_path(udev), wd); - unlink(filename); - - udev_device_set_watch_handle(dev, -1); -} - -struct udev_device *udev_watch_lookup(struct udev *udev, int wd) -{ - char filename[UTIL_PATH_SIZE]; - char majmin[UTIL_PATH_SIZE]; - char *s; - size_t l; - ssize_t len; - - if (inotify_fd < 0 || wd < 0) - return NULL; - - snprintf(filename, sizeof(filename), "%s/watch/%d", udev_get_run_path(udev), wd); - s = majmin; - l = util_strpcpy(&s, sizeof(majmin), udev_get_sys_path(udev)); - len = readlink(filename, s, l); - if (len <= 0 || (size_t)len == l) - return NULL; - s[len] = '\0'; - - return udev_device_new_from_id_filename(udev, s); -} diff --git a/src/udev/src/udev.conf b/src/udev/src/udev.conf deleted file mode 100644 index f39253eb67..0000000000 --- a/src/udev/src/udev.conf +++ /dev/null @@ -1,3 +0,0 @@ -# see udev(7) for details - -#udev_log="info" diff --git a/src/udev/src/udev.h b/src/udev/src/udev.h deleted file mode 100644 index bc051c9b65..0000000000 --- a/src/udev/src/udev.h +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (C) 2003 Greg Kroah-Hartman - * Copyright (C) 2003-2010 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef _UDEV_H_ -#define _UDEV_H_ - -#include -#include -#include - -#include "libudev.h" -#include "libudev-private.h" - -struct udev_event { - struct udev *udev; - struct udev_device *dev; - struct udev_device *dev_parent; - struct udev_device *dev_db; - char *name; - char *program_result; - mode_t mode; - uid_t uid; - gid_t gid; - struct udev_list run_list; - int exec_delay; - unsigned long long birth_usec; - unsigned long long timeout_usec; - int fd_signal; - unsigned int builtin_run; - unsigned int builtin_ret; - bool sigterm; - bool inotify_watch; - bool inotify_watch_final; - bool group_final; - bool owner_final; - bool mode_set; - bool mode_final; - bool name_final; - bool devlink_final; - bool run_final; -}; - -struct udev_watch { - struct udev_list_node node; - int handle; - char *name; -}; - -/* udev-rules.c */ -struct udev_rules; -struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names); -struct udev_rules *udev_rules_unref(struct udev_rules *rules); -int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event, const sigset_t *sigmask); -void udev_rules_apply_static_dev_perms(struct udev_rules *rules); - -/* udev-event.c */ -struct udev_event *udev_event_new(struct udev_device *dev); -void udev_event_unref(struct udev_event *event); -size_t udev_event_apply_format(struct udev_event *event, const char *src, char *dest, size_t size); -int udev_event_apply_subsys_kernel(struct udev_event *event, const char *string, - char *result, size_t maxsize, int read_value); -int udev_event_spawn(struct udev_event *event, - const char *cmd, char **envp, const sigset_t *sigmask, - char *result, size_t ressize); -int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules, const sigset_t *sigset); -int udev_event_execute_run(struct udev_event *event, const sigset_t *sigset); -int udev_build_argv(struct udev *udev, char *cmd, int *argc, char *argv[]); - -/* udev-watch.c */ -int udev_watch_init(struct udev *udev); -void udev_watch_restore(struct udev *udev); -void udev_watch_begin(struct udev *udev, struct udev_device *dev); -void udev_watch_end(struct udev *udev, struct udev_device *dev); -struct udev_device *udev_watch_lookup(struct udev *udev, int wd); - -/* udev-node.c */ -void udev_node_add(struct udev_device *dev, mode_t mode, uid_t uid, gid_t gid); -void udev_node_remove(struct udev_device *dev); -void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old); - -/* udev-ctrl.c */ -struct udev_ctrl; -struct udev_ctrl *udev_ctrl_new(struct udev *udev); -struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd); -int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl); -struct udev_ctrl *udev_ctrl_ref(struct udev_ctrl *uctrl); -struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl); -int udev_ctrl_cleanup(struct udev_ctrl *uctrl); -struct udev *udev_ctrl_get_udev(struct udev_ctrl *uctrl); -int udev_ctrl_get_fd(struct udev_ctrl *uctrl); -int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, int timeout); -int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, int timeout); -int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, int timeout); -int udev_ctrl_send_reload(struct udev_ctrl *uctrl, int timeout); -int udev_ctrl_send_ping(struct udev_ctrl *uctrl, int timeout); -int udev_ctrl_send_exit(struct udev_ctrl *uctrl, int timeout); -int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, int timeout); -int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, int timeout); -struct udev_ctrl_connection; -struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl); -struct udev_ctrl_connection *udev_ctrl_connection_ref(struct udev_ctrl_connection *conn); -struct udev_ctrl_connection *udev_ctrl_connection_unref(struct udev_ctrl_connection *conn); -struct udev_ctrl_msg; -struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn); -struct udev_ctrl_msg *udev_ctrl_msg_ref(struct udev_ctrl_msg *ctrl_msg); -struct udev_ctrl_msg *udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_set_log_level(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_stop_exec_queue(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_start_exec_queue(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_reload(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_ping(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_exit(struct udev_ctrl_msg *ctrl_msg); -const char *udev_ctrl_get_set_env(struct udev_ctrl_msg *ctrl_msg); -int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg); - -/* built-in commands */ -enum udev_builtin_cmd { - UDEV_BUILTIN_BLKID, - UDEV_BUILTIN_FIRMWARE, - UDEV_BUILTIN_INPUT_ID, - UDEV_BUILTIN_KMOD, - UDEV_BUILTIN_PATH_ID, - UDEV_BUILTIN_PCI_DB, - UDEV_BUILTIN_USB_DB, - UDEV_BUILTIN_USB_ID, - UDEV_BUILTIN_MAX -}; -struct udev_builtin { - const char *name; - int (*cmd)(struct udev_device *dev, int argc, char *argv[], bool test); - const char *help; - int (*init)(struct udev *udev); - void (*exit)(struct udev *udev); - bool (*validate)(struct udev *udev); - bool run_once; -}; -extern const struct udev_builtin udev_builtin_blkid; -extern const struct udev_builtin udev_builtin_firmware; -extern const struct udev_builtin udev_builtin_input_id; -extern const struct udev_builtin udev_builtin_kmod; -extern const struct udev_builtin udev_builtin_path_id; -extern const struct udev_builtin udev_builtin_pci_db; -extern const struct udev_builtin udev_builtin_usb_db; -extern const struct udev_builtin udev_builtin_usb_id; -int udev_builtin_init(struct udev *udev); -void udev_builtin_exit(struct udev *udev); -enum udev_builtin_cmd udev_builtin_lookup(const char *command); -const char *udev_builtin_name(enum udev_builtin_cmd cmd); -bool udev_builtin_run_once(enum udev_builtin_cmd cmd); -int udev_builtin_run(struct udev_device *dev, enum udev_builtin_cmd cmd, const char *command, bool test); -void udev_builtin_list(struct udev *udev); -int udev_builtin_add_property(struct udev_device *dev, bool test, const char *key, const char *val); - -/* udev logging */ -void udev_main_log(struct udev *udev, int priority, - const char *file, int line, const char *fn, - const char *format, va_list args); - -/* udevadm commands */ -struct udevadm_cmd { - const char *name; - int (*cmd)(struct udev *udev, int argc, char *argv[]); - const char *help; - int debug; -}; -extern const struct udevadm_cmd udevadm_info; -extern const struct udevadm_cmd udevadm_trigger; -extern const struct udevadm_cmd udevadm_settle; -extern const struct udevadm_cmd udevadm_control; -extern const struct udevadm_cmd udevadm_monitor; -extern const struct udevadm_cmd udevadm_test; -extern const struct udevadm_cmd udevadm_test_builtin; -#endif diff --git a/src/udev/src/udev.pc.in b/src/udev/src/udev.pc.in deleted file mode 100644 index 0b04c02ef6..0000000000 --- a/src/udev/src/udev.pc.in +++ /dev/null @@ -1,5 +0,0 @@ -Name: udev -Description: udev -Version: @VERSION@ - -udevdir=@pkglibexecdir@ diff --git a/src/udev/src/udev.service.in b/src/udev/src/udev.service.in deleted file mode 100644 index c27eb1baf5..0000000000 --- a/src/udev/src/udev.service.in +++ /dev/null @@ -1,14 +0,0 @@ -[Unit] -Description=udev Kernel Device Manager -Wants=udev-control.socket udev-kernel.socket -After=udev-control.socket udev-kernel.socket -Before=basic.target -DefaultDependencies=no -ConditionCapability=CAP_MKNOD - -[Service] -Type=notify -OOMScoreAdjust=-1000 -Sockets=udev-control.socket udev-kernel.socket -Restart=on-failure -ExecStart=@pkglibexecdir@/udevd diff --git a/src/udev/src/udev.xml b/src/udev/src/udev.xml deleted file mode 100644 index 8eb583a823..0000000000 --- a/src/udev/src/udev.xml +++ /dev/null @@ -1,695 +0,0 @@ - - - - - - - udev - udev - - - - udev - 7 - - - - udev - Linux dynamic device management - - - Description - udev supplies the system software with device events, manages permissions - of device nodes and may create additional symlinks in the /dev - directory, or renames network interfaces. The kernel usually just assigns unpredictable - device names based on the order of discovery. Meaningful symlinks or network device - names provide a way to reliably identify devices based on their properties or - current configuration. - - The udev daemon, udevd - 8, receives device uevents directly from - the kernel whenever a device is added or removed from the system, or it changes its - state. When udev receives a device event, it matches its configured set of rules - against various device attributes to identify the device. Rules that match may - provide additional device information to be stored in the udev database or - to be used to create meaningful symlink names. - - All device information udev processes is stored in the udev database and - sent out to possible event subscribers. Access to all stored data and the event - sources is provided by the library libudev. - - - Configuration - udev configuration files are placed in /etc/udev - and /usr/lib/udev. All empty lines or lines beginning with - '#' are ignored. - - Configuration file - udev expects its main configuration file at /etc/udev/udev.conf. - It consists of a set of variables allowing the user to override default udev values. - The following variables can be set: - - - - - Specifies where to place the device nodes in the filesystem. - The default value is /dev. - - - - - - - The logging priority. Valid values are the numerical syslog priorities - or their textual representations: , - and . - - - - - - Rules files - The udev rules are read from the files located in the - system rules directory /usr/lib/udev/rules.d, - the volatile runtime directory /run/udev/rules.d - and the local administration directory /etc/udev/rules.d. - All rules files are collectively sorted and processed in lexical order, - regardless of the directories in which they live. However, files with - identical file names replace each other. Files in /etc - have the highest priority, files in /run take precedence - over files with the same name in /lib. This can be - used to override a system-supplied rules file with a local file if needed; - a symlink in /etc with the same name as a rules file in - /lib, pointing to /dev/null, - disables the rules file entirely. - - Rule files must have the extension .rules; other - extensions are ignored. - - Every line in the rules file contains at least one key-value pair. - There are two kind of keys: match and assignment. - If all match keys are matching against its value, the rule gets applied and the - assignment keys get the specified value assigned. - - A matching rule may rename a network interface, add symlinks - pointing to the device node, or run a specified program as part of - the event handling. - - A rule consists of a comma-separated list of one or more key-value pairs. - Each key has a distinct operation, depending on the used operator. Valid - operators are: - - - - - Compare for equality. - - - - - - - Compare for inequality. - - - - - - - Assign a value to a key. Keys that represent a list are reset - and only this single value is assigned. - - - - - - - Add the value to a key that holds a list of entries. - - - - - - - Assign a value to a key finally; disallow any later changes. - - - - - The following key names can be used to match against device properties. - Some of the keys also match against properties of the parent devices in sysfs, - not only the device that has generated the event. If multiple keys that match - a parent device are specified in a single rule, all these keys must match at - one and the same parent device. - - - - - Match the name of the event action. - - - - - - - Match the devpath of the event device. - - - - - - - Match the name of the event device. - - - - - - - Match the name of a network interface. It can be used once the - NAME key has been set in one of the preceding rules. - - - - - - - Match the name of a symlink targeting the node. It can - be used once a SYMLINK key has been set in one of the preceding - rules. There may be multiple symlinks; only one needs to match. - - - - - - - - Match the subsystem of the event device. - - - - - - Match the driver name of the event device. Only set this key for devices - which are bound to a driver at the time the event is generated. - - - - - - Match sysfs attribute values of the event device. Trailing - whitespace in the attribute values is ignored unless the specified match - value itself contains trailing whitespace. - - - - - - - - Search the devpath upwards for a matching device name. - - - - - - - Search the devpath upwards for a matching device subsystem name. - - - - - - - Search the devpath upwards for a matching device driver name. - - - - - - - Search the devpath upwards for a device with matching sysfs attribute values. - If multiple matches are specified, all of them - must match on the same device. Trailing whitespace in the attribute values is ignored - unless the specified match value itself contains trailing whitespace. - - - - - - - Search the devpath upwards for a device with matching tag. - - - - - - - Match against a device property value. - - - - - - - Match against a device tag. - - - - - - - Test the existence of a file. An octal mode mask can be specified - if needed. - - - - - - - Execute a program to determine whether there - is a match; the key is true if the program returns - successfully. The device properties are made available to the - executed program in the environment. The program's stdout - is available in the RESULT key. - - - - - - - Match the returned string of the last PROGRAM call. This key can - be used in the same or in any later rule after a PROGRAM call. - - - - - Most of the fields support shell-style pattern matching. The following - pattern characters are supported: - - - - - Matches zero or more characters. - - - - - - Matches any single character. - - - - - - Matches any single character specified within the brackets. For - example, the pattern string 'tty[SR]' would match either 'ttyS' or 'ttyR'. - Ranges are also supported via the '-' character. - For example, to match on the range of all digits, the pattern [0-9] could - be used. If the first character following the '[' is a '!', any characters - not enclosed are matched. - - - - - The following keys can get values assigned: - - - - - The name to use for a network interface. The name of a device node - can not be changed by udev, only additional symlinks can be created. - - - - - - - The name of a symlink targeting the node. Every matching rule adds - this value to the list of symlinks to be created. Multiple symlinks may be - specified by separating the names by the space character. In case multiple - devices claim the same name, the link always points to the device with - the highest link_priority. If the current device goes away, the links are - re-evaluated and the device with the next highest link_priority becomes the owner of - the link. If no link_priority is specified, the order of the devices (and - which one of them owns the link) is undefined. Also, symlink names must - never conflict with the kernel's default device node names, as that would - result in unpredictable behavior. - - - - - - - - The permissions for the device node. Every specified value overrides - the compiled-in default value. - - - - - - - The value that should be written to a sysfs attribute of the - event device. - - - - - - - Set a device property value. Property names with a leading '.' - are neither stored in the database nor exported to events or - external tools (run by, say, the PROGRAM match key). - - - - - - - Attach a tag to a device. This is used to filter events for users - of libudev's monitor functionality, or to enumerate a group of tagged - devices. The implementation can only work efficiently if only a few - tags are attached to a device. It is only meant to be used in - contexts with specific device filter requirements, and not as a - general-purpose flag. Excessive use might result in inefficient event - handling. - - - - - - - Add a program to the list of programs to be executed for a specific - device. - If no absolute path is given, the program is expected to live in - /usr/lib/udev, otherwise the absolute path must be specified. The program - name and following arguments are separated by spaces. Single quotes can - be used to specify arguments with spaces. - This can only be used for very short running tasks. Running an - event process for a long period of time may block all further events for - this or a dependent device. Starting daemons or other long running processes - is not appropriate for udev. - - - - - - - A named label to which a GOTO may jump. - - - - - - - Jumps to the next LABEL with a matching name. - - - - - - - Import a set of variables as device properties, - depending on type: - - - - - Execute an external program specified as the assigned value and - import its output, which must be in environment key - format. Path specification, command/argument separation, - and quoting work like in . - - - - - - Import a text file specified as the assigned value, the content - of which must be in environment key format. - - - - - - Import a single property specified as the assigned value from the - current device database. This works only if the database is already populated - by an earlier event. - - - - - - Import a single property from the kernel command line. For simple flags - the value of the property is set to '1'. - - - - - - Import the stored keys from the parent device by reading - the database entry of the parent device. The value assigned to - is used as a filter of key names - to import (with the same shell-style pattern matching used for - comparisons). - - - - - - - - - - Wait for a file to become available or until a timeout of - 10 seconds expires. The path is relative to the sysfs device; - if no path is specified, this waits for an attribute to appear. - - - - - - - Rule and device options: - - - - - Specify the priority of the created symlinks. Devices with higher - priorities overwrite existing symlinks of other devices. The default is 0. - - - - - - Number of seconds an event waits for operations to finish before - giving up and terminating itself. - - - - - - Usually control and other possibly unsafe characters are replaced - in strings used for device naming. The mode of replacement can be specified - with this option. - - - - - - Apply the permissions specified in this rule to the static device node with - the specified name. Static device nodes might be provided by kernel modules - or copied from /usr/lib/udev/devices. These nodes might not have - a corresponding kernel device at the time udevd is started; they can trigger - automatic kernel module loading. - - - - - - Watch the device node with inotify; when the node is closed after being opened for - writing, a change uevent is synthesized. - - - - - - Disable the watching of a device node with inotify. - - - - - - - - The , , , - , , and - fields support simple string substitutions. The - substitutions are performed after all rules have been processed, right before the program - is executed, allowing for the use of device properties set by earlier matching - rules. For all other fields, substitutions are performed while the individual rule is - being processed. The available substitutions are: - - - , - - The kernel name for this device. - - - - - , - - The kernel number for this device. For example, 'sda3' has - kernel number of '3' - - - - - , - - The devpath of the device. - - - - - , - - The name of the device matched while searching the devpath upwards for - , , and . - - - - - - - - The driver name of the device matched while searching the devpath upwards for - , , and . - - - - - - , - - The value of a sysfs attribute found at the device where - all keys of the rule have matched. If the matching device does not have - such an attribute, and a previous KERNELS, SUBSYSTEMS, DRIVERS, or - ATTRS test selected a parent device, then the attribute from that - parent device is used. - If the attribute is a symlink, the last element of the symlink target is - returned as the value. - - - - - , - - A device property value. - - - - - , - - The kernel major number for the device. - - - - - , - - The kernel minor number for the device. - - - - - , - - The string returned by the external program requested with PROGRAM. - A single part of the string, separated by a space character, may be selected - by specifying the part number as an attribute: . - If the number is followed by the '+' character, this part plus all remaining parts - of the result string are substituted: - - - - - , - - The node name of the parent device. - - - - - - - The current name of the device. If not changed by a rule, it is the - name of the kernel device. - - - - - - - A space-separated list of the current symlinks. The value is - only set during a remove event or if an earlier rule assigned a value. - - - - - , - - The udev_root value. - - - - - , - - The sysfs mount point. - - - - - , - - The name of the device node. - - - - - - - The '%' character itself. - - - - - - - The '$' character itself. - - - - - - - Author - Written by Greg Kroah-Hartman greg@kroah.com and - Kay Sievers kay.sievers@vrfy.org. With much help from - Dan Stekloff and many others. - - - - See Also - - udevd8 - , - - udevadm8 - - - diff --git a/src/udev/src/udevadm-control.c b/src/udev/src/udevadm-control.c deleted file mode 100644 index cafa214944..0000000000 --- a/src/udev/src/udevadm-control.c +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) 2005-2011 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -static void print_help(void) -{ - printf("Usage: udevadm control COMMAND\n" - " --exit instruct the daemon to cleanup and exit\n" - " --log-priority= set the udev log level for the daemon\n" - " --stop-exec-queue do not execute events, queue only\n" - " --start-exec-queue execute events, flush queue\n" - " --reload reload rules and databases\n" - " --property== set a global property for all events\n" - " --children-max= maximum number of children\n" - " --timeout= maximum time to block for a reply\n" - " --help print this help text\n\n"); -} - -static int adm_control(struct udev *udev, int argc, char *argv[]) -{ - struct udev_ctrl *uctrl = NULL; - int timeout = 60; - int rc = 1; - - static const struct option options[] = { - { "exit", no_argument, NULL, 'e' }, - { "log-priority", required_argument, NULL, 'l' }, - { "stop-exec-queue", no_argument, NULL, 's' }, - { "start-exec-queue", no_argument, NULL, 'S' }, - { "reload", no_argument, NULL, 'R' }, - { "reload-rules", no_argument, NULL, 'R' }, - { "property", required_argument, NULL, 'p' }, - { "env", required_argument, NULL, 'p' }, - { "children-max", required_argument, NULL, 'm' }, - { "timeout", required_argument, NULL, 't' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - - if (getuid() != 0) { - fprintf(stderr, "root privileges required\n"); - return 1; - } - - uctrl = udev_ctrl_new(udev); - if (uctrl == NULL) - return 2; - - for (;;) { - int option; - - option = getopt_long(argc, argv, "el:sSRp:m:h", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'e': - if (udev_ctrl_send_exit(uctrl, timeout) < 0) - rc = 2; - else - rc = 0; - break; - case 'l': { - int i; - - i = util_log_priority(optarg); - if (i < 0) { - fprintf(stderr, "invalid number '%s'\n", optarg); - goto out; - } - if (udev_ctrl_send_set_log_level(uctrl, util_log_priority(optarg), timeout) < 0) - rc = 2; - else - rc = 0; - break; - } - case 's': - if (udev_ctrl_send_stop_exec_queue(uctrl, timeout) < 0) - rc = 2; - else - rc = 0; - break; - case 'S': - if (udev_ctrl_send_start_exec_queue(uctrl, timeout) < 0) - rc = 2; - else - rc = 0; - break; - case 'R': - if (udev_ctrl_send_reload(uctrl, timeout) < 0) - rc = 2; - else - rc = 0; - break; - case 'p': - if (strchr(optarg, '=') == NULL) { - fprintf(stderr, "expect = instead of '%s'\n", optarg); - goto out; - } - if (udev_ctrl_send_set_env(uctrl, optarg, timeout) < 0) - rc = 2; - else - rc = 0; - break; - case 'm': { - char *endp; - int i; - - i = strtoul(optarg, &endp, 0); - if (endp[0] != '\0' || i < 1) { - fprintf(stderr, "invalid number '%s'\n", optarg); - goto out; - } - if (udev_ctrl_send_set_children_max(uctrl, i, timeout) < 0) - rc = 2; - else - rc = 0; - break; - } - case 't': { - int seconds; - - seconds = atoi(optarg); - if (seconds >= 0) - timeout = seconds; - else - fprintf(stderr, "invalid timeout value\n"); - break; - } - case 'h': - print_help(); - rc = 0; - break; - } - } - - if (argv[optind] != NULL) - fprintf(stderr, "unknown option\n"); - else if (optind == 1) - fprintf(stderr, "missing option\n"); -out: - udev_ctrl_unref(uctrl); - return rc; -} - -const struct udevadm_cmd udevadm_control = { - .name = "control", - .cmd = adm_control, - .help = "control the udev daemon", -}; diff --git a/src/udev/src/udevadm-info.c b/src/udev/src/udevadm-info.c deleted file mode 100644 index ee9b59fea8..0000000000 --- a/src/udev/src/udevadm-info.c +++ /dev/null @@ -1,568 +0,0 @@ -/* - * Copyright (C) 2004-2009 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -static bool skip_attribute(const char *name) -{ - static const char const *skip[] = { - "uevent", - "dev", - "modalias", - "resource", - "driver", - "subsystem", - "module", - }; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(skip); i++) - if (strcmp(name, skip[i]) == 0) - return true; - return false; -} - -static void print_all_attributes(struct udev_device *device, const char *key) -{ - struct udev *udev = udev_device_get_udev(device); - struct udev_list_entry *sysattr; - - udev_list_entry_foreach(sysattr, udev_device_get_sysattr_list_entry(device)) { - const char *name; - const char *value; - size_t len; - - name = udev_list_entry_get_name(sysattr); - if (skip_attribute(name)) - continue; - - value = udev_device_get_sysattr_value(device, name); - if (value == NULL) - continue; - dbg(udev, "attr '%s'='%s'\n", name, value); - - /* skip any values that look like a path */ - if (value[0] == '/') - continue; - - /* skip nonprintable attributes */ - len = strlen(value); - while (len > 0 && isprint(value[len-1])) - len--; - if (len > 0) { - dbg(udev, "attribute value of '%s' non-printable, skip\n", name); - continue; - } - - printf(" %s{%s}==\"%s\"\n", key, name, value); - } - printf("\n"); -} - -static int print_device_chain(struct udev_device *device) -{ - struct udev_device *device_parent; - const char *str; - - printf("\n" - "Udevadm info starts with the device specified by the devpath and then\n" - "walks up the chain of parent devices. It prints for every device\n" - "found, all possible attributes in the udev rules key format.\n" - "A rule to match, can be composed by the attributes of the device\n" - "and the attributes from one single parent device.\n" - "\n"); - - printf(" looking at device '%s':\n", udev_device_get_devpath(device)); - printf(" KERNEL==\"%s\"\n", udev_device_get_sysname(device)); - str = udev_device_get_subsystem(device); - if (str == NULL) - str = ""; - printf(" SUBSYSTEM==\"%s\"\n", str); - str = udev_device_get_driver(device); - if (str == NULL) - str = ""; - printf(" DRIVER==\"%s\"\n", str); - print_all_attributes(device, "ATTR"); - - device_parent = device; - do { - device_parent = udev_device_get_parent(device_parent); - if (device_parent == NULL) - break; - printf(" looking at parent device '%s':\n", udev_device_get_devpath(device_parent)); - printf(" KERNELS==\"%s\"\n", udev_device_get_sysname(device_parent)); - str = udev_device_get_subsystem(device_parent); - if (str == NULL) - str = ""; - printf(" SUBSYSTEMS==\"%s\"\n", str); - str = udev_device_get_driver(device_parent); - if (str == NULL) - str = ""; - printf(" DRIVERS==\"%s\"\n", str); - print_all_attributes(device_parent, "ATTRS"); - } while (device_parent != NULL); - - return 0; -} - -static void print_record(struct udev_device *device) -{ - size_t len; - const char *str; - int i; - struct udev_list_entry *list_entry; - - printf("P: %s\n", udev_device_get_devpath(device)); - - len = strlen(udev_get_dev_path(udev_device_get_udev(device))); - str = udev_device_get_devnode(device); - if (str != NULL) - printf("N: %s\n", &str[len+1]); - - i = udev_device_get_devlink_priority(device); - if (i != 0) - printf("L: %i\n", i); - - udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device)) { - len = strlen(udev_get_dev_path(udev_device_get_udev(device))); - printf("S: %s\n", &udev_list_entry_get_name(list_entry)[len+1]); - } - - udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device)) - printf("E: %s=%s\n", - udev_list_entry_get_name(list_entry), - udev_list_entry_get_value(list_entry)); - printf("\n"); -} - -static int stat_device(const char *name, bool export, const char *prefix) -{ - struct stat statbuf; - - if (stat(name, &statbuf) != 0) - return -1; - - if (export) { - if (prefix == NULL) - prefix = "INFO_"; - printf("%sMAJOR=%d\n" - "%sMINOR=%d\n", - prefix, major(statbuf.st_dev), - prefix, minor(statbuf.st_dev)); - } else - printf("%d:%d\n", major(statbuf.st_dev), minor(statbuf.st_dev)); - return 0; -} - -static int export_devices(struct udev *udev) -{ - struct udev_enumerate *udev_enumerate; - struct udev_list_entry *list_entry; - - udev_enumerate = udev_enumerate_new(udev); - if (udev_enumerate == NULL) - return -1; - udev_enumerate_scan_devices(udev_enumerate); - udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) { - struct udev_device *device; - - device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry)); - if (device != NULL) { - print_record(device); - udev_device_unref(device); - } - } - udev_enumerate_unref(udev_enumerate); - return 0; -} - -static void cleanup_dir(DIR *dir, mode_t mask, int depth) -{ - struct dirent *dent; - - if (depth <= 0) - return; - - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - struct stat stats; - - if (dent->d_name[0] == '.') - continue; - if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0) - continue; - if ((stats.st_mode & mask) != 0) - continue; - if (S_ISDIR(stats.st_mode)) { - DIR *dir2; - - dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)); - if (dir2 != NULL) { - cleanup_dir(dir2, mask, depth-1); - closedir(dir2); - } - unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR); - } else { - unlinkat(dirfd(dir), dent->d_name, 0); - } - } -} - -static void cleanup_db(struct udev *udev) -{ - char filename[UTIL_PATH_SIZE]; - DIR *dir; - - util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/queue.bin", NULL); - unlink(filename); - - util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/data", NULL); - dir = opendir(filename); - if (dir != NULL) { - cleanup_dir(dir, S_ISVTX, 1); - closedir(dir); - } - - util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/links", NULL); - dir = opendir(filename); - if (dir != NULL) { - cleanup_dir(dir, 0, 2); - closedir(dir); - } - - util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/tags", NULL); - dir = opendir(filename); - if (dir != NULL) { - cleanup_dir(dir, 0, 2); - closedir(dir); - } - - util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/watch", NULL); - dir = opendir(filename); - if (dir != NULL) { - cleanup_dir(dir, 0, 1); - closedir(dir); - } - - util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/firmware-missing", NULL); - dir = opendir(filename); - if (dir != NULL) { - cleanup_dir(dir, 0, 1); - closedir(dir); - } -} - -static int uinfo(struct udev *udev, int argc, char *argv[]) -{ - struct udev_device *device = NULL; - bool root = 0; - bool export = 0; - const char *export_prefix = NULL; - char path[UTIL_PATH_SIZE]; - char name[UTIL_PATH_SIZE]; - struct udev_list_entry *list_entry; - int rc = 0; - - static const struct option options[] = { - { "name", required_argument, NULL, 'n' }, - { "path", required_argument, NULL, 'p' }, - { "query", required_argument, NULL, 'q' }, - { "attribute-walk", no_argument, NULL, 'a' }, - { "cleanup-db", no_argument, NULL, 'c' }, - { "export-db", no_argument, NULL, 'e' }, - { "root", no_argument, NULL, 'r' }, - { "run", no_argument, NULL, 'R' }, - { "device-id-of-file", required_argument, NULL, 'd' }, - { "export", no_argument, NULL, 'x' }, - { "export-prefix", required_argument, NULL, 'P' }, - { "version", no_argument, NULL, 'V' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - - enum action_type { - ACTION_NONE, - ACTION_QUERY, - ACTION_ATTRIBUTE_WALK, - ACTION_ROOT, - ACTION_DEVICE_ID_FILE, - } action = ACTION_NONE; - - enum query_type { - QUERY_NONE, - QUERY_NAME, - QUERY_PATH, - QUERY_SYMLINK, - QUERY_PROPERTY, - QUERY_ALL, - } query = QUERY_NONE; - - for (;;) { - int option; - struct stat statbuf; - - option = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL); - if (option == -1) - break; - - dbg(udev, "option '%c'\n", option); - switch (option) { - case 'n': - if (device != NULL) { - fprintf(stderr, "device already specified\n"); - rc = 2; - goto exit; - } - /* remove /dev if given */ - if (strncmp(optarg, udev_get_dev_path(udev), strlen(udev_get_dev_path(udev))) != 0) - util_strscpyl(name, sizeof(name), udev_get_dev_path(udev), "/", optarg, NULL); - else - util_strscpy(name, sizeof(name), optarg); - util_remove_trailing_chars(name, '/'); - if (stat(name, &statbuf) < 0) { - fprintf(stderr, "device node not found\n"); - rc = 2; - goto exit; - } else { - char type; - - if (S_ISBLK(statbuf.st_mode)) { - type = 'b'; - } else if (S_ISCHR(statbuf.st_mode)) { - type = 'c'; - } else { - fprintf(stderr, "device node has wrong file type\n"); - rc = 2; - goto exit; - } - device = udev_device_new_from_devnum(udev, type, statbuf.st_rdev); - if (device == NULL) { - fprintf(stderr, "device node not found\n"); - rc = 2; - goto exit; - } - } - break; - case 'p': - if (device != NULL) { - fprintf(stderr, "device already specified\n"); - rc = 2; - goto exit; - } - /* add sys dir if needed */ - if (strncmp(optarg, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) != 0) - util_strscpyl(path, sizeof(path), udev_get_sys_path(udev), optarg, NULL); - else - util_strscpy(path, sizeof(path), optarg); - util_remove_trailing_chars(path, '/'); - device = udev_device_new_from_syspath(udev, path); - if (device == NULL) { - fprintf(stderr, "device path not found\n"); - rc = 2; - goto exit; - } - break; - case 'q': - action = ACTION_QUERY; - if (strcmp(optarg, "property") == 0 || strcmp(optarg, "env") == 0) { - query = QUERY_PROPERTY; - } else if (strcmp(optarg, "name") == 0) { - query = QUERY_NAME; - } else if (strcmp(optarg, "symlink") == 0) { - query = QUERY_SYMLINK; - } else if (strcmp(optarg, "path") == 0) { - query = QUERY_PATH; - } else if (strcmp(optarg, "all") == 0) { - query = QUERY_ALL; - } else { - fprintf(stderr, "unknown query type\n"); - rc = 3; - goto exit; - } - break; - case 'r': - if (action == ACTION_NONE) - action = ACTION_ROOT; - root = true; - break; - case 'R': - printf("%s\n", udev_get_run_path(udev)); - goto exit; - case 'd': - action = ACTION_DEVICE_ID_FILE; - util_strscpy(name, sizeof(name), optarg); - break; - case 'a': - action = ACTION_ATTRIBUTE_WALK; - break; - case 'e': - export_devices(udev); - goto exit; - case 'c': - cleanup_db(udev); - goto exit; - case 'x': - export = true; - break; - case 'P': - export_prefix = optarg; - break; - case 'V': - printf("%s\n", VERSION); - goto exit; - case 'h': - printf("Usage: udevadm info OPTIONS\n" - " --query= query device information:\n" - " name name of device node\n" - " symlink pointing to node\n" - " path sys device path\n" - " property the device properties\n" - " all all values\n" - " --path= sys device path used for query or attribute walk\n" - " --name= node or symlink name used for query or attribute walk\n" - " --root prepend dev directory to path names\n" - " --attribute-walk print all key matches while walking along the chain\n" - " of parent devices\n" - " --device-id-of-file= print major:minor of device containing this file\n" - " --export export key/value pairs\n" - " --export-prefix export the key name with a prefix\n" - " --export-db export the content of the udev database\n" - " --cleanup-db cleanup the udev database\n" - " --help\n\n"); - goto exit; - default: - rc = 1; - goto exit; - } - } - - switch (action) { - case ACTION_QUERY: - if (device == NULL) { - fprintf(stderr, "query needs a valid device specified by --path= or --name=\n"); - rc = 4; - goto exit; - } - - switch(query) { - case QUERY_NAME: { - const char *node = udev_device_get_devnode(device); - - if (node == NULL) { - fprintf(stderr, "no device node found\n"); - rc = 5; - goto exit; - } - - if (root) { - printf("%s\n", udev_device_get_devnode(device)); - } else { - size_t len = strlen(udev_get_dev_path(udev)); - - printf("%s\n", &udev_device_get_devnode(device)[len+1]); - } - break; - } - case QUERY_SYMLINK: - list_entry = udev_device_get_devlinks_list_entry(device); - while (list_entry != NULL) { - if (root) { - printf("%s", udev_list_entry_get_name(list_entry)); - } else { - size_t len; - - len = strlen(udev_get_dev_path(udev_device_get_udev(device))); - printf("%s", &udev_list_entry_get_name(list_entry)[len+1]); - } - list_entry = udev_list_entry_get_next(list_entry); - if (list_entry != NULL) - printf(" "); - } - printf("\n"); - break; - case QUERY_PATH: - printf("%s\n", udev_device_get_devpath(device)); - goto exit; - case QUERY_PROPERTY: - list_entry = udev_device_get_properties_list_entry(device); - while (list_entry != NULL) { - if (export) { - const char *prefix = export_prefix; - - if (prefix == NULL) - prefix = ""; - printf("%s%s='%s'\n", prefix, - udev_list_entry_get_name(list_entry), - udev_list_entry_get_value(list_entry)); - } else { - printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry)); - } - list_entry = udev_list_entry_get_next(list_entry); - } - break; - case QUERY_ALL: - print_record(device); - break; - default: - fprintf(stderr, "unknown query type\n"); - break; - } - break; - case ACTION_ATTRIBUTE_WALK: - if (device == NULL) { - fprintf(stderr, "query needs a valid device specified by --path= or --name=\n"); - rc = 4; - goto exit; - } - print_device_chain(device); - break; - case ACTION_DEVICE_ID_FILE: - if (stat_device(name, export, export_prefix) != 0) - rc = 1; - break; - case ACTION_ROOT: - printf("%s\n", udev_get_dev_path(udev)); - break; - default: - fprintf(stderr, "missing option\n"); - rc = 1; - break; - } - -exit: - udev_device_unref(device); - return rc; -} - -const struct udevadm_cmd udevadm_info = { - .name = "info", - .cmd = uinfo, - .help = "query sysfs or the udev database", -}; diff --git a/src/udev/src/udevadm-monitor.c b/src/udev/src/udevadm-monitor.c deleted file mode 100644 index 5997dd8e18..0000000000 --- a/src/udev/src/udevadm-monitor.c +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright (C) 2004-2010 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -static bool udev_exit; - -static void sig_handler(int signum) -{ - if (signum == SIGINT || signum == SIGTERM) - udev_exit = true; -} - -static void print_device(struct udev_device *device, const char *source, int prop) -{ - struct timespec ts; - - clock_gettime(CLOCK_MONOTONIC, &ts); - printf("%-6s[%llu.%06u] %-8s %s (%s)\n", - source, - (unsigned long long) ts.tv_sec, (unsigned int) ts.tv_nsec/1000, - udev_device_get_action(device), - udev_device_get_devpath(device), - udev_device_get_subsystem(device)); - if (prop) { - struct udev_list_entry *list_entry; - - udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device)) - printf("%s=%s\n", - udev_list_entry_get_name(list_entry), - udev_list_entry_get_value(list_entry)); - printf("\n"); - } -} - -static int adm_monitor(struct udev *udev, int argc, char *argv[]) -{ - struct sigaction act; - sigset_t mask; - int option; - bool prop = false; - bool print_kernel = false; - bool print_udev = false; - struct udev_list subsystem_match_list; - struct udev_list tag_match_list; - struct udev_monitor *udev_monitor = NULL; - struct udev_monitor *kernel_monitor = NULL; - int fd_ep = -1; - int fd_kernel = -1, fd_udev = -1; - struct epoll_event ep_kernel, ep_udev; - int rc = 0; - - static const struct option options[] = { - { "property", no_argument, NULL, 'p' }, - { "environment", no_argument, NULL, 'e' }, - { "kernel", no_argument, NULL, 'k' }, - { "udev", no_argument, NULL, 'u' }, - { "subsystem-match", required_argument, NULL, 's' }, - { "tag-match", required_argument, NULL, 't' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - - udev_list_init(udev, &subsystem_match_list, true); - udev_list_init(udev, &tag_match_list, true); - - for (;;) { - option = getopt_long(argc, argv, "pekus:t:h", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'p': - case 'e': - prop = true; - break; - case 'k': - print_kernel = true; - break; - case 'u': - print_udev = true; - break; - case 's': - { - char subsys[UTIL_NAME_SIZE]; - char *devtype; - - util_strscpy(subsys, sizeof(subsys), optarg); - devtype = strchr(subsys, '/'); - if (devtype != NULL) { - devtype[0] = '\0'; - devtype++; - } - udev_list_entry_add(&subsystem_match_list, subsys, devtype); - break; - } - case 't': - udev_list_entry_add(&tag_match_list, optarg, NULL); - break; - case 'h': - printf("Usage: udevadm monitor [--property] [--kernel] [--udev] [--help]\n" - " --property print the event properties\n" - " --kernel print kernel uevents\n" - " --udev print udev events\n" - " --subsystem-match= filter events by subsystem\n" - " --tag-match= filter events by tag\n" - " --help\n\n"); - goto out; - default: - rc = 1; - goto out; - } - } - - if (!print_kernel && !print_udev) { - print_kernel = true; - print_udev = true; - } - - /* set signal handlers */ - memset(&act, 0x00, sizeof(struct sigaction)); - act.sa_handler = sig_handler; - sigemptyset(&act.sa_mask); - act.sa_flags = SA_RESTART; - sigaction(SIGINT, &act, NULL); - sigaction(SIGTERM, &act, NULL); - sigemptyset(&mask); - sigaddset(&mask, SIGINT); - sigaddset(&mask, SIGTERM); - sigprocmask(SIG_UNBLOCK, &mask, NULL); - - fd_ep = epoll_create1(EPOLL_CLOEXEC); - if (fd_ep < 0) { - err(udev, "error creating epoll fd: %m\n"); - goto out; - } - - printf("monitor will print the received events for:\n"); - if (print_udev) { - struct udev_list_entry *entry; - - udev_monitor = udev_monitor_new_from_netlink(udev, "udev"); - if (udev_monitor == NULL) { - fprintf(stderr, "error: unable to create netlink socket\n"); - rc = 1; - goto out; - } - udev_monitor_set_receive_buffer_size(udev_monitor, 128*1024*1024); - fd_udev = udev_monitor_get_fd(udev_monitor); - - udev_list_entry_foreach(entry, udev_list_get_entry(&subsystem_match_list)) { - const char *subsys = udev_list_entry_get_name(entry); - const char *devtype = udev_list_entry_get_value(entry); - - if (udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, subsys, devtype) < 0) - fprintf(stderr, "error: unable to apply subsystem filter '%s'\n", subsys); - } - - udev_list_entry_foreach(entry, udev_list_get_entry(&tag_match_list)) { - const char *tag = udev_list_entry_get_name(entry); - - if (udev_monitor_filter_add_match_tag(udev_monitor, tag) < 0) - fprintf(stderr, "error: unable to apply tag filter '%s'\n", tag); - } - - if (udev_monitor_enable_receiving(udev_monitor) < 0) { - fprintf(stderr, "error: unable to subscribe to udev events\n"); - rc = 2; - goto out; - } - - memset(&ep_udev, 0, sizeof(struct epoll_event)); - ep_udev.events = EPOLLIN; - ep_udev.data.fd = fd_udev; - if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_udev, &ep_udev) < 0) { - err(udev, "fail to add fd to epoll: %m\n"); - goto out; - } - - printf("UDEV - the event which udev sends out after rule processing\n"); - } - - if (print_kernel) { - struct udev_list_entry *entry; - - kernel_monitor = udev_monitor_new_from_netlink(udev, "kernel"); - if (kernel_monitor == NULL) { - fprintf(stderr, "error: unable to create netlink socket\n"); - rc = 3; - goto out; - } - udev_monitor_set_receive_buffer_size(kernel_monitor, 128*1024*1024); - fd_kernel = udev_monitor_get_fd(kernel_monitor); - - udev_list_entry_foreach(entry, udev_list_get_entry(&subsystem_match_list)) { - const char *subsys = udev_list_entry_get_name(entry); - - if (udev_monitor_filter_add_match_subsystem_devtype(kernel_monitor, subsys, NULL) < 0) - fprintf(stderr, "error: unable to apply subsystem filter '%s'\n", subsys); - } - - if (udev_monitor_enable_receiving(kernel_monitor) < 0) { - fprintf(stderr, "error: unable to subscribe to kernel events\n"); - rc = 4; - goto out; - } - - memset(&ep_kernel, 0, sizeof(struct epoll_event)); - ep_kernel.events = EPOLLIN; - ep_kernel.data.fd = fd_kernel; - if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_kernel, &ep_kernel) < 0) { - err(udev, "fail to add fd to epoll: %m\n"); - goto out; - } - - printf("KERNEL - the kernel uevent\n"); - } - printf("\n"); - - while (!udev_exit) { - int fdcount; - struct epoll_event ev[4]; - int i; - - fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), -1); - if (fdcount < 0) { - if (errno != EINTR) - fprintf(stderr, "error receiving uevent message: %m\n"); - continue; - } - - for (i = 0; i < fdcount; i++) { - if (ev[i].data.fd == fd_kernel && ev[i].events & EPOLLIN) { - struct udev_device *device; - - device = udev_monitor_receive_device(kernel_monitor); - if (device == NULL) - continue; - print_device(device, "KERNEL", prop); - udev_device_unref(device); - } else if (ev[i].data.fd == fd_udev && ev[i].events & EPOLLIN) { - struct udev_device *device; - - device = udev_monitor_receive_device(udev_monitor); - if (device == NULL) - continue; - print_device(device, "UDEV", prop); - udev_device_unref(device); - } - } - } -out: - if (fd_ep >= 0) - close(fd_ep); - udev_monitor_unref(udev_monitor); - udev_monitor_unref(kernel_monitor); - udev_list_cleanup(&subsystem_match_list); - udev_list_cleanup(&tag_match_list); - return rc; -} - -const struct udevadm_cmd udevadm_monitor = { - .name = "monitor", - .cmd = adm_monitor, - .help = "listen to kernel and udev events", -}; diff --git a/src/udev/src/udevadm-settle.c b/src/udev/src/udevadm-settle.c deleted file mode 100644 index b168defd90..0000000000 --- a/src/udev/src/udevadm-settle.c +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright (C) 2006-2009 Kay Sievers - * Copyright (C) 2009 Canonical Ltd. - * Copyright (C) 2009 Scott James Remnant - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -static int adm_settle(struct udev *udev, int argc, char *argv[]) -{ - static const struct option options[] = { - { "seq-start", required_argument, NULL, 's' }, - { "seq-end", required_argument, NULL, 'e' }, - { "timeout", required_argument, NULL, 't' }, - { "exit-if-exists", required_argument, NULL, 'E' }, - { "quiet", no_argument, NULL, 'q' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - unsigned long long start_usec = now_usec(); - unsigned long long start = 0; - unsigned long long end = 0; - int quiet = 0; - const char *exists = NULL; - unsigned int timeout = 120; - struct pollfd pfd[1]; - struct udev_queue *udev_queue = NULL; - int rc = EXIT_FAILURE; - - dbg(udev, "version %s\n", VERSION); - - for (;;) { - int option; - int seconds; - - option = getopt_long(argc, argv, "s:e:t:E:qh", options, NULL); - if (option == -1) - break; - - switch (option) { - case 's': - start = strtoull(optarg, NULL, 0); - break; - case 'e': - end = strtoull(optarg, NULL, 0); - break; - case 't': - seconds = atoi(optarg); - if (seconds >= 0) - timeout = seconds; - else - fprintf(stderr, "invalid timeout value\n"); - dbg(udev, "timeout=%i\n", timeout); - break; - case 'q': - quiet = 1; - break; - case 'E': - exists = optarg; - break; - case 'h': - printf("Usage: udevadm settle OPTIONS\n" - " --timeout= maximum time to wait for events\n" - " --seq-start= first seqnum to wait for\n" - " --seq-end= last seqnum to wait for\n" - " --exit-if-exists= stop waiting if file exists\n" - " --quiet do not print list after timeout\n" - " --help\n\n"); - exit(EXIT_SUCCESS); - default: - exit(EXIT_FAILURE); - } - } - - udev_queue = udev_queue_new(udev); - if (udev_queue == NULL) - exit(2); - - if (start > 0) { - unsigned long long kernel_seq; - - kernel_seq = udev_queue_get_kernel_seqnum(udev_queue); - - /* unless specified, the last event is the current kernel seqnum */ - if (end == 0) - end = udev_queue_get_kernel_seqnum(udev_queue); - - if (start > end) { - err(udev, "seq-start larger than seq-end, ignoring\n"); - start = 0; - end = 0; - } - - if (start > kernel_seq || end > kernel_seq) { - err(udev, "seq-start or seq-end larger than current kernel value, ignoring\n"); - start = 0; - end = 0; - } - info(udev, "start=%llu end=%llu current=%llu\n", start, end, kernel_seq); - } else { - if (end > 0) { - err(udev, "seq-end needs seq-start parameter, ignoring\n"); - end = 0; - } - } - - /* guarantee that the udev daemon isn't pre-processing */ - if (getuid() == 0) { - struct udev_ctrl *uctrl; - - uctrl = udev_ctrl_new(udev); - if (uctrl != NULL) { - if (udev_ctrl_send_ping(uctrl, timeout) < 0) { - info(udev, "no connection to daemon\n"); - udev_ctrl_unref(uctrl); - rc = EXIT_SUCCESS; - goto out; - } - udev_ctrl_unref(uctrl); - } - } - - pfd[0].events = POLLIN; - pfd[0].fd = inotify_init1(IN_CLOEXEC); - if (pfd[0].fd < 0) { - err(udev, "inotify_init failed: %m\n"); - } else { - if (inotify_add_watch(pfd[0].fd, udev_get_run_path(udev), IN_MOVED_TO) < 0) { - err(udev, "watching '%s' failed\n", udev_get_run_path(udev)); - close(pfd[0].fd); - pfd[0].fd = -1; - } - } - - for (;;) { - struct stat statbuf; - - if (exists != NULL && stat(exists, &statbuf) == 0) { - rc = EXIT_SUCCESS; - break; - } - - if (start > 0) { - /* if asked for, wait for a specific sequence of events */ - if (udev_queue_get_seqnum_sequence_is_finished(udev_queue, start, end) == 1) { - rc = EXIT_SUCCESS; - break; - } - } else { - /* exit if queue is empty */ - if (udev_queue_get_queue_is_empty(udev_queue)) { - rc = EXIT_SUCCESS; - break; - } - } - - if (pfd[0].fd >= 0) { - int delay; - - if (exists != NULL || start > 0) - delay = 100; - else - delay = 1000; - /* wake up after delay, or immediately after the queue is rebuilt */ - if (poll(pfd, 1, delay) > 0 && pfd[0].revents & POLLIN) { - char buf[sizeof(struct inotify_event) + PATH_MAX]; - - read(pfd[0].fd, buf, sizeof(buf)); - } - } else { - sleep(1); - } - - if (timeout > 0) { - unsigned long long age_usec; - - age_usec = now_usec() - start_usec; - if (age_usec / (1000 * 1000) >= timeout) { - struct udev_list_entry *list_entry; - - if (!quiet && udev_queue_get_queued_list_entry(udev_queue) != NULL) { - info(udev, "timeout waiting for udev queue\n"); - printf("\nudevadm settle - timeout of %i seconds reached, the event queue contains:\n", timeout); - udev_list_entry_foreach(list_entry, udev_queue_get_queued_list_entry(udev_queue)) - printf(" %s (%s)\n", - udev_list_entry_get_name(list_entry), - udev_list_entry_get_value(list_entry)); - } - - break; - } - } - } -out: - if (pfd[0].fd >= 0) - close(pfd[0].fd); - udev_queue_unref(udev_queue); - return rc; -} - -const struct udevadm_cmd udevadm_settle = { - .name = "settle", - .cmd = adm_settle, - .help = "wait for the event queue to finish", -}; diff --git a/src/udev/src/udevadm-test-builtin.c b/src/udev/src/udevadm-test-builtin.c deleted file mode 100644 index 3a49f7ce9c..0000000000 --- a/src/udev/src/udevadm-test-builtin.c +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2011 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -static void help(struct udev *udev) -{ - fprintf(stderr, "\n"); - fprintf(stderr, "Usage: udevadm builtin [--help] \n"); - udev_builtin_list(udev); - fprintf(stderr, "\n"); -} - -static int adm_builtin(struct udev *udev, int argc, char *argv[]) -{ - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - {} - }; - char *command = NULL; - char *syspath = NULL; - char filename[UTIL_PATH_SIZE]; - struct udev_device *dev = NULL; - enum udev_builtin_cmd cmd; - int rc = EXIT_SUCCESS; - - dbg(udev, "version %s\n", VERSION); - - for (;;) { - int option; - - option = getopt_long(argc, argv, "h", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'h': - help(udev); - goto out; - } - } - - command = argv[optind++]; - if (command == NULL) { - fprintf(stderr, "command missing\n"); - help(udev); - rc = 2; - goto out; - } - - syspath = argv[optind++]; - if (syspath == NULL) { - fprintf(stderr, "syspath missing\n\n"); - rc = 3; - goto out; - } - - udev_builtin_init(udev); - - cmd = udev_builtin_lookup(command); - if (cmd >= UDEV_BUILTIN_MAX) { - fprintf(stderr, "unknown command '%s'\n", command); - help(udev); - rc = 5; - goto out; - } - - /* add /sys if needed */ - if (strncmp(syspath, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) != 0) - util_strscpyl(filename, sizeof(filename), udev_get_sys_path(udev), syspath, NULL); - else - util_strscpy(filename, sizeof(filename), syspath); - util_remove_trailing_chars(filename, '/'); - - dev = udev_device_new_from_syspath(udev, filename); - if (dev == NULL) { - fprintf(stderr, "unable to open device '%s'\n\n", filename); - rc = 4; - goto out; - } - - if (udev_builtin_run(dev, cmd, command, true) < 0) { - fprintf(stderr, "error executing '%s'\n\n", command); - rc = 6; - } -out: - udev_device_unref(dev); - udev_builtin_exit(udev); - return rc; -} - -const struct udevadm_cmd udevadm_test_builtin = { - .name = "test-builtin", - .cmd = adm_builtin, - .help = "test a built-in command", - .debug = true, -}; diff --git a/src/udev/src/udevadm-test.c b/src/udev/src/udevadm-test.c deleted file mode 100644 index 6275cff899..0000000000 --- a/src/udev/src/udevadm-test.c +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (C) 2003-2004 Greg Kroah-Hartman - * Copyright (C) 2004-2008 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -static int adm_test(struct udev *udev, int argc, char *argv[]) -{ - int resolve_names = 1; - char filename[UTIL_PATH_SIZE]; - const char *action = "add"; - const char *syspath = NULL; - struct udev_event *event = NULL; - struct udev_device *dev = NULL; - struct udev_rules *rules = NULL; - struct udev_list_entry *entry; - sigset_t mask, sigmask_orig; - int err; - int rc = 0; - - static const struct option options[] = { - { "action", required_argument, NULL, 'a' }, - { "resolve-names", required_argument, NULL, 'N' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - - info(udev, "version %s\n", VERSION); - - for (;;) { - int option; - - option = getopt_long(argc, argv, "a:s:N:fh", options, NULL); - if (option == -1) - break; - - dbg(udev, "option '%c'\n", option); - switch (option) { - case 'a': - action = optarg; - break; - case 'N': - if (strcmp (optarg, "early") == 0) { - resolve_names = 1; - } else if (strcmp (optarg, "late") == 0) { - resolve_names = 0; - } else if (strcmp (optarg, "never") == 0) { - resolve_names = -1; - } else { - fprintf(stderr, "resolve-names must be early, late or never\n"); - err(udev, "resolve-names must be early, late or never\n"); - exit(EXIT_FAILURE); - } - break; - case 'h': - printf("Usage: udevadm test OPTIONS \n" - " --action= set action string\n" - " --help\n\n"); - exit(EXIT_SUCCESS); - default: - exit(EXIT_FAILURE); - } - } - syspath = argv[optind]; - - if (syspath == NULL) { - fprintf(stderr, "syspath parameter missing\n"); - rc = 2; - goto out; - } - - printf("This program is for debugging only, it does not run any program,\n" - "specified by a RUN key. It may show incorrect results, because\n" - "some values may be different, or not available at a simulation run.\n" - "\n"); - - sigprocmask(SIG_SETMASK, NULL, &sigmask_orig); - - udev_builtin_init(udev); - - rules = udev_rules_new(udev, resolve_names); - if (rules == NULL) { - fprintf(stderr, "error reading rules\n"); - rc = 3; - goto out; - } - - /* add /sys if needed */ - if (strncmp(syspath, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) != 0) - util_strscpyl(filename, sizeof(filename), udev_get_sys_path(udev), syspath, NULL); - else - util_strscpy(filename, sizeof(filename), syspath); - util_remove_trailing_chars(filename, '/'); - - dev = udev_device_new_from_syspath(udev, filename); - if (dev == NULL) { - fprintf(stderr, "unable to open device '%s'\n", filename); - rc = 4; - goto out; - } - - /* skip reading of db, but read kernel parameters */ - udev_device_set_info_loaded(dev); - udev_device_read_uevent_file(dev); - - udev_device_set_action(dev, action); - event = udev_event_new(dev); - - sigfillset(&mask); - sigprocmask(SIG_SETMASK, &mask, &sigmask_orig); - event->fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); - if (event->fd_signal < 0) { - fprintf(stderr, "error creating signalfd\n"); - rc = 5; - goto out; - } - - err = udev_event_execute_rules(event, rules, &sigmask_orig); - - udev_list_entry_foreach(entry, udev_device_get_properties_list_entry(dev)) - printf("%s=%s\n", udev_list_entry_get_name(entry), udev_list_entry_get_value(entry)); - - if (err == 0) { - udev_list_entry_foreach(entry, udev_list_get_entry(&event->run_list)) { - char program[UTIL_PATH_SIZE]; - - udev_event_apply_format(event, udev_list_entry_get_name(entry), program, sizeof(program)); - printf("run: '%s'\n", program); - } - } -out: - if (event != NULL && event->fd_signal >= 0) - close(event->fd_signal); - udev_event_unref(event); - udev_device_unref(dev); - udev_rules_unref(rules); - udev_builtin_exit(udev); - return rc; -} - -const struct udevadm_cmd udevadm_test = { - .name = "test", - .cmd = adm_test, - .help = "test an event run", - .debug = true, -}; diff --git a/src/udev/src/udevadm-trigger.c b/src/udev/src/udevadm-trigger.c deleted file mode 100644 index 3cce23dfb2..0000000000 --- a/src/udev/src/udevadm-trigger.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (C) 2008-2009 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -static int verbose; -static int dry_run; - -static void exec_list(struct udev_enumerate *udev_enumerate, const char *action) -{ - struct udev *udev = udev_enumerate_get_udev(udev_enumerate); - struct udev_list_entry *entry; - - udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(udev_enumerate)) { - char filename[UTIL_PATH_SIZE]; - int fd; - - if (verbose) - printf("%s\n", udev_list_entry_get_name(entry)); - if (dry_run) - continue; - util_strscpyl(filename, sizeof(filename), udev_list_entry_get_name(entry), "/uevent", NULL); - fd = open(filename, O_WRONLY); - if (fd < 0) { - dbg(udev, "error on opening %s: %m\n", filename); - continue; - } - if (write(fd, action, strlen(action)) < 0) - info(udev, "error writing '%s' to '%s': %m\n", action, filename); - close(fd); - } -} - -static const char *keyval(const char *str, const char **val, char *buf, size_t size) -{ - char *pos; - - util_strscpy(buf, size,str); - pos = strchr(buf, '='); - if (pos != NULL) { - pos[0] = 0; - pos++; - } - *val = pos; - return buf; -} - -static int adm_trigger(struct udev *udev, int argc, char *argv[]) -{ - static const struct option options[] = { - { "verbose", no_argument, NULL, 'v' }, - { "dry-run", no_argument, NULL, 'n' }, - { "type", required_argument, NULL, 't' }, - { "action", required_argument, NULL, 'c' }, - { "subsystem-match", required_argument, NULL, 's' }, - { "subsystem-nomatch", required_argument, NULL, 'S' }, - { "attr-match", required_argument, NULL, 'a' }, - { "attr-nomatch", required_argument, NULL, 'A' }, - { "property-match", required_argument, NULL, 'p' }, - { "tag-match", required_argument, NULL, 'g' }, - { "sysname-match", required_argument, NULL, 'y' }, - { "parent-match", required_argument, NULL, 'b' }, - { "help", no_argument, NULL, 'h' }, - {} - }; - enum { - TYPE_DEVICES, - TYPE_SUBSYSTEMS, - } device_type = TYPE_DEVICES; - const char *action = "change"; - struct udev_enumerate *udev_enumerate; - int rc = 0; - - dbg(udev, "version %s\n", VERSION); - udev_enumerate = udev_enumerate_new(udev); - if (udev_enumerate == NULL) { - rc = 1; - goto exit; - } - - for (;;) { - int option; - const char *key; - const char *val; - char buf[UTIL_PATH_SIZE]; - - option = getopt_long(argc, argv, "vng:o:t:hc:p:s:S:a:A:y:b:", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'v': - verbose = 1; - break; - case 'n': - dry_run = 1; - break; - case 't': - if (strcmp(optarg, "devices") == 0) { - device_type = TYPE_DEVICES; - } else if (strcmp(optarg, "subsystems") == 0) { - device_type = TYPE_SUBSYSTEMS; - } else { - err(udev, "unknown type --type=%s\n", optarg); - rc = 2; - goto exit; - } - break; - case 'c': - action = optarg; - break; - case 's': - udev_enumerate_add_match_subsystem(udev_enumerate, optarg); - break; - case 'S': - udev_enumerate_add_nomatch_subsystem(udev_enumerate, optarg); - break; - case 'a': - key = keyval(optarg, &val, buf, sizeof(buf)); - udev_enumerate_add_match_sysattr(udev_enumerate, key, val); - break; - case 'A': - key = keyval(optarg, &val, buf, sizeof(buf)); - udev_enumerate_add_nomatch_sysattr(udev_enumerate, key, val); - break; - case 'p': - key = keyval(optarg, &val, buf, sizeof(buf)); - udev_enumerate_add_match_property(udev_enumerate, key, val); - break; - case 'g': - udev_enumerate_add_match_tag(udev_enumerate, optarg); - break; - case 'y': - udev_enumerate_add_match_sysname(udev_enumerate, optarg); - break; - case 'b': { - char path[UTIL_PATH_SIZE]; - struct udev_device *dev; - - /* add sys dir if needed */ - if (strncmp(optarg, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) != 0) - util_strscpyl(path, sizeof(path), udev_get_sys_path(udev), optarg, NULL); - else - util_strscpy(path, sizeof(path), optarg); - util_remove_trailing_chars(path, '/'); - dev = udev_device_new_from_syspath(udev, path); - if (dev == NULL) { - err(udev, "unable to open the device '%s'\n", optarg); - rc = 2; - goto exit; - } - udev_enumerate_add_match_parent(udev_enumerate, dev); - /* drop reference immediately, enumerate pins the device as long as needed */ - udev_device_unref(dev); - break; - } - case 'h': - printf("Usage: udevadm trigger OPTIONS\n" - " --verbose print the list of devices while running\n" - " --dry-run do not actually trigger the events\n" - " --type= type of events to trigger\n" - " devices sys devices (default)\n" - " subsystems sys subsystems and drivers\n" - " --action= event action value, default is \"change\"\n" - " --subsystem-match= trigger devices from a matching subsystem\n" - " --subsystem-nomatch= exclude devices from a matching subsystem\n" - " --attr-match=]> trigger devices with a matching attribute\n" - " --attr-nomatch=]> exclude devices with a matching attribute\n" - " --property-match== trigger devices with a matching property\n" - " --tag-match== trigger devices with a matching property\n" - " --sysname-match= trigger devices with a matching name\n" - " --parent-match= trigger devices with that parent device\n" - " --help\n\n"); - goto exit; - default: - rc = 1; - goto exit; - } - } - - switch (device_type) { - case TYPE_SUBSYSTEMS: - udev_enumerate_scan_subsystems(udev_enumerate); - exec_list(udev_enumerate, action); - goto exit; - case TYPE_DEVICES: - udev_enumerate_scan_devices(udev_enumerate); - exec_list(udev_enumerate, action); - goto exit; - default: - goto exit; - } -exit: - udev_enumerate_unref(udev_enumerate); - return rc; -} - -const struct udevadm_cmd udevadm_trigger = { - .name = "trigger", - .cmd = adm_trigger, - .help = "request events from the kernel", -}; diff --git a/src/udev/src/udevadm.c b/src/udev/src/udevadm.c deleted file mode 100644 index 224ece0bb7..0000000000 --- a/src/udev/src/udevadm.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (C) 2007-2009 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -static bool debug; - -void udev_main_log(struct udev *udev, int priority, - const char *file, int line, const char *fn, - const char *format, va_list args) -{ - if (debug) { - fprintf(stderr, "%s: ", fn); - vfprintf(stderr, format, args); - } else { - va_list args2; - - va_copy(args2, args); - vfprintf(stderr, format, args2); - va_end(args2); - vsyslog(priority, format, args); - } -} - -static int adm_version(struct udev *udev, int argc, char *argv[]) -{ - printf("%s\n", VERSION); - return 0; -} -static const struct udevadm_cmd udevadm_version = { - .name = "version", - .cmd = adm_version, -}; - -static int adm_help(struct udev *udev, int argc, char *argv[]); -static const struct udevadm_cmd udevadm_help = { - .name = "help", - .cmd = adm_help, -}; - -static const struct udevadm_cmd *udevadm_cmds[] = { - &udevadm_info, - &udevadm_trigger, - &udevadm_settle, - &udevadm_control, - &udevadm_monitor, - &udevadm_test, - &udevadm_test_builtin, - &udevadm_version, - &udevadm_help, -}; - -static int adm_help(struct udev *udev, int argc, char *argv[]) -{ - unsigned int i; - - fprintf(stderr, "Usage: udevadm [--help] [--version] [--debug] COMMAND [COMMAND OPTIONS]\n"); - for (i = 0; i < ARRAY_SIZE(udevadm_cmds); i++) - if (udevadm_cmds[i]->help != NULL) - printf(" %-12s %s\n", udevadm_cmds[i]->name, udevadm_cmds[i]->help); - fprintf(stderr, "\n"); - return 0; -} - -static int run_command(struct udev *udev, const struct udevadm_cmd *cmd, int argc, char *argv[]) -{ - if (cmd->debug) { - debug = true; - if (udev_get_log_priority(udev) < LOG_INFO) - udev_set_log_priority(udev, LOG_INFO); - } - info(udev, "calling: %s\n", cmd->name); - return cmd->cmd(udev, argc, argv); -} - -int main(int argc, char *argv[]) -{ - struct udev *udev; - static const struct option options[] = { - { "debug", no_argument, NULL, 'd' }, - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'V' }, - {} - }; - const char *command; - unsigned int i; - int rc = 1; - - udev = udev_new(); - if (udev == NULL) - goto out; - - udev_log_init("udevadm"); - udev_set_log_fn(udev, udev_main_log); - udev_selinux_init(udev); - - for (;;) { - int option; - - option = getopt_long(argc, argv, "+dhV", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'd': - debug = true; - if (udev_get_log_priority(udev) < LOG_INFO) - udev_set_log_priority(udev, LOG_INFO); - break; - case 'h': - rc = adm_help(udev, argc, argv); - goto out; - case 'V': - rc = adm_version(udev, argc, argv); - goto out; - default: - goto out; - } - } - command = argv[optind]; - - info(udev, "runtime dir '%s'\n", udev_get_run_path(udev)); - - if (command != NULL) - for (i = 0; i < ARRAY_SIZE(udevadm_cmds); i++) { - if (strcmp(udevadm_cmds[i]->name, command) == 0) { - argc -= optind; - argv += optind; - optind = 0; - rc = run_command(udev, udevadm_cmds[i], argc, argv); - goto out; - } - } - - fprintf(stderr, "missing or unknown command\n\n"); - adm_help(udev, argc, argv); - rc = 2; -out: - udev_selinux_exit(udev); - udev_unref(udev); - udev_log_close(); - return rc; -} diff --git a/src/udev/src/udevadm.xml b/src/udev/src/udevadm.xml deleted file mode 100644 index 455ce80ca9..0000000000 --- a/src/udev/src/udevadm.xml +++ /dev/null @@ -1,472 +0,0 @@ - - - - - - - udevadm - udev - - - - udevadm - 8 - - - - - udevadmudev management tool - - - - - udevadm - - - - - - udevadm info options - - - udevadm trigger options - - - udevadm settle options - - - udevadm control command - - - udevadm monitor options - - - udevadm test options devpath - - - udevadm test-builtin options command devpath - - - - Description - udevadm expects a command and command specific options. It - controls the runtime behavior of udev, requests kernel events, - manages the event queue, and provides simple debugging mechanisms. - - - OPTIONS - - - - - Print debug messages to stderr. - - - - - - Print version number. - - - - - - Print help text. - - - - - udevadm info <replaceable>options</replaceable> - Queries the udev database for device information - stored in the udev database. It can also query the properties - of a device from its sysfs representation to help creating udev - rules that match this device. - - - - - Query the database for specified type of device data. It needs the - or to identify the specified - device. Valid queries are: - name, symlink, path, - property, all. - - - - - - The devpath of the device to query. - - - - - - The name of the device node or a symlink to query - - - - - - The udev root directory: /dev. If used in conjunction - with a name or symlink query, the - query returns the absolute path including the root directory. - - - - - - The udev runtime directory: /run/udev. - - - - - - Print all sysfs properties of the specified device that can be used - in udev rules to match the specified device. It prints all devices - along the chain, up to the root of sysfs that can be used in udev rules. - - - - - - Print output as key/value pairs. Values are enclosed in single quotes. - - - - - - Add a prefix to the key name of exported values. - - - - - - Print major/minor numbers of the underlying device, where the file - lives on. - - - - - - Export the content of the udev database. - - - - - - Cleanup the udev database. - - - - - - Print version. - - - - - - Print help text. - - - - - - udevadm trigger <optional>options</optional> - Request device events from the kernel. Primarily used to replay events at system coldplug time. - - - - - Print the list of devices which will be triggered. - - - - - - Do not actually trigger the event. - - - - - - Trigger a specific type of devices. Valid types are: - devices, subsystems. - The default value is devices. - - - - - - Type of event to be triggered. The default value is change. - - - - - - Trigger events for devices which belong to a matching subsystem. This option - can be specified multiple times and supports shell style pattern matching. - - - - - - Do not trigger events for devices which belong to a matching subsystem. This option - can be specified multiple times and supports shell style pattern matching. - - - - - - Trigger events for devices with a matching sysfs attribute. If a value is specified - along with the attribute name, the content of the attribute is matched against the given - value using shell style pattern matching. If no value is specified, the existence of the - sysfs attribute is checked. This option can be specified multiple times. - - - - - - Do not trigger events for devices with a matching sysfs attribute. If a value is - specified along with the attribute name, the content of the attribute is matched against - the given value using shell style pattern matching. If no value is specified, the existence - of the sysfs attribute is checked. This option can be specified multiple times. - - - - - - Trigger events for devices with a matching property value. This option can be - specified multiple times and supports shell style pattern matching. - - - - - - Trigger events for devices with a matching tag. This option can be - specified multiple times. - - - - - - Trigger events for devices with a matching sys device name. This option can be - specified multiple times and supports shell style pattern matching. - - - - - - Trigger events for all children of a given device. - - - - - - udevadm settle <optional>options</optional> - Watches the udev event queue, and exits if all current events are handled. - - - - - Maximum number of seconds to wait for the event queue to become empty. - The default value is 120 seconds. A value of 0 will check if the queue is empty - and always return immediately. - - - - - - Wait only for events after the given sequence number. - - - - - - Wait only for events before the given sequence number. - - - - - - Stop waiting if file exists. - - - - - - Do not print any output, like the remaining queue entries when reaching the timeout. - - - - - - Print help text. - - - - - - udevadm control <replaceable>command</replaceable> - Modify the internal state of the running udev daemon. - - - - - Signal and wait for udevd to exit. - - - - - - Set the internal log level of udevd. Valid values are the numerical - syslog priorities or their textual representations: , - and . - - - - - - Signal udevd to stop executing new events. Incoming events - will be queued. - - - - - - Signal udevd to enable the execution of events. - - - - - - Signal udevd to reload the rules files and other databases like the kernel - module index. Reloading rules and databases does not apply any changes to already - existing devices; the new configuration will only be applied to new events. - - - - - - Set a global property for all events. - - - - value - - Set the maximum number of events, udevd will handle at the - same time. - - - - seconds - - The maximum number seconds to wait for a reply from udevd. - - - - - - Print help text. - - - - - - udevadm monitor <optional>options</optional> - Listens to the kernel uevents and events sent out by a udev rule - and prints the devpath of the event to the console. It can be used to analyze the - event timing, by comparing the timestamps of the kernel uevent and the udev event. - - - - - - Print the kernel uevents. - - - - - - Print the udev event after the rule processing. - - - - - - Also print the properties of the event. - - - - - - Filter events by subsystem[/devtype]. Only udev events with a matching subsystem value will pass. - - - - - - Filter events by property. Only udev events with a given tag attached will pass. - - - - - - Print help text. - - - - - - udevadm test <optional>options</optional> <replaceable>devpath</replaceable> - Simulate a udev event run for the given device, and print debug output. - - - - - The action string. - - - - - - The subsystem string. - - - - - - Print help text. - - - - - - udevadm test-builtin <optional>options</optional> <replaceable>command</replaceable> <replaceable>devpath</replaceable> - Run a built-in command for the given device, and print debug output. - - - - - Print help text. - - - - - - - Author - Written by Kay Sievers kay.sievers@vrfy.org. - - - - See Also - - udev7 - - - udevd8 - - - diff --git a/src/udev/src/udevd.c b/src/udev/src/udevd.c deleted file mode 100644 index 170221790a..0000000000 --- a/src/udev/src/udevd.c +++ /dev/null @@ -1,1746 +0,0 @@ -/* - * Copyright (C) 2004-2011 Kay Sievers - * Copyright (C) 2004 Chris Friesen - * Copyright (C) 2009 Canonical Ltd. - * Copyright (C) 2009 Scott James Remnant - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" -#include "sd-daemon.h" - -static bool debug; - -void udev_main_log(struct udev *udev, int priority, - const char *file, int line, const char *fn, - const char *format, va_list args) -{ - if (debug) { - char buf[1024]; - struct timespec ts; - - vsnprintf(buf, sizeof(buf), format, args); - clock_gettime(CLOCK_MONOTONIC, &ts); - fprintf(stderr, "[%llu.%06u] [%u] %s: %s", - (unsigned long long) ts.tv_sec, (unsigned int) ts.tv_nsec/1000, - (int) getpid(), fn, buf); - } else { - vsyslog(priority, format, args); - } -} - -static struct udev_rules *rules; -static struct udev_queue_export *udev_queue_export; -static struct udev_ctrl *udev_ctrl; -static struct udev_monitor *monitor; -static int worker_watch[2] = { -1, -1 }; -static int fd_signal = -1; -static int fd_ep = -1; -static int fd_inotify = -1; -static bool stop_exec_queue; -static bool reload; -static int children; -static int children_max; -static int exec_delay; -static sigset_t sigmask_orig; -static UDEV_LIST(event_list); -static UDEV_LIST(worker_list); -static bool udev_exit; - -enum event_state { - EVENT_UNDEF, - EVENT_QUEUED, - EVENT_RUNNING, -}; - -struct event { - struct udev_list_node node; - struct udev *udev; - struct udev_device *dev; - enum event_state state; - int exitcode; - unsigned long long int delaying_seqnum; - unsigned long long int seqnum; - const char *devpath; - size_t devpath_len; - const char *devpath_old; - dev_t devnum; - bool is_block; - int ifindex; -}; - -static struct event *node_to_event(struct udev_list_node *node) -{ - char *event; - - event = (char *)node; - event -= offsetof(struct event, node); - return (struct event *)event; -} - -static void event_queue_cleanup(struct udev *udev, enum event_state type); - -enum worker_state { - WORKER_UNDEF, - WORKER_RUNNING, - WORKER_IDLE, - WORKER_KILLED, -}; - -struct worker { - struct udev_list_node node; - struct udev *udev; - int refcount; - pid_t pid; - struct udev_monitor *monitor; - enum worker_state state; - struct event *event; - unsigned long long event_start_usec; -}; - -/* passed from worker to main process */ -struct worker_message { - pid_t pid; - int exitcode; -}; - -static struct worker *node_to_worker(struct udev_list_node *node) -{ - char *worker; - - worker = (char *)node; - worker -= offsetof(struct worker, node); - return (struct worker *)worker; -} - -static void event_queue_delete(struct event *event, bool export) -{ - udev_list_node_remove(&event->node); - - if (export) { - udev_queue_export_device_finished(udev_queue_export, event->dev); - info(event->udev, "seq %llu done with %i\n", udev_device_get_seqnum(event->dev), event->exitcode); - } - udev_device_unref(event->dev); - free(event); -} - -static struct worker *worker_ref(struct worker *worker) -{ - worker->refcount++; - return worker; -} - -static void worker_cleanup(struct worker *worker) -{ - udev_list_node_remove(&worker->node); - udev_monitor_unref(worker->monitor); - children--; - free(worker); -} - -static void worker_unref(struct worker *worker) -{ - worker->refcount--; - if (worker->refcount > 0) - return; - info(worker->udev, "worker [%u] cleaned up\n", worker->pid); - worker_cleanup(worker); -} - -static void worker_list_cleanup(struct udev *udev) -{ - struct udev_list_node *loop, *tmp; - - udev_list_node_foreach_safe(loop, tmp, &worker_list) { - struct worker *worker = node_to_worker(loop); - - worker_cleanup(worker); - } -} - -static void worker_new(struct event *event) -{ - struct udev *udev = event->udev; - struct worker *worker; - struct udev_monitor *worker_monitor; - pid_t pid; - - /* listen for new events */ - worker_monitor = udev_monitor_new_from_netlink(udev, NULL); - if (worker_monitor == NULL) - return; - /* allow the main daemon netlink address to send devices to the worker */ - udev_monitor_allow_unicast_sender(worker_monitor, monitor); - udev_monitor_enable_receiving(worker_monitor); - - worker = calloc(1, sizeof(struct worker)); - if (worker == NULL) { - udev_monitor_unref(worker_monitor); - return; - } - /* worker + event reference */ - worker->refcount = 2; - worker->udev = udev; - - pid = fork(); - switch (pid) { - case 0: { - struct udev_device *dev = NULL; - int fd_monitor; - struct epoll_event ep_signal, ep_monitor; - sigset_t mask; - int rc = EXIT_SUCCESS; - - /* take initial device from queue */ - dev = event->dev; - event->dev = NULL; - - free(worker); - worker_list_cleanup(udev); - event_queue_cleanup(udev, EVENT_UNDEF); - udev_queue_export_unref(udev_queue_export); - udev_monitor_unref(monitor); - udev_ctrl_unref(udev_ctrl); - close(fd_signal); - close(fd_ep); - close(worker_watch[READ_END]); - - sigfillset(&mask); - fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); - if (fd_signal < 0) { - err(udev, "error creating signalfd %m\n"); - rc = 2; - goto out; - } - - fd_ep = epoll_create1(EPOLL_CLOEXEC); - if (fd_ep < 0) { - err(udev, "error creating epoll fd: %m\n"); - rc = 3; - goto out; - } - - memset(&ep_signal, 0, sizeof(struct epoll_event)); - ep_signal.events = EPOLLIN; - ep_signal.data.fd = fd_signal; - - fd_monitor = udev_monitor_get_fd(worker_monitor); - memset(&ep_monitor, 0, sizeof(struct epoll_event)); - ep_monitor.events = EPOLLIN; - ep_monitor.data.fd = fd_monitor; - - if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 || - epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_monitor, &ep_monitor) < 0) { - err(udev, "fail to add fds to epoll: %m\n"); - rc = 4; - goto out; - } - - /* request TERM signal if parent exits */ - prctl(PR_SET_PDEATHSIG, SIGTERM); - - for (;;) { - struct udev_event *udev_event; - struct worker_message msg; - int err; - - info(udev, "seq %llu running\n", udev_device_get_seqnum(dev)); - udev_event = udev_event_new(dev); - if (udev_event == NULL) { - rc = 5; - goto out; - } - - /* needed for SIGCHLD/SIGTERM in spawn() */ - udev_event->fd_signal = fd_signal; - - if (exec_delay > 0) - udev_event->exec_delay = exec_delay; - - /* apply rules, create node, symlinks */ - err = udev_event_execute_rules(udev_event, rules, &sigmask_orig); - - if (err == 0) - udev_event_execute_run(udev_event, &sigmask_orig); - - /* apply/restore inotify watch */ - if (err == 0 && udev_event->inotify_watch) { - udev_watch_begin(udev, dev); - udev_device_update_db(dev); - } - - /* send processed event back to libudev listeners */ - udev_monitor_send_device(worker_monitor, NULL, dev); - - /* send udevd the result of the event execution */ - memset(&msg, 0, sizeof(struct worker_message)); - if (err != 0) - msg.exitcode = err; - msg.pid = getpid(); - send(worker_watch[WRITE_END], &msg, sizeof(struct worker_message), 0); - - info(udev, "seq %llu processed with %i\n", udev_device_get_seqnum(dev), err); - - udev_device_unref(dev); - dev = NULL; - - if (udev_event->sigterm) { - udev_event_unref(udev_event); - goto out; - } - - udev_event_unref(udev_event); - - /* wait for more device messages from main udevd, or term signal */ - while (dev == NULL) { - struct epoll_event ev[4]; - int fdcount; - int i; - - fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), -1); - if (fdcount < 0) { - if (errno == EINTR) - continue; - err = -errno; - err(udev, "failed to poll: %m\n"); - goto out; - } - - for (i = 0; i < fdcount; i++) { - if (ev[i].data.fd == fd_monitor && ev[i].events & EPOLLIN) { - dev = udev_monitor_receive_device(worker_monitor); - break; - } else if (ev[i].data.fd == fd_signal && ev[i].events & EPOLLIN) { - struct signalfd_siginfo fdsi; - ssize_t size; - - size = read(fd_signal, &fdsi, sizeof(struct signalfd_siginfo)); - if (size != sizeof(struct signalfd_siginfo)) - continue; - switch (fdsi.ssi_signo) { - case SIGTERM: - goto out; - } - } - } - } - } -out: - udev_device_unref(dev); - if (fd_signal >= 0) - close(fd_signal); - if (fd_ep >= 0) - close(fd_ep); - close(fd_inotify); - close(worker_watch[WRITE_END]); - udev_rules_unref(rules); - udev_builtin_exit(udev); - udev_monitor_unref(worker_monitor); - udev_unref(udev); - udev_log_close(); - exit(rc); - } - case -1: - udev_monitor_unref(worker_monitor); - event->state = EVENT_QUEUED; - free(worker); - err(udev, "fork of child failed: %m\n"); - break; - default: - /* close monitor, but keep address around */ - udev_monitor_disconnect(worker_monitor); - worker->monitor = worker_monitor; - worker->pid = pid; - worker->state = WORKER_RUNNING; - worker->event_start_usec = now_usec(); - worker->event = event; - event->state = EVENT_RUNNING; - udev_list_node_append(&worker->node, &worker_list); - children++; - info(udev, "seq %llu forked new worker [%u]\n", udev_device_get_seqnum(event->dev), pid); - break; - } -} - -static void event_run(struct event *event) -{ - struct udev_list_node *loop; - - udev_list_node_foreach(loop, &worker_list) { - struct worker *worker = node_to_worker(loop); - ssize_t count; - - if (worker->state != WORKER_IDLE) - continue; - - count = udev_monitor_send_device(monitor, worker->monitor, event->dev); - if (count < 0) { - err(event->udev, "worker [%u] did not accept message %zi (%m), kill it\n", worker->pid, count); - kill(worker->pid, SIGKILL); - worker->state = WORKER_KILLED; - continue; - } - worker_ref(worker); - worker->event = event; - worker->state = WORKER_RUNNING; - worker->event_start_usec = now_usec(); - event->state = EVENT_RUNNING; - return; - } - - if (children >= children_max) { - if (children_max > 1) - info(event->udev, "maximum number (%i) of children reached\n", children); - return; - } - - /* start new worker and pass initial device */ - worker_new(event); -} - -static int event_queue_insert(struct udev_device *dev) -{ - struct event *event; - - event = calloc(1, sizeof(struct event)); - if (event == NULL) - return -1; - - event->udev = udev_device_get_udev(dev); - event->dev = dev; - event->seqnum = udev_device_get_seqnum(dev); - event->devpath = udev_device_get_devpath(dev); - event->devpath_len = strlen(event->devpath); - event->devpath_old = udev_device_get_devpath_old(dev); - event->devnum = udev_device_get_devnum(dev); - event->is_block = (strcmp("block", udev_device_get_subsystem(dev)) == 0); - event->ifindex = udev_device_get_ifindex(dev); - - udev_queue_export_device_queued(udev_queue_export, dev); - info(event->udev, "seq %llu queued, '%s' '%s'\n", udev_device_get_seqnum(dev), - udev_device_get_action(dev), udev_device_get_subsystem(dev)); - - event->state = EVENT_QUEUED; - udev_list_node_append(&event->node, &event_list); - return 0; -} - -static void worker_kill(struct udev *udev, int retain) -{ - struct udev_list_node *loop; - int max; - - if (children <= retain) - return; - - max = children - retain; - - udev_list_node_foreach(loop, &worker_list) { - struct worker *worker = node_to_worker(loop); - - if (max-- <= 0) - break; - - if (worker->state == WORKER_KILLED) - continue; - - worker->state = WORKER_KILLED; - kill(worker->pid, SIGTERM); - } -} - -/* lookup event for identical, parent, child device */ -static bool is_devpath_busy(struct event *event) -{ - struct udev_list_node *loop; - size_t common; - - /* check if queue contains events we depend on */ - udev_list_node_foreach(loop, &event_list) { - struct event *loop_event = node_to_event(loop); - - /* we already found a later event, earlier can not block us, no need to check again */ - if (loop_event->seqnum < event->delaying_seqnum) - continue; - - /* event we checked earlier still exists, no need to check again */ - if (loop_event->seqnum == event->delaying_seqnum) - return true; - - /* found ourself, no later event can block us */ - if (loop_event->seqnum >= event->seqnum) - break; - - /* check major/minor */ - if (major(event->devnum) != 0 && event->devnum == loop_event->devnum && event->is_block == loop_event->is_block) - return true; - - /* check network device ifindex */ - if (event->ifindex != 0 && event->ifindex == loop_event->ifindex) - return true; - - /* check our old name */ - if (event->devpath_old != NULL && strcmp(loop_event->devpath, event->devpath_old) == 0) { - event->delaying_seqnum = loop_event->seqnum; - return true; - } - - /* compare devpath */ - common = MIN(loop_event->devpath_len, event->devpath_len); - - /* one devpath is contained in the other? */ - if (memcmp(loop_event->devpath, event->devpath, common) != 0) - continue; - - /* identical device event found */ - if (loop_event->devpath_len == event->devpath_len) { - /* devices names might have changed/swapped in the meantime */ - if (major(event->devnum) != 0 && (event->devnum != loop_event->devnum || event->is_block != loop_event->is_block)) - continue; - if (event->ifindex != 0 && event->ifindex != loop_event->ifindex) - continue; - event->delaying_seqnum = loop_event->seqnum; - return true; - } - - /* parent device event found */ - if (event->devpath[common] == '/') { - event->delaying_seqnum = loop_event->seqnum; - return true; - } - - /* child device event found */ - if (loop_event->devpath[common] == '/') { - event->delaying_seqnum = loop_event->seqnum; - return true; - } - - /* no matching device */ - continue; - } - - return false; -} - -static void event_queue_start(struct udev *udev) -{ - struct udev_list_node *loop; - - udev_list_node_foreach(loop, &event_list) { - struct event *event = node_to_event(loop); - - if (event->state != EVENT_QUEUED) - continue; - - /* do not start event if parent or child event is still running */ - if (is_devpath_busy(event)) { - dbg(udev, "delay seq %llu (%s)\n", event->seqnum, event->devpath); - continue; - } - - event_run(event); - } -} - -static void event_queue_cleanup(struct udev *udev, enum event_state match_type) -{ - struct udev_list_node *loop, *tmp; - - udev_list_node_foreach_safe(loop, tmp, &event_list) { - struct event *event = node_to_event(loop); - - if (match_type != EVENT_UNDEF && match_type != event->state) - continue; - - event_queue_delete(event, false); - } -} - -static void worker_returned(int fd_worker) -{ - for (;;) { - struct worker_message msg; - ssize_t size; - struct udev_list_node *loop; - - size = recv(fd_worker, &msg, sizeof(struct worker_message), MSG_DONTWAIT); - if (size != sizeof(struct worker_message)) - break; - - /* lookup worker who sent the signal */ - udev_list_node_foreach(loop, &worker_list) { - struct worker *worker = node_to_worker(loop); - - if (worker->pid != msg.pid) - continue; - - /* worker returned */ - if (worker->event) { - worker->event->exitcode = msg.exitcode; - event_queue_delete(worker->event, true); - worker->event = NULL; - } - if (worker->state != WORKER_KILLED) - worker->state = WORKER_IDLE; - worker_unref(worker); - break; - } - } -} - -/* receive the udevd message from userspace */ -static struct udev_ctrl_connection *handle_ctrl_msg(struct udev_ctrl *uctrl) -{ - struct udev *udev = udev_ctrl_get_udev(uctrl); - struct udev_ctrl_connection *ctrl_conn; - struct udev_ctrl_msg *ctrl_msg = NULL; - const char *str; - int i; - - ctrl_conn = udev_ctrl_get_connection(uctrl); - if (ctrl_conn == NULL) - goto out; - - ctrl_msg = udev_ctrl_receive_msg(ctrl_conn); - if (ctrl_msg == NULL) - goto out; - - i = udev_ctrl_get_set_log_level(ctrl_msg); - if (i >= 0) { - info(udev, "udevd message (SET_LOG_PRIORITY) received, log_priority=%i\n", i); - udev_set_log_priority(udev, i); - worker_kill(udev, 0); - } - - if (udev_ctrl_get_stop_exec_queue(ctrl_msg) > 0) { - info(udev, "udevd message (STOP_EXEC_QUEUE) received\n"); - stop_exec_queue = true; - } - - if (udev_ctrl_get_start_exec_queue(ctrl_msg) > 0) { - info(udev, "udevd message (START_EXEC_QUEUE) received\n"); - stop_exec_queue = false; - } - - if (udev_ctrl_get_reload(ctrl_msg) > 0) { - info(udev, "udevd message (RELOAD) received\n"); - reload = true; - } - - str = udev_ctrl_get_set_env(ctrl_msg); - if (str != NULL) { - char *key; - - key = strdup(str); - if (key != NULL) { - char *val; - - val = strchr(key, '='); - if (val != NULL) { - val[0] = '\0'; - val = &val[1]; - if (val[0] == '\0') { - info(udev, "udevd message (ENV) received, unset '%s'\n", key); - udev_add_property(udev, key, NULL); - } else { - info(udev, "udevd message (ENV) received, set '%s=%s'\n", key, val); - udev_add_property(udev, key, val); - } - } else { - err(udev, "wrong key format '%s'\n", key); - } - free(key); - } - worker_kill(udev, 0); - } - - i = udev_ctrl_get_set_children_max(ctrl_msg); - if (i >= 0) { - info(udev, "udevd message (SET_MAX_CHILDREN) received, children_max=%i\n", i); - children_max = i; - } - - if (udev_ctrl_get_ping(ctrl_msg) > 0) - info(udev, "udevd message (SYNC) received\n"); - - if (udev_ctrl_get_exit(ctrl_msg) > 0) { - info(udev, "udevd message (EXIT) received\n"); - udev_exit = true; - /* keep reference to block the client until we exit */ - udev_ctrl_connection_ref(ctrl_conn); - } -out: - udev_ctrl_msg_unref(ctrl_msg); - return udev_ctrl_connection_unref(ctrl_conn); -} - -/* read inotify messages */ -static int handle_inotify(struct udev *udev) -{ - int nbytes, pos; - char *buf; - struct inotify_event *ev; - - if ((ioctl(fd_inotify, FIONREAD, &nbytes) < 0) || (nbytes <= 0)) - return 0; - - buf = malloc(nbytes); - if (buf == NULL) { - err(udev, "error getting buffer for inotify\n"); - return -1; - } - - nbytes = read(fd_inotify, buf, nbytes); - - for (pos = 0; pos < nbytes; pos += sizeof(struct inotify_event) + ev->len) { - struct udev_device *dev; - - ev = (struct inotify_event *)(buf + pos); - dev = udev_watch_lookup(udev, ev->wd); - if (dev != NULL) { - info(udev, "inotify event: %x for %s\n", ev->mask, udev_device_get_devnode(dev)); - if (ev->mask & IN_CLOSE_WRITE) { - char filename[UTIL_PATH_SIZE]; - int fd; - - info(udev, "device %s closed, synthesising 'change'\n", udev_device_get_devnode(dev)); - util_strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL); - fd = open(filename, O_WRONLY); - if (fd >= 0) { - if (write(fd, "change", 6) < 0) - info(udev, "error writing uevent: %m\n"); - close(fd); - } - } - if (ev->mask & IN_IGNORED) - udev_watch_end(udev, dev); - - udev_device_unref(dev); - } - - } - - free(buf); - return 0; -} - -static void handle_signal(struct udev *udev, int signo) -{ - switch (signo) { - case SIGINT: - case SIGTERM: - udev_exit = true; - break; - case SIGCHLD: - for (;;) { - pid_t pid; - int status; - struct udev_list_node *loop, *tmp; - - pid = waitpid(-1, &status, WNOHANG); - if (pid <= 0) - break; - - udev_list_node_foreach_safe(loop, tmp, &worker_list) { - struct worker *worker = node_to_worker(loop); - - if (worker->pid != pid) - continue; - info(udev, "worker [%u] exit\n", pid); - - if (WIFEXITED(status)) { - if (WEXITSTATUS(status) != 0) - err(udev, "worker [%u] exit with return code %i\n", pid, WEXITSTATUS(status)); - } else if (WIFSIGNALED(status)) { - err(udev, "worker [%u] terminated by signal %i (%s)\n", - pid, WTERMSIG(status), strsignal(WTERMSIG(status))); - } else if (WIFSTOPPED(status)) { - err(udev, "worker [%u] stopped\n", pid); - } else if (WIFCONTINUED(status)) { - err(udev, "worker [%u] continued\n", pid); - } else { - err(udev, "worker [%u] exit with status 0x%04x\n", pid, status); - } - - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - if (worker->event) { - err(udev, "worker [%u] failed while handling '%s'\n", - pid, worker->event->devpath); - worker->event->exitcode = -32; - event_queue_delete(worker->event, true); - /* drop reference taken for state 'running' */ - worker_unref(worker); - } - } - worker_unref(worker); - break; - } - } - break; - case SIGHUP: - reload = true; - break; - } -} - -static void static_dev_create_from_modules(struct udev *udev) -{ - struct utsname kernel; - char modules[UTIL_PATH_SIZE]; - char buf[4096]; - FILE *f; - - uname(&kernel); - util_strscpyl(modules, sizeof(modules), "/lib/modules/", kernel.release, "/modules.devname", NULL); - f = fopen(modules, "r"); - if (f == NULL) - return; - - while (fgets(buf, sizeof(buf), f) != NULL) { - char *s; - const char *modname; - const char *devname; - const char *devno; - int maj, min; - char type; - mode_t mode; - char filename[UTIL_PATH_SIZE]; - - if (buf[0] == '#') - continue; - - modname = buf; - s = strchr(modname, ' '); - if (s == NULL) - continue; - s[0] = '\0'; - - devname = &s[1]; - s = strchr(devname, ' '); - if (s == NULL) - continue; - s[0] = '\0'; - - devno = &s[1]; - s = strchr(devno, ' '); - if (s == NULL) - s = strchr(devno, '\n'); - if (s != NULL) - s[0] = '\0'; - if (sscanf(devno, "%c%u:%u", &type, &maj, &min) != 3) - continue; - - if (type == 'c') - mode = S_IFCHR; - else if (type == 'b') - mode = S_IFBLK; - else - continue; - - util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/", devname, NULL); - util_create_path_selinux(udev, filename); - udev_selinux_setfscreatecon(udev, filename, mode); - info(udev, "mknod '%s' %c%u:%u\n", filename, type, maj, min); - if (mknod(filename, mode, makedev(maj, min)) < 0 && errno == EEXIST) - utimensat(AT_FDCWD, filename, NULL, 0); - udev_selinux_resetfscreatecon(udev); - } - - fclose(f); -} - -static int copy_dev_dir(struct udev *udev, DIR *dir_from, DIR *dir_to, int maxdepth) -{ - struct dirent *dent; - - for (dent = readdir(dir_from); dent != NULL; dent = readdir(dir_from)) { - struct stat stats; - - if (dent->d_name[0] == '.') - continue; - if (fstatat(dirfd(dir_from), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0) - continue; - - if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) { - udev_selinux_setfscreateconat(udev, dirfd(dir_to), dent->d_name, stats.st_mode & 0777); - if (mknodat(dirfd(dir_to), dent->d_name, stats.st_mode, stats.st_rdev) == 0) { - fchmodat(dirfd(dir_to), dent->d_name, stats.st_mode & 0777, 0); - fchownat(dirfd(dir_to), dent->d_name, stats.st_uid, stats.st_gid, 0); - } else { - utimensat(dirfd(dir_to), dent->d_name, NULL, 0); - } - udev_selinux_resetfscreatecon(udev); - } else if (S_ISLNK(stats.st_mode)) { - char target[UTIL_PATH_SIZE]; - ssize_t len; - - len = readlinkat(dirfd(dir_from), dent->d_name, target, sizeof(target)); - if (len <= 0 || len == (ssize_t)sizeof(target)) - continue; - target[len] = '\0'; - udev_selinux_setfscreateconat(udev, dirfd(dir_to), dent->d_name, S_IFLNK); - if (symlinkat(target, dirfd(dir_to), dent->d_name) < 0 && errno == EEXIST) - utimensat(dirfd(dir_to), dent->d_name, NULL, AT_SYMLINK_NOFOLLOW); - udev_selinux_resetfscreatecon(udev); - } else if (S_ISDIR(stats.st_mode)) { - DIR *dir2_from, *dir2_to; - - if (maxdepth == 0) - continue; - - udev_selinux_setfscreateconat(udev, dirfd(dir_to), dent->d_name, S_IFDIR|0755); - mkdirat(dirfd(dir_to), dent->d_name, 0755); - udev_selinux_resetfscreatecon(udev); - - dir2_to = fdopendir(openat(dirfd(dir_to), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)); - if (dir2_to == NULL) - continue; - - dir2_from = fdopendir(openat(dirfd(dir_from), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)); - if (dir2_from == NULL) { - closedir(dir2_to); - continue; - } - - copy_dev_dir(udev, dir2_from, dir2_to, maxdepth-1); - - closedir(dir2_to); - closedir(dir2_from); - } - } - - return 0; -} - -static void static_dev_create_links(struct udev *udev, DIR *dir) -{ - struct stdlinks { - const char *link; - const char *target; - }; - static const struct stdlinks stdlinks[] = { - { "core", "/proc/kcore" }, - { "fd", "/proc/self/fd" }, - { "stdin", "/proc/self/fd/0" }, - { "stdout", "/proc/self/fd/1" }, - { "stderr", "/proc/self/fd/2" }, - }; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(stdlinks); i++) { - struct stat sb; - - if (stat(stdlinks[i].target, &sb) == 0) { - udev_selinux_setfscreateconat(udev, dirfd(dir), stdlinks[i].link, S_IFLNK); - if (symlinkat(stdlinks[i].target, dirfd(dir), stdlinks[i].link) < 0 && errno == EEXIST) - utimensat(dirfd(dir), stdlinks[i].link, NULL, AT_SYMLINK_NOFOLLOW); - udev_selinux_resetfscreatecon(udev); - } - } -} - -static void static_dev_create_from_devices(struct udev *udev, DIR *dir) -{ - DIR *dir_from; - - dir_from = opendir(PKGLIBEXECDIR "/devices"); - if (dir_from == NULL) - return; - copy_dev_dir(udev, dir_from, dir, 8); - closedir(dir_from); -} - -static void static_dev_create(struct udev *udev) -{ - DIR *dir; - - dir = opendir(udev_get_dev_path(udev)); - if (dir == NULL) - return; - - static_dev_create_links(udev, dir); - static_dev_create_from_devices(udev, dir); - - closedir(dir); -} - -static int mem_size_mb(void) -{ - FILE *f; - char buf[4096]; - long int memsize = -1; - - f = fopen("/proc/meminfo", "r"); - if (f == NULL) - return -1; - - while (fgets(buf, sizeof(buf), f) != NULL) { - long int value; - - if (sscanf(buf, "MemTotal: %ld kB", &value) == 1) { - memsize = value / 1024; - break; - } - } - - fclose(f); - return memsize; -} - -static int convert_db(struct udev *udev) -{ - char filename[UTIL_PATH_SIZE]; - FILE *f; - struct udev_enumerate *udev_enumerate; - struct udev_list_entry *list_entry; - - /* current database */ - util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/data", NULL); - if (access(filename, F_OK) >= 0) - return 0; - - /* make sure we do not get here again */ - util_create_path(udev, filename); - mkdir(filename, 0755); - - /* old database */ - util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/db", NULL); - if (access(filename, F_OK) < 0) - return 0; - - f = fopen("/dev/kmsg", "w"); - if (f != NULL) { - fprintf(f, "<30>udevd[%u]: converting old udev database\n", getpid()); - fclose(f); - } - - udev_enumerate = udev_enumerate_new(udev); - if (udev_enumerate == NULL) - return -1; - udev_enumerate_scan_devices(udev_enumerate); - udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) { - struct udev_device *device; - - device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry)); - if (device == NULL) - continue; - - /* try to find the old database for devices without a current one */ - if (udev_device_read_db(device, NULL) < 0) { - bool have_db; - const char *id; - struct stat stats; - char devpath[UTIL_PATH_SIZE]; - char from[UTIL_PATH_SIZE]; - - have_db = false; - - /* find database in old location */ - id = udev_device_get_id_filename(device); - util_strscpyl(from, sizeof(from), udev_get_dev_path(udev), "/.udev/db/", id, NULL); - if (lstat(from, &stats) == 0) { - if (!have_db) { - udev_device_read_db(device, from); - have_db = true; - } - unlink(from); - } - - /* find old database with $subsys:$sysname name */ - util_strscpyl(from, sizeof(from), udev_get_dev_path(udev), - "/.udev/db/", udev_device_get_subsystem(device), ":", - udev_device_get_sysname(device), NULL); - if (lstat(from, &stats) == 0) { - if (!have_db) { - udev_device_read_db(device, from); - have_db = true; - } - unlink(from); - } - - /* find old database with the encoded devpath name */ - util_path_encode(udev_device_get_devpath(device), devpath, sizeof(devpath)); - util_strscpyl(from, sizeof(from), udev_get_dev_path(udev), "/.udev/db/", devpath, NULL); - if (lstat(from, &stats) == 0) { - if (!have_db) { - udev_device_read_db(device, from); - have_db = true; - } - unlink(from); - } - - /* write out new database */ - if (have_db) - udev_device_update_db(device); - } - udev_device_unref(device); - } - udev_enumerate_unref(udev_enumerate); - return 0; -} - -static int systemd_fds(struct udev *udev, int *rctrl, int *rnetlink) -{ - int ctrl = -1, netlink = -1; - int fd, n; - - n = sd_listen_fds(true); - if (n <= 0) - return -1; - - for (fd = SD_LISTEN_FDS_START; fd < n + SD_LISTEN_FDS_START; fd++) { - if (sd_is_socket(fd, AF_LOCAL, SOCK_SEQPACKET, -1)) { - if (ctrl >= 0) - return -1; - ctrl = fd; - continue; - } - - if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1)) { - if (netlink >= 0) - return -1; - netlink = fd; - continue; - } - - return -1; - } - - if (ctrl < 0 || netlink < 0) - return -1; - - info(udev, "ctrl=%i netlink=%i\n", ctrl, netlink); - *rctrl = ctrl; - *rnetlink = netlink; - return 0; -} - -static bool check_rules_timestamp(struct udev *udev) -{ - char **p; - unsigned long long *stamp_usec; - int i, n; - bool changed = false; - - n = udev_get_rules_path(udev, &p, &stamp_usec); - for (i = 0; i < n; i++) { - struct stat stats; - - if (stat(p[i], &stats) < 0) - continue; - - if (stamp_usec[i] == ts_usec(&stats.st_mtim)) - continue; - - /* first check */ - if (stamp_usec[i] != 0) { - info(udev, "reload - timestamp of '%s' changed\n", p[i]); - changed = true; - } - - /* update timestamp */ - stamp_usec[i] = ts_usec(&stats.st_mtim); - } - - return changed; -} - -int main(int argc, char *argv[]) -{ - struct udev *udev; - FILE *f; - sigset_t mask; - int daemonize = false; - int resolve_names = 1; - static const struct option options[] = { - { "daemon", no_argument, NULL, 'd' }, - { "debug", no_argument, NULL, 'D' }, - { "children-max", required_argument, NULL, 'c' }, - { "exec-delay", required_argument, NULL, 'e' }, - { "resolve-names", required_argument, NULL, 'N' }, - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'V' }, - {} - }; - int fd_ctrl = -1; - int fd_netlink = -1; - int fd_worker = -1; - struct epoll_event ep_ctrl, ep_inotify, ep_signal, ep_netlink, ep_worker; - struct udev_ctrl_connection *ctrl_conn = NULL; - char **s; - int rc = 1; - - udev = udev_new(); - if (udev == NULL) - goto exit; - - udev_log_init("udevd"); - udev_set_log_fn(udev, udev_main_log); - info(udev, "version %s\n", VERSION); - udev_selinux_init(udev); - - for (;;) { - int option; - - option = getopt_long(argc, argv, "c:deDtN:hV", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'd': - daemonize = true; - break; - case 'c': - children_max = strtoul(optarg, NULL, 0); - break; - case 'e': - exec_delay = strtoul(optarg, NULL, 0); - break; - case 'D': - debug = true; - if (udev_get_log_priority(udev) < LOG_INFO) - udev_set_log_priority(udev, LOG_INFO); - break; - case 'N': - if (strcmp (optarg, "early") == 0) { - resolve_names = 1; - } else if (strcmp (optarg, "late") == 0) { - resolve_names = 0; - } else if (strcmp (optarg, "never") == 0) { - resolve_names = -1; - } else { - fprintf(stderr, "resolve-names must be early, late or never\n"); - err(udev, "resolve-names must be early, late or never\n"); - goto exit; - } - break; - case 'h': - printf("Usage: udevd OPTIONS\n" - " --daemon\n" - " --debug\n" - " --children-max=\n" - " --exec-delay=\n" - " --resolve-names=early|late|never\n" - " --version\n" - " --help\n" - "\n"); - goto exit; - case 'V': - printf("%s\n", VERSION); - goto exit; - default: - goto exit; - } - } - - /* - * read the kernel commandline, in case we need to get into debug mode - * udev.log-priority= syslog priority - * udev.children-max= events are fully serialized if set to 1 - * - */ - f = fopen("/proc/cmdline", "r"); - if (f != NULL) { - char cmdline[4096]; - - if (fgets(cmdline, sizeof(cmdline), f) != NULL) { - char *pos; - - pos = strstr(cmdline, "udev.log-priority="); - if (pos != NULL) { - pos += strlen("udev.log-priority="); - udev_set_log_priority(udev, util_log_priority(pos)); - } - - pos = strstr(cmdline, "udev.children-max="); - if (pos != NULL) { - pos += strlen("udev.children-max="); - children_max = strtoul(pos, NULL, 0); - } - - pos = strstr(cmdline, "udev.exec-delay="); - if (pos != NULL) { - pos += strlen("udev.exec-delay="); - exec_delay = strtoul(pos, NULL, 0); - } - } - fclose(f); - } - - if (getuid() != 0) { - fprintf(stderr, "root privileges required\n"); - err(udev, "root privileges required\n"); - goto exit; - } - - /* set umask before creating any file/directory */ - chdir("/"); - umask(022); - - /* /run/udev */ - mkdir(udev_get_run_path(udev), 0755); - - /* create standard links, copy static nodes, create nodes from modules */ - static_dev_create(udev); - static_dev_create_from_modules(udev); - - /* before opening new files, make sure std{in,out,err} fds are in a sane state */ - if (daemonize) { - int fd; - - fd = open("/dev/null", O_RDWR); - if (fd >= 0) { - if (write(STDOUT_FILENO, 0, 0) < 0) - dup2(fd, STDOUT_FILENO); - if (write(STDERR_FILENO, 0, 0) < 0) - dup2(fd, STDERR_FILENO); - if (fd > STDERR_FILENO) - close(fd); - } else { - fprintf(stderr, "cannot open /dev/null\n"); - err(udev, "cannot open /dev/null\n"); - } - } - - if (systemd_fds(udev, &fd_ctrl, &fd_netlink) >= 0) { - /* get control and netlink socket from from systemd */ - udev_ctrl = udev_ctrl_new_from_fd(udev, fd_ctrl); - if (udev_ctrl == NULL) { - err(udev, "error taking over udev control socket"); - rc = 1; - goto exit; - } - - monitor = udev_monitor_new_from_netlink_fd(udev, "kernel", fd_netlink); - if (monitor == NULL) { - err(udev, "error taking over netlink socket\n"); - rc = 3; - goto exit; - } - } else { - /* open control and netlink socket */ - udev_ctrl = udev_ctrl_new(udev); - if (udev_ctrl == NULL) { - fprintf(stderr, "error initializing udev control socket"); - err(udev, "error initializing udev control socket"); - rc = 1; - goto exit; - } - fd_ctrl = udev_ctrl_get_fd(udev_ctrl); - - monitor = udev_monitor_new_from_netlink(udev, "kernel"); - if (monitor == NULL) { - fprintf(stderr, "error initializing netlink socket\n"); - err(udev, "error initializing netlink socket\n"); - rc = 3; - goto exit; - } - fd_netlink = udev_monitor_get_fd(monitor); - } - - if (udev_monitor_enable_receiving(monitor) < 0) { - fprintf(stderr, "error binding netlink socket\n"); - err(udev, "error binding netlink socket\n"); - rc = 3; - goto exit; - } - - if (udev_ctrl_enable_receiving(udev_ctrl) < 0) { - fprintf(stderr, "error binding udev control socket\n"); - err(udev, "error binding udev control socket\n"); - rc = 1; - goto exit; - } - - udev_monitor_set_receive_buffer_size(monitor, 128*1024*1024); - - /* create queue file before signalling 'ready', to make sure we block 'settle' */ - udev_queue_export = udev_queue_export_new(udev); - if (udev_queue_export == NULL) { - err(udev, "error creating queue file\n"); - goto exit; - } - - if (daemonize) { - pid_t pid; - int fd; - - pid = fork(); - switch (pid) { - case 0: - break; - case -1: - err(udev, "fork of daemon failed: %m\n"); - rc = 4; - goto exit; - default: - rc = EXIT_SUCCESS; - goto exit_daemonize; - } - - setsid(); - - fd = open("/proc/self/oom_score_adj", O_RDWR); - if (fd < 0) { - /* Fallback to old interface */ - fd = open("/proc/self/oom_adj", O_RDWR); - if (fd < 0) { - err(udev, "error disabling OOM: %m\n"); - } else { - /* OOM_DISABLE == -17 */ - write(fd, "-17", 3); - close(fd); - } - } else { - write(fd, "-1000", 5); - close(fd); - } - } else { - sd_notify(1, "READY=1"); - } - - f = fopen("/dev/kmsg", "w"); - if (f != NULL) { - fprintf(f, "<30>udevd[%u]: starting version " VERSION "\n", getpid()); - fclose(f); - } - - if (!debug) { - int fd; - - fd = open("/dev/null", O_RDWR); - if (fd >= 0) { - dup2(fd, STDIN_FILENO); - dup2(fd, STDOUT_FILENO); - dup2(fd, STDERR_FILENO); - close(fd); - } - } - - fd_inotify = udev_watch_init(udev); - if (fd_inotify < 0) { - fprintf(stderr, "error initializing inotify\n"); - err(udev, "error initializing inotify\n"); - rc = 4; - goto exit; - } - udev_watch_restore(udev); - - /* block and listen to all signals on signalfd */ - sigfillset(&mask); - sigprocmask(SIG_SETMASK, &mask, &sigmask_orig); - fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); - if (fd_signal < 0) { - fprintf(stderr, "error creating signalfd\n"); - err(udev, "error creating signalfd\n"); - rc = 5; - goto exit; - } - - /* unnamed socket from workers to the main daemon */ - if (socketpair(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0, worker_watch) < 0) { - fprintf(stderr, "error creating socketpair\n"); - err(udev, "error creating socketpair\n"); - rc = 6; - goto exit; - } - fd_worker = worker_watch[READ_END]; - - udev_builtin_init(udev); - - rules = udev_rules_new(udev, resolve_names); - if (rules == NULL) { - err(udev, "error reading rules\n"); - goto exit; - } - - memset(&ep_ctrl, 0, sizeof(struct epoll_event)); - ep_ctrl.events = EPOLLIN; - ep_ctrl.data.fd = fd_ctrl; - - memset(&ep_inotify, 0, sizeof(struct epoll_event)); - ep_inotify.events = EPOLLIN; - ep_inotify.data.fd = fd_inotify; - - memset(&ep_signal, 0, sizeof(struct epoll_event)); - ep_signal.events = EPOLLIN; - ep_signal.data.fd = fd_signal; - - memset(&ep_netlink, 0, sizeof(struct epoll_event)); - ep_netlink.events = EPOLLIN; - ep_netlink.data.fd = fd_netlink; - - memset(&ep_worker, 0, sizeof(struct epoll_event)); - ep_worker.events = EPOLLIN; - ep_worker.data.fd = fd_worker; - - fd_ep = epoll_create1(EPOLL_CLOEXEC); - if (fd_ep < 0) { - err(udev, "error creating epoll fd: %m\n"); - goto exit; - } - if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_ctrl, &ep_ctrl) < 0 || - epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_inotify, &ep_inotify) < 0 || - epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 || - epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_netlink, &ep_netlink) < 0 || - epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_worker, &ep_worker) < 0) { - err(udev, "fail to add fds to epoll: %m\n"); - goto exit; - } - - /* if needed, convert old database from earlier udev version */ - convert_db(udev); - - if (children_max <= 0) { - int memsize = mem_size_mb(); - - /* set value depending on the amount of RAM */ - if (memsize > 0) - children_max = 128 + (memsize / 8); - else - children_max = 128; - } - info(udev, "set children_max to %u\n", children_max); - - udev_rules_apply_static_dev_perms(rules); - - udev_list_node_init(&event_list); - udev_list_node_init(&worker_list); - - for (;;) { - static unsigned long long last_usec; - struct epoll_event ev[8]; - int fdcount; - int timeout; - bool is_worker, is_signal, is_inotify, is_netlink, is_ctrl; - int i; - - if (udev_exit) { - /* close sources of new events and discard buffered events */ - if (fd_ctrl >= 0) { - epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_ctrl, NULL); - fd_ctrl = -1; - } - if (monitor != NULL) { - epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_netlink, NULL); - udev_monitor_unref(monitor); - monitor = NULL; - } - if (fd_inotify >= 0) { - epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_inotify, NULL); - close(fd_inotify); - fd_inotify = -1; - } - - /* discard queued events and kill workers */ - event_queue_cleanup(udev, EVENT_QUEUED); - worker_kill(udev, 0); - - /* exit after all has cleaned up */ - if (udev_list_node_is_empty(&event_list) && udev_list_node_is_empty(&worker_list)) - break; - - /* timeout at exit for workers to finish */ - timeout = 30 * 1000; - } else if (udev_list_node_is_empty(&event_list) && children <= 2) { - /* we are idle */ - timeout = -1; - } else { - /* kill idle or hanging workers */ - timeout = 3 * 1000; - } - fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), timeout); - if (fdcount < 0) - continue; - - if (fdcount == 0) { - struct udev_list_node *loop; - - /* timeout */ - if (udev_exit) { - err(udev, "timeout, giving up waiting for workers to finish\n"); - break; - } - - /* kill idle workers */ - if (udev_list_node_is_empty(&event_list)) { - info(udev, "cleanup idle workers\n"); - worker_kill(udev, 2); - } - - /* check for hanging events */ - udev_list_node_foreach(loop, &worker_list) { - struct worker *worker = node_to_worker(loop); - - if (worker->state != WORKER_RUNNING) - continue; - - if ((now_usec() - worker->event_start_usec) > 30 * 1000 * 1000) { - err(udev, "worker [%u] timeout, kill it\n", worker->pid, - worker->event ? worker->event->devpath : ""); - kill(worker->pid, SIGKILL); - worker->state = WORKER_KILLED; - /* drop reference taken for state 'running' */ - worker_unref(worker); - if (worker->event) { - err(udev, "seq %llu '%s' killed\n", - udev_device_get_seqnum(worker->event->dev), worker->event->devpath); - worker->event->exitcode = -64; - event_queue_delete(worker->event, true); - worker->event = NULL; - } - } - } - - } - - is_worker = is_signal = is_inotify = is_netlink = is_ctrl = false; - for (i = 0; i < fdcount; i++) { - if (ev[i].data.fd == fd_worker && ev[i].events & EPOLLIN) - is_worker = true; - else if (ev[i].data.fd == fd_netlink && ev[i].events & EPOLLIN) - is_netlink = true; - else if (ev[i].data.fd == fd_signal && ev[i].events & EPOLLIN) - is_signal = true; - else if (ev[i].data.fd == fd_inotify && ev[i].events & EPOLLIN) - is_inotify = true; - else if (ev[i].data.fd == fd_ctrl && ev[i].events & EPOLLIN) - is_ctrl = true; - } - - /* check for changed config, every 3 seconds at most */ - if ((now_usec() - last_usec) > 3 * 1000 * 1000) { - if (check_rules_timestamp(udev)) - reload = true; - if (udev_builtin_validate(udev)) - reload = true; - - last_usec = now_usec(); - } - - /* reload requested, HUP signal received, rules changed, builtin changed */ - if (reload) { - worker_kill(udev, 0); - rules = udev_rules_unref(rules); - udev_builtin_exit(udev); - reload = 0; - } - - /* event has finished */ - if (is_worker) - worker_returned(fd_worker); - - if (is_netlink) { - struct udev_device *dev; - - dev = udev_monitor_receive_device(monitor); - if (dev != NULL) { - udev_device_set_usec_initialized(dev, now_usec()); - if (event_queue_insert(dev) < 0) - udev_device_unref(dev); - } - } - - /* start new events */ - if (!udev_list_node_is_empty(&event_list) && !udev_exit && !stop_exec_queue) { - if (rules == NULL) - rules = udev_rules_new(udev, resolve_names); - if (rules != NULL) - event_queue_start(udev); - } - - if (is_signal) { - struct signalfd_siginfo fdsi; - ssize_t size; - - size = read(fd_signal, &fdsi, sizeof(struct signalfd_siginfo)); - if (size == sizeof(struct signalfd_siginfo)) - handle_signal(udev, fdsi.ssi_signo); - } - - /* we are shutting down, the events below are not handled anymore */ - if (udev_exit) - continue; - - /* device node watch */ - if (is_inotify) - handle_inotify(udev); - - /* - * This needs to be after the inotify handling, to make sure, - * that the ping is send back after the possibly generated - * "change" events by the inotify device node watch. - * - * A single time we may receive a client connection which we need to - * keep open to block the client. It will be closed right before we - * exit. - */ - if (is_ctrl) - ctrl_conn = handle_ctrl_msg(udev_ctrl); - } - - rc = EXIT_SUCCESS; -exit: - udev_queue_export_cleanup(udev_queue_export); - udev_ctrl_cleanup(udev_ctrl); -exit_daemonize: - if (fd_ep >= 0) - close(fd_ep); - worker_list_cleanup(udev); - event_queue_cleanup(udev, EVENT_UNDEF); - udev_rules_unref(rules); - udev_builtin_exit(udev); - if (fd_signal >= 0) - close(fd_signal); - if (worker_watch[READ_END] >= 0) - close(worker_watch[READ_END]); - if (worker_watch[WRITE_END] >= 0) - close(worker_watch[WRITE_END]); - udev_monitor_unref(monitor); - udev_queue_export_unref(udev_queue_export); - udev_ctrl_connection_unref(ctrl_conn); - udev_ctrl_unref(udev_ctrl); - udev_selinux_exit(udev); - udev_unref(udev); - udev_log_close(); - return rc; -} diff --git a/src/udev/src/udevd.xml b/src/udev/src/udevd.xml deleted file mode 100644 index c516eb9793..0000000000 --- a/src/udev/src/udevd.xml +++ /dev/null @@ -1,151 +0,0 @@ - - - - - - - udevd - udev - - - - udevd - 8 - - - - - udevdevent managing daemon - - - - - udevd - - - - - - - - - - - Description - udevd listens to kernel uevents. For every event, udevd executes matching - instructions specified in udev rules. See - udev7 - . - On startup the content of the directory /usr/lib/udev/devices - is copied to /dev. If kernel modules specify static device - nodes, these nodes are created even without a corresponding kernel device, to - allow on-demand loading of kernel modules. Matching permissions specified in udev - rules are applied to these static device nodes. - The behavior of the running daemon can be changed with - udevadm control. - - - Options - - - - - Detach and run in the background. - - - - - - Print debug messages to stderr. - - - - - - Limit the number of parallel executed events. - - - - - - Number of seconds to delay the execution of RUN instructions. - This might be useful when debugging system crashes during coldplug - cause by loading non-working kernel modules. - - - - - - Specify when udevd should resolve names of users and groups. - When set to (the default) names will be - resolved when the rules are parsed. When set to - names will be resolved for every event. - When set to names will never be resolved - and all devices will be owned by root. - - - - - - Print version number. - - - - - - Print help text. - - - - - - Environment - - - UDEV_LOG= - - Set the logging priority. - - - - - - Kernel command line - - - udev.log-priority= - - Set the logging priority. - - - - udev.children-max= - - Limit the number of parallel executed events. - - - - udev.exec-delay= - - Number of seconds to delay the execution of RUN instructions. - This might be useful when debugging system crashes during coldplug - cause by loading non-working kernel modules. - - - - - - Author - Written by Kay Sievers kay.sievers@vrfy.org. - - - - See Also - - udev7 - , - udevadm8 - - - diff --git a/src/udev/src/v4l_id/60-persistent-v4l.rules b/src/udev/src/v4l_id/60-persistent-v4l.rules deleted file mode 100644 index 93c5ee8c27..0000000000 --- a/src/udev/src/v4l_id/60-persistent-v4l.rules +++ /dev/null @@ -1,20 +0,0 @@ -# do not edit this file, it will be overwritten on update - -ACTION=="remove", GOTO="persistent_v4l_end" -SUBSYSTEM!="video4linux", GOTO="persistent_v4l_end" -ENV{MAJOR}=="", GOTO="persistent_v4l_end" - -IMPORT{program}="v4l_id $devnode" - -SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" -KERNEL=="video*", ENV{ID_SERIAL}=="?*", SYMLINK+="v4l/by-id/$env{ID_BUS}-$env{ID_SERIAL}-video-index$attr{index}" - -# check for valid "index" number -TEST!="index", GOTO="persistent_v4l_end" -ATTR{index}!="?*", GOTO="persistent_v4l_end" - -IMPORT{builtin}="path_id" -ENV{ID_PATH}=="?*", KERNEL=="video*|vbi*", SYMLINK+="v4l/by-path/$env{ID_PATH}-video-index$attr{index}" -ENV{ID_PATH}=="?*", KERNEL=="audio*", SYMLINK+="v4l/by-path/$env{ID_PATH}-audio-index$attr{index}" - -LABEL="persistent_v4l_end" diff --git a/src/udev/src/v4l_id/v4l_id.c b/src/udev/src/v4l_id/v4l_id.c deleted file mode 100644 index a2a80b5f43..0000000000 --- a/src/udev/src/v4l_id/v4l_id.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2009 Kay Sievers - * Copyright (c) 2009 Filippo Argiolas - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details: - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -int main (int argc, char *argv[]) -{ - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - {} - }; - int fd; - char *device; - struct v4l2_capability v2cap; - - while (1) { - int option; - - option = getopt_long(argc, argv, "h", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'h': - printf("Usage: v4l_id [--help] \n\n"); - return 0; - default: - return 1; - } - } - device = argv[optind]; - - if (device == NULL) - return 2; - fd = open (device, O_RDONLY); - if (fd < 0) - return 3; - - if (ioctl (fd, VIDIOC_QUERYCAP, &v2cap) == 0) { - printf("ID_V4L_VERSION=2\n"); - printf("ID_V4L_PRODUCT=%s\n", v2cap.card); - printf("ID_V4L_CAPABILITIES=:"); - if ((v2cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) > 0) - printf("capture:"); - if ((v2cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) > 0) - printf("video_output:"); - if ((v2cap.capabilities & V4L2_CAP_VIDEO_OVERLAY) > 0) - printf("video_overlay:"); - if ((v2cap.capabilities & V4L2_CAP_AUDIO) > 0) - printf("audio:"); - if ((v2cap.capabilities & V4L2_CAP_TUNER) > 0) - printf("tuner:"); - if ((v2cap.capabilities & V4L2_CAP_RADIO) > 0) - printf("radio:"); - printf("\n"); - } - - close (fd); - return 0; -} diff --git a/src/udev/test-libudev.c b/src/udev/test-libudev.c new file mode 100644 index 0000000000..6161fb3e31 --- /dev/null +++ b/src/udev/test-libudev.c @@ -0,0 +1,501 @@ +/* + * test-libudev + * + * Copyright (C) 2008 Kay Sievers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libudev.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +static void log_fn(struct udev *udev, + int priority, const char *file, int line, const char *fn, + const char *format, va_list args) +{ + printf("test-libudev: %s %s:%d ", fn, file, line); + vprintf(format, args); +} + +static void print_device(struct udev_device *device) +{ + const char *str; + dev_t devnum; + int count; + struct udev_list_entry *list_entry; + + printf("*** device: %p ***\n", device); + str = udev_device_get_action(device); + if (str != NULL) + printf("action: '%s'\n", str); + + str = udev_device_get_syspath(device); + printf("syspath: '%s'\n", str); + + str = udev_device_get_sysname(device); + printf("sysname: '%s'\n", str); + + str = udev_device_get_sysnum(device); + if (str != NULL) + printf("sysnum: '%s'\n", str); + + str = udev_device_get_devpath(device); + printf("devpath: '%s'\n", str); + + str = udev_device_get_subsystem(device); + if (str != NULL) + printf("subsystem: '%s'\n", str); + + str = udev_device_get_devtype(device); + if (str != NULL) + printf("devtype: '%s'\n", str); + + str = udev_device_get_driver(device); + if (str != NULL) + printf("driver: '%s'\n", str); + + str = udev_device_get_devnode(device); + if (str != NULL) + printf("devname: '%s'\n", str); + + devnum = udev_device_get_devnum(device); + if (major(devnum) > 0) + printf("devnum: %u:%u\n", major(devnum), minor(devnum)); + + count = 0; + udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device)) { + printf("link: '%s'\n", udev_list_entry_get_name(list_entry)); + count++; + } + if (count > 0) + printf("found %i links\n", count); + + count = 0; + udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device)) { + printf("property: '%s=%s'\n", + udev_list_entry_get_name(list_entry), + udev_list_entry_get_value(list_entry)); + count++; + } + if (count > 0) + printf("found %i properties\n", count); + + str = udev_device_get_property_value(device, "MAJOR"); + if (str != NULL) + printf("MAJOR: '%s'\n", str); + + str = udev_device_get_sysattr_value(device, "dev"); + if (str != NULL) + printf("attr{dev}: '%s'\n", str); + + printf("\n"); +} + +static int test_device(struct udev *udev, const char *syspath) +{ + struct udev_device *device; + + printf("looking at device: %s\n", syspath); + device = udev_device_new_from_syspath(udev, syspath); + if (device == NULL) { + printf("no device found\n"); + return -1; + } + print_device(device); + udev_device_unref(device); + return 0; +} + +static int test_device_parents(struct udev *udev, const char *syspath) +{ + struct udev_device *device; + struct udev_device *device_parent; + + printf("looking at device: %s\n", syspath); + device = udev_device_new_from_syspath(udev, syspath); + if (device == NULL) + return -1; + + printf("looking at parents\n"); + device_parent = device; + do { + print_device(device_parent); + device_parent = udev_device_get_parent(device_parent); + } while (device_parent != NULL); + + printf("looking at parents again\n"); + device_parent = device; + do { + print_device(device_parent); + device_parent = udev_device_get_parent(device_parent); + } while (device_parent != NULL); + udev_device_unref(device); + + return 0; +} + +static int test_device_devnum(struct udev *udev) +{ + dev_t devnum = makedev(1, 3); + struct udev_device *device; + + printf("looking up device: %u:%u\n", major(devnum), minor(devnum)); + device = udev_device_new_from_devnum(udev, 'c', devnum); + if (device == NULL) + return -1; + print_device(device); + udev_device_unref(device); + return 0; +} + +static int test_device_subsys_name(struct udev *udev) +{ + struct udev_device *device; + + printf("looking up device: 'block':'sda'\n"); + device = udev_device_new_from_subsystem_sysname(udev, "block", "sda"); + if (device == NULL) + return -1; + print_device(device); + udev_device_unref(device); + + printf("looking up device: 'subsystem':'pci'\n"); + device = udev_device_new_from_subsystem_sysname(udev, "subsystem", "pci"); + if (device == NULL) + return -1; + print_device(device); + udev_device_unref(device); + + printf("looking up device: 'drivers':'scsi:sd'\n"); + device = udev_device_new_from_subsystem_sysname(udev, "drivers", "scsi:sd"); + if (device == NULL) + return -1; + print_device(device); + udev_device_unref(device); + + printf("looking up device: 'module':'printk'\n"); + device = udev_device_new_from_subsystem_sysname(udev, "module", "printk"); + if (device == NULL) + return -1; + print_device(device); + udev_device_unref(device); + return 0; +} + +static int test_enumerate_print_list(struct udev_enumerate *enumerate) +{ + struct udev_list_entry *list_entry; + int count = 0; + + udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(enumerate)) { + struct udev_device *device; + + device = udev_device_new_from_syspath(udev_enumerate_get_udev(enumerate), + udev_list_entry_get_name(list_entry)); + if (device != NULL) { + printf("device: '%s' (%s)\n", + udev_device_get_syspath(device), + udev_device_get_subsystem(device)); + udev_device_unref(device); + count++; + } + } + printf("found %i devices\n\n", count); + return count; +} + +static int test_monitor(struct udev *udev) +{ + struct udev_monitor *udev_monitor = NULL; + int fd_ep; + int fd_udev = -1; + struct epoll_event ep_udev, ep_stdin; + + fd_ep = epoll_create1(EPOLL_CLOEXEC); + if (fd_ep < 0) { + printf("error creating epoll fd: %m\n"); + goto out; + } + + udev_monitor = udev_monitor_new_from_netlink(udev, "udev"); + if (udev_monitor == NULL) { + printf("no socket\n"); + goto out; + } + fd_udev = udev_monitor_get_fd(udev_monitor); + + if (udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "block", NULL) < 0 || + udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "tty", NULL) < 0 || + udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "usb", "usb_device") < 0) { + printf("filter failed\n"); + goto out; + } + + if (udev_monitor_enable_receiving(udev_monitor) < 0) { + printf("bind failed\n"); + goto out; + } + + memset(&ep_udev, 0, sizeof(struct epoll_event)); + ep_udev.events = EPOLLIN; + ep_udev.data.fd = fd_udev; + if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_udev, &ep_udev) < 0) { + printf("fail to add fd to epoll: %m\n"); + goto out; + } + + memset(&ep_stdin, 0, sizeof(struct epoll_event)); + ep_stdin.events = EPOLLIN; + ep_stdin.data.fd = STDIN_FILENO; + if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, STDIN_FILENO, &ep_stdin) < 0) { + printf("fail to add fd to epoll: %m\n"); + goto out; + } + + for (;;) { + int fdcount; + struct epoll_event ev[4]; + struct udev_device *device; + int i; + + printf("waiting for events from udev, press ENTER to exit\n"); + fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), -1); + printf("epoll fd count: %i\n", fdcount); + + for (i = 0; i < fdcount; i++) { + if (ev[i].data.fd == fd_udev && ev[i].events & EPOLLIN) { + device = udev_monitor_receive_device(udev_monitor); + if (device == NULL) { + printf("no device from socket\n"); + continue; + } + print_device(device); + udev_device_unref(device); + } else if (ev[i].data.fd == STDIN_FILENO && ev[i].events & EPOLLIN) { + printf("exiting loop\n"); + goto out; + } + } + } +out: + if (fd_ep >= 0) + close(fd_ep); + udev_monitor_unref(udev_monitor); + return 0; +} + +static int test_queue(struct udev *udev) +{ + struct udev_queue *udev_queue; + unsigned long long int seqnum; + struct udev_list_entry *list_entry; + + udev_queue = udev_queue_new(udev); + if (udev_queue == NULL) + return -1; + seqnum = udev_queue_get_kernel_seqnum(udev_queue); + printf("seqnum kernel: %llu\n", seqnum); + seqnum = udev_queue_get_udev_seqnum(udev_queue); + printf("seqnum udev : %llu\n", seqnum); + + if (udev_queue_get_queue_is_empty(udev_queue)) + printf("queue is empty\n"); + printf("get queue list\n"); + udev_list_entry_foreach(list_entry, udev_queue_get_queued_list_entry(udev_queue)) + printf("queued: '%s' [%s]\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry)); + printf("\n"); + printf("get queue list again\n"); + udev_list_entry_foreach(list_entry, udev_queue_get_queued_list_entry(udev_queue)) + printf("queued: '%s' [%s]\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry)); + printf("\n"); + + list_entry = udev_queue_get_queued_list_entry(udev_queue); + if (list_entry != NULL) { + printf("event [%llu] is queued\n", seqnum); + seqnum = strtoull(udev_list_entry_get_value(list_entry), NULL, 10); + if (udev_queue_get_seqnum_is_finished(udev_queue, seqnum)) + printf("event [%llu] is not finished\n", seqnum); + else + printf("event [%llu] is finished\n", seqnum); + } + printf("\n"); + udev_queue_unref(udev_queue); + return 0; +} + +static int test_enumerate(struct udev *udev, const char *subsystem) +{ + struct udev_enumerate *udev_enumerate; + + printf("enumerate '%s'\n", subsystem == NULL ? "" : subsystem); + udev_enumerate = udev_enumerate_new(udev); + if (udev_enumerate == NULL) + return -1; + udev_enumerate_add_match_subsystem(udev_enumerate, subsystem); + udev_enumerate_scan_devices(udev_enumerate); + test_enumerate_print_list(udev_enumerate); + udev_enumerate_unref(udev_enumerate); + + printf("enumerate 'net' + duplicated scan + null + zero\n"); + udev_enumerate = udev_enumerate_new(udev); + if (udev_enumerate == NULL) + return -1; + udev_enumerate_add_match_subsystem(udev_enumerate, "net"); + udev_enumerate_scan_devices(udev_enumerate); + udev_enumerate_scan_devices(udev_enumerate); + udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/zero"); + udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/null"); + udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/zero"); + udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/null"); + udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/zero"); + udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/null"); + udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/null"); + udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/zero"); + udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/zero"); + udev_enumerate_scan_devices(udev_enumerate); + test_enumerate_print_list(udev_enumerate); + udev_enumerate_unref(udev_enumerate); + + printf("enumerate 'block'\n"); + udev_enumerate = udev_enumerate_new(udev); + if (udev_enumerate == NULL) + return -1; + udev_enumerate_add_match_subsystem(udev_enumerate,"block"); + udev_enumerate_add_match_is_initialized(udev_enumerate); + udev_enumerate_scan_devices(udev_enumerate); + test_enumerate_print_list(udev_enumerate); + udev_enumerate_unref(udev_enumerate); + + printf("enumerate 'not block'\n"); + udev_enumerate = udev_enumerate_new(udev); + if (udev_enumerate == NULL) + return -1; + udev_enumerate_add_nomatch_subsystem(udev_enumerate, "block"); + udev_enumerate_scan_devices(udev_enumerate); + test_enumerate_print_list(udev_enumerate); + udev_enumerate_unref(udev_enumerate); + + printf("enumerate 'pci, mem, vc'\n"); + udev_enumerate = udev_enumerate_new(udev); + if (udev_enumerate == NULL) + return -1; + udev_enumerate_add_match_subsystem(udev_enumerate, "pci"); + udev_enumerate_add_match_subsystem(udev_enumerate, "mem"); + udev_enumerate_add_match_subsystem(udev_enumerate, "vc"); + udev_enumerate_scan_devices(udev_enumerate); + test_enumerate_print_list(udev_enumerate); + udev_enumerate_unref(udev_enumerate); + + printf("enumerate 'subsystem'\n"); + udev_enumerate = udev_enumerate_new(udev); + if (udev_enumerate == NULL) + return -1; + udev_enumerate_scan_subsystems(udev_enumerate); + test_enumerate_print_list(udev_enumerate); + udev_enumerate_unref(udev_enumerate); + + printf("enumerate 'property IF_FS_*=filesystem'\n"); + udev_enumerate = udev_enumerate_new(udev); + if (udev_enumerate == NULL) + return -1; + udev_enumerate_add_match_property(udev_enumerate, "ID_FS*", "filesystem"); + udev_enumerate_scan_devices(udev_enumerate); + test_enumerate_print_list(udev_enumerate); + udev_enumerate_unref(udev_enumerate); + return 0; +} + +int main(int argc, char *argv[]) +{ + struct udev *udev = NULL; + static const struct option options[] = { + { "syspath", required_argument, NULL, 'p' }, + { "subsystem", required_argument, NULL, 's' }, + { "debug", no_argument, NULL, 'd' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + {} + }; + const char *syspath = "/devices/virtual/mem/null"; + const char *subsystem = NULL; + char path[1024]; + const char *str; + + udev = udev_new(); + printf("context: %p\n", udev); + if (udev == NULL) { + printf("no context\n"); + return 1; + } + udev_set_log_fn(udev, log_fn); + printf("set log: %p\n", log_fn); + + for (;;) { + int option; + + option = getopt_long(argc, argv, "+p:s:dhV", options, NULL); + if (option == -1) + break; + + switch (option) { + case 'p': + syspath = optarg; + break; + case 's': + subsystem = optarg; + break; + case 'd': + if (udev_get_log_priority(udev) < LOG_INFO) + udev_set_log_priority(udev, LOG_INFO); + break; + case 'h': + printf("--debug --syspath= --subsystem= --help\n"); + goto out; + case 'V': + printf("%s\n", VERSION); + goto out; + default: + goto out; + } + } + + str = udev_get_sys_path(udev); + printf("sys_path: '%s'\n", str); + str = udev_get_dev_path(udev); + printf("dev_path: '%s'\n", str); + + /* add sys path if needed */ + if (strncmp(syspath, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) != 0) { + snprintf(path, sizeof(path), "%s%s", udev_get_sys_path(udev), syspath); + syspath = path; + } + + test_device(udev, syspath); + test_device_devnum(udev); + test_device_subsys_name(udev); + test_device_parents(udev, syspath); + + test_enumerate(udev, subsystem); + + test_queue(udev); + + test_monitor(udev); +out: + udev_unref(udev); + return 0; +} diff --git a/src/udev/test-udev.c b/src/udev/test-udev.c new file mode 100644 index 0000000000..c9712e974d --- /dev/null +++ b/src/udev/test-udev.c @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2003-2004 Greg Kroah-Hartman + * Copyright (C) 2004-2008 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +void udev_main_log(struct udev *udev, int priority, + const char *file, int line, const char *fn, + const char *format, va_list args) {} + +int main(int argc, char *argv[]) +{ + struct udev *udev; + struct udev_event *event = NULL; + struct udev_device *dev = NULL; + struct udev_rules *rules = NULL; + char syspath[UTIL_PATH_SIZE]; + const char *devpath; + const char *action; + sigset_t mask, sigmask_orig; + int err = -EINVAL; + + udev = udev_new(); + if (udev == NULL) + exit(1); + info(udev, "version %s\n", VERSION); + udev_selinux_init(udev); + + sigprocmask(SIG_SETMASK, NULL, &sigmask_orig); + + action = argv[1]; + if (action == NULL) { + err(udev, "action missing\n"); + goto out; + } + + devpath = argv[2]; + if (devpath == NULL) { + err(udev, "devpath missing\n"); + goto out; + } + + rules = udev_rules_new(udev, 1); + + util_strscpyl(syspath, sizeof(syspath), udev_get_sys_path(udev), devpath, NULL); + dev = udev_device_new_from_syspath(udev, syspath); + if (dev == NULL) { + info(udev, "unknown device '%s'\n", devpath); + goto out; + } + + udev_device_set_action(dev, action); + event = udev_event_new(dev); + + sigfillset(&mask); + sigprocmask(SIG_SETMASK, &mask, &sigmask_orig); + event->fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); + if (event->fd_signal < 0) { + fprintf(stderr, "error creating signalfd\n"); + goto out; + } + + /* do what devtmpfs usually provides us */ + if (udev_device_get_devnode(dev) != NULL) { + mode_t mode; + + if (strcmp(udev_device_get_subsystem(dev), "block") == 0) + mode |= S_IFBLK; + else + mode |= S_IFCHR; + + if (strcmp(action, "remove") != 0) { + util_create_path(udev, udev_device_get_devnode(dev)); + mknod(udev_device_get_devnode(dev), mode, udev_device_get_devnum(dev)); + } else { + unlink(udev_device_get_devnode(dev)); + util_delete_path(udev, udev_device_get_devnode(dev)); + } + } + + err = udev_event_execute_rules(event, rules, &sigmask_orig); + if (err == 0) + udev_event_execute_run(event, NULL); +out: + if (event != NULL && event->fd_signal >= 0) + close(event->fd_signal); + udev_event_unref(event); + udev_device_unref(dev); + udev_rules_unref(rules); + udev_selinux_exit(udev); + udev_unref(udev); + if (err != 0) + return 1; + return 0; +} diff --git a/src/udev/test/rules-test.sh b/src/udev/test/rules-test.sh index 1e224ff8b5..5b3acea31f 100755 --- a/src/udev/test/rules-test.sh +++ b/src/udev/test/rules-test.sh @@ -12,4 +12,4 @@ type python >/dev/null 2>&1 || { exit 0 } -$srcdir/test/rule-syntax-check.py `find $srcdir/rules -name '*.rules'` +$srcdir/src/udev/test/rule-syntax-check.py `find $srcdir/rules -name '*.rules'` diff --git a/src/udev/udev-builtin-blkid.c b/src/udev/udev-builtin-blkid.c new file mode 100644 index 0000000000..e57f03e5a1 --- /dev/null +++ b/src/udev/udev-builtin-blkid.c @@ -0,0 +1,207 @@ +/* + * probe disks for filesystems and partitions + * + * Copyright (C) 2011 Kay Sievers + * Copyright (C) 2011 Karel Zak + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +static void print_property(struct udev_device *dev, bool test, const char *name, const char *value) +{ + char s[265]; + + s[0] = '\0'; + + if (!strcmp(name, "TYPE")) { + udev_builtin_add_property(dev, test, "ID_FS_TYPE", value); + + } else if (!strcmp(name, "USAGE")) { + udev_builtin_add_property(dev, test, "ID_FS_USAGE", value); + + } else if (!strcmp(name, "VERSION")) { + udev_builtin_add_property(dev, test, "ID_FS_VERSION", value); + + } else if (!strcmp(name, "UUID")) { + blkid_safe_string(value, s, sizeof(s)); + udev_builtin_add_property(dev, test, "ID_FS_UUID", s); + blkid_encode_string(value, s, sizeof(s)); + udev_builtin_add_property(dev, test, "ID_FS_UUID_ENC", s); + + } else if (!strcmp(name, "UUID_SUB")) { + blkid_safe_string(value, s, sizeof(s)); + udev_builtin_add_property(dev, test, "ID_FS_UUID_SUB", s); + blkid_encode_string(value, s, sizeof(s)); + udev_builtin_add_property(dev, test, "ID_FS_UUID_SUB_ENC", s); + + } else if (!strcmp(name, "LABEL")) { + blkid_safe_string(value, s, sizeof(s)); + udev_builtin_add_property(dev, test, "ID_FS_LABEL", s); + blkid_encode_string(value, s, sizeof(s)); + udev_builtin_add_property(dev, test, "ID_FS_LABEL_ENC", s); + + } else if (!strcmp(name, "PTTYPE")) { + udev_builtin_add_property(dev, test, "ID_PART_TABLE_TYPE", value); + + } else if (!strcmp(name, "PART_ENTRY_NAME")) { + blkid_encode_string(value, s, sizeof(s)); + udev_builtin_add_property(dev, test, "ID_PART_ENTRY_NAME", s); + + } else if (!strcmp(name, "PART_ENTRY_TYPE")) { + blkid_encode_string(value, s, sizeof(s)); + udev_builtin_add_property(dev, test, "ID_PART_ENTRY_TYPE", s); + + } else if (!strncmp(name, "PART_ENTRY_", 11)) { + util_strscpyl(s, sizeof(s), "ID_", name, NULL); + udev_builtin_add_property(dev, test, s, value); + } +} + +static int probe_superblocks(blkid_probe pr) +{ + struct stat st; + int rc; + + if (fstat(blkid_probe_get_fd(pr), &st)) + return -1; + + blkid_probe_enable_partitions(pr, 1); + + if (!S_ISCHR(st.st_mode) && blkid_probe_get_size(pr) <= 1024 * 1440 && + blkid_probe_is_wholedisk(pr)) { + /* + * check if the small disk is partitioned, if yes then + * don't probe for filesystems. + */ + blkid_probe_enable_superblocks(pr, 0); + + rc = blkid_do_fullprobe(pr); + if (rc < 0) + return rc; /* -1 = error, 1 = nothing, 0 = succes */ + + if (blkid_probe_lookup_value(pr, "PTTYPE", NULL, NULL) == 0) + return 0; /* partition table detected */ + } + + blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS); + blkid_probe_enable_superblocks(pr, 1); + + return blkid_do_safeprobe(pr); +} + +static int builtin_blkid(struct udev_device *dev, int argc, char *argv[], bool test) +{ + struct udev *udev = udev_device_get_udev(dev); + int64_t offset = 0; + bool noraid = false; + int fd = -1; + blkid_probe pr; + const char *data; + const char *name; + int nvals; + int i; + size_t len; + int err = 0; + + static const struct option options[] = { + { "offset", optional_argument, NULL, 'o' }, + { "noraid", no_argument, NULL, 'R' }, + {} + }; + + for (;;) { + int option; + + option = getopt_long(argc, argv, "oR", options, NULL); + if (option == -1) + break; + + switch (option) { + case 'o': + offset = strtoull(optarg, NULL, 0); + break; + case 'R': + noraid = true; + break; + } + } + + pr = blkid_new_probe(); + if (!pr) { + err = -ENOMEM; + return EXIT_FAILURE; + } + + blkid_probe_set_superblocks_flags(pr, + BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID | + BLKID_SUBLKS_TYPE | BLKID_SUBLKS_SECTYPE | + BLKID_SUBLKS_USAGE | BLKID_SUBLKS_VERSION); + + if (noraid) + blkid_probe_filter_superblocks_usage(pr, BLKID_FLTR_NOTIN, BLKID_USAGE_RAID); + + fd = open(udev_device_get_devnode(dev), O_RDONLY|O_CLOEXEC); + if (fd < 0) { + fprintf(stderr, "error: %s: %m\n", udev_device_get_devnode(dev)); + goto out; + } + + err = blkid_probe_set_device(pr, fd, offset, 0); + if (err < 0) + goto out; + + info(udev, "probe %s %sraid offset=%llu\n", + udev_device_get_devnode(dev), + noraid ? "no" : "", (unsigned long long) offset); + + err = probe_superblocks(pr); + if (err < 0) + goto out; + + nvals = blkid_probe_numof_values(pr); + for (i = 0; i < nvals; i++) { + if (blkid_probe_get_value(pr, i, &name, &data, &len)) + continue; + len = strnlen((char *) data, len); + print_property(dev, test, name, (char *) data); + } + + blkid_free_probe(pr); +out: + if (fd > 0) + close(fd); + if (err < 0) + return EXIT_FAILURE; + return EXIT_SUCCESS; +} + +const struct udev_builtin udev_builtin_blkid = { + .name = "blkid", + .cmd = builtin_blkid, + .help = "filesystem and partition probing", + .run_once = true, +}; diff --git a/src/udev/udev-builtin-firmware.c b/src/udev/udev-builtin-firmware.c new file mode 100644 index 0000000000..d212c64b4d --- /dev/null +++ b/src/udev/udev-builtin-firmware.c @@ -0,0 +1,168 @@ +/* + * firmware - Kernel firmware loader + * + * Copyright (C) 2009 Piter Punk + * Copyright (C) 2009-2011 Kay Sievers + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details:* + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +static bool set_loading(struct udev *udev, char *loadpath, const char *state) +{ + FILE *ldfile; + + ldfile = fopen(loadpath, "we"); + if (ldfile == NULL) { + err(udev, "error: can not open '%s'\n", loadpath); + return false; + }; + fprintf(ldfile, "%s\n", state); + fclose(ldfile); + return true; +} + +static bool copy_firmware(struct udev *udev, const char *source, const char *target, size_t size) +{ + char *buf; + FILE *fsource = NULL, *ftarget = NULL; + bool ret = false; + + buf = malloc(size); + if (buf == NULL) { + err(udev,"No memory available to load firmware file"); + return false; + } + + info(udev, "writing '%s' (%zi) to '%s'\n", source, size, target); + + fsource = fopen(source, "re"); + if (fsource == NULL) + goto exit; + ftarget = fopen(target, "we"); + if (ftarget == NULL) + goto exit; + if (fread(buf, size, 1, fsource) != 1) + goto exit; + if (fwrite(buf, size, 1, ftarget) == 1) + ret = true; +exit: + if (ftarget != NULL) + fclose(ftarget); + if (fsource != NULL) + fclose(fsource); + free(buf); + return ret; +} + +static int builtin_firmware(struct udev_device *dev, int argc, char *argv[], bool test) +{ + struct udev *udev = udev_device_get_udev(dev); + static const char *searchpath[] = { FIRMWARE_PATH }; + char fwencpath[UTIL_PATH_SIZE]; + char misspath[UTIL_PATH_SIZE]; + char loadpath[UTIL_PATH_SIZE]; + char datapath[UTIL_PATH_SIZE]; + char fwpath[UTIL_PATH_SIZE]; + const char *firmware; + FILE *fwfile; + struct utsname kernel; + struct stat statbuf; + unsigned int i; + int rc = EXIT_SUCCESS; + + firmware = udev_device_get_property_value(dev, "FIRMWARE"); + if (firmware == NULL) { + err(udev, "firmware parameter missing\n\n"); + rc = EXIT_FAILURE; + goto exit; + } + + /* lookup firmware file */ + uname(&kernel); + for (i = 0; i < ARRAY_SIZE(searchpath); i++) { + util_strscpyl(fwpath, sizeof(fwpath), searchpath[i], kernel.release, "/", firmware, NULL); + dbg(udev, "trying %s\n", fwpath); + fwfile = fopen(fwpath, "re"); + if (fwfile != NULL) + break; + + util_strscpyl(fwpath, sizeof(fwpath), searchpath[i], firmware, NULL); + dbg(udev, "trying %s\n", fwpath); + fwfile = fopen(fwpath, "re"); + if (fwfile != NULL) + break; + } + + util_path_encode(firmware, fwencpath, sizeof(fwencpath)); + util_strscpyl(misspath, sizeof(misspath), udev_get_run_path(udev), "/firmware-missing/", fwencpath, NULL); + util_strscpyl(loadpath, sizeof(loadpath), udev_device_get_syspath(dev), "/loading", NULL); + + if (fwfile == NULL) { + int err; + + /* This link indicates the missing firmware file and the associated device */ + info(udev, "did not find firmware file '%s'\n", firmware); + do { + err = util_create_path(udev, misspath); + if (err != 0 && err != -ENOENT) + break; + err = symlink(udev_device_get_devpath(dev), misspath); + if (err != 0) + err = -errno; + } while (err == -ENOENT); + rc = EXIT_FAILURE; + set_loading(udev, loadpath, "-1"); + goto exit; + } + + if (stat(fwpath, &statbuf) < 0 || statbuf.st_size == 0) { + rc = EXIT_FAILURE; + goto exit; + } + if (unlink(misspath) == 0) + util_delete_path(udev, misspath); + + if (!set_loading(udev, loadpath, "1")) + goto exit; + + util_strscpyl(datapath, sizeof(datapath), udev_device_get_syspath(dev), "/data", NULL); + if (!copy_firmware(udev, fwpath, datapath, statbuf.st_size)) { + err(udev, "error sending firmware '%s' to device\n", firmware); + set_loading(udev, loadpath, "-1"); + rc = EXIT_FAILURE; + goto exit; + }; + + set_loading(udev, loadpath, "0"); +exit: + if (fwfile) + fclose(fwfile); + return rc; +} + +const struct udev_builtin udev_builtin_firmware = { + .name = "firmware", + .cmd = builtin_firmware, + .help = "kernel firmware loader", + .run_once = true, +}; diff --git a/src/udev/udev-builtin-hwdb.c b/src/udev/udev-builtin-hwdb.c new file mode 100644 index 0000000000..aa996f375d --- /dev/null +++ b/src/udev/udev-builtin-hwdb.c @@ -0,0 +1,247 @@ +/* + * usb-db, pci-db - lookup vendor/product database + * + * Copyright (C) 2009 Lennart Poettering + * Copyright (C) 2011 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +static int get_id_attr( + struct udev_device *parent, + const char *name, + uint16_t *value) { + + const char *t; + unsigned u; + + if (!(t = udev_device_get_sysattr_value(parent, name))) { + fprintf(stderr, "%s lacks %s.\n", udev_device_get_syspath(parent), name); + return -1; + } + + if (!strncmp(t, "0x", 2)) + t += 2; + + if (sscanf(t, "%04x", &u) != 1 || u > 0xFFFFU) { + fprintf(stderr, "Failed to parse %s on %s.\n", name, udev_device_get_syspath(parent)); + return -1; + } + + *value = (uint16_t) u; + return 0; +} + +static int get_vid_pid( + struct udev_device *parent, + const char *vendor_attr, + const char *product_attr, + uint16_t *vid, + uint16_t *pid) { + + if (get_id_attr(parent, vendor_attr, vid) < 0) + return -1; + else if (*vid <= 0) { + fprintf(stderr, "Invalid vendor id.\n"); + return -1; + } + + if (get_id_attr(parent, product_attr, pid) < 0) + return -1; + + return 0; +} + +static void rstrip(char *n) { + size_t i; + + for (i = strlen(n); i > 0 && isspace(n[i-1]); i--) + n[i-1] = 0; +} + +#define HEXCHARS "0123456789abcdefABCDEF" +#define WHITESPACE " \t\n\r" +static int lookup_vid_pid(const char *database, + uint16_t vid, uint16_t pid, + char **vendor, char **product) +{ + + FILE *f; + int ret = -1; + int found_vendor = 0; + char *line = NULL; + + *vendor = *product = NULL; + + if (!(f = fopen(database, "rme"))) { + fprintf(stderr, "Failed to open database file '%s': %s\n", database, strerror(errno)); + return -1; + } + + for (;;) { + size_t n; + + if (getline(&line, &n, f) < 0) + break; + + rstrip(line); + + if (line[0] == '#' || line[0] == 0) + continue; + + if (strspn(line, HEXCHARS) == 4) { + unsigned u; + + if (found_vendor) + break; + + if (sscanf(line, "%04x", &u) == 1 && u == vid) { + char *t; + + t = line+4; + t += strspn(t, WHITESPACE); + + if (!(*vendor = strdup(t))) { + fprintf(stderr, "Out of memory.\n"); + goto finish; + } + + found_vendor = 1; + } + + continue; + } + + if (found_vendor && line[0] == '\t' && strspn(line+1, HEXCHARS) == 4) { + unsigned u; + + if (sscanf(line+1, "%04x", &u) == 1 && u == pid) { + char *t; + + t = line+5; + t += strspn(t, WHITESPACE); + + if (!(*product = strdup(t))) { + fprintf(stderr, "Out of memory.\n"); + goto finish; + } + + break; + } + } + } + + ret = 0; + +finish: + free(line); + fclose(f); + + if (ret < 0) { + free(*product); + free(*vendor); + + *product = *vendor = NULL; + } + + return ret; +} + +static struct udev_device *find_device(struct udev_device *dev, const char *subsys, const char *devtype) +{ + const char *str; + + str = udev_device_get_subsystem(dev); + if (str == NULL) + goto try_parent; + if (strcmp(str, subsys) != 0) + goto try_parent; + + if (devtype != NULL) { + str = udev_device_get_devtype(dev); + if (str == NULL) + goto try_parent; + if (strcmp(str, devtype) != 0) + goto try_parent; + } + return dev; +try_parent: + return udev_device_get_parent_with_subsystem_devtype(dev, subsys, devtype); +} + + +static int builtin_db(struct udev_device *dev, bool test, + const char *database, + const char *vendor_attr, const char *product_attr, + const char *subsys, const char *devtype) +{ + struct udev_device *parent; + uint16_t vid = 0, pid = 0; + char *vendor = NULL, *product = NULL; + + parent = find_device(dev, subsys, devtype); + if (!parent) { + fprintf(stderr, "Failed to find device.\n"); + goto finish; + } + + if (get_vid_pid(parent, vendor_attr, product_attr, &vid, &pid) < 0) + goto finish; + + if (lookup_vid_pid(database, vid, pid, &vendor, &product) < 0) + goto finish; + + if (vendor) + udev_builtin_add_property(dev, test, "ID_VENDOR_FROM_DATABASE", vendor); + if (product) + udev_builtin_add_property(dev, test, "ID_MODEL_FROM_DATABASE", product); + +finish: + free(vendor); + free(product); + return 0; +} + +static int builtin_usb_db(struct udev_device *dev, int argc, char *argv[], bool test) +{ + return builtin_db(dev, test, USB_DATABASE, "idVendor", "idProduct", "usb", "usb_device"); +} + +static int builtin_pci_db(struct udev_device *dev, int argc, char *argv[], bool test) +{ + return builtin_db(dev, test, PCI_DATABASE, "vendor", "device", "pci", NULL); +} + +const struct udev_builtin udev_builtin_usb_db = { + .name = "usb-db", + .cmd = builtin_usb_db, + .help = "USB vendor/product database", + .run_once = true, +}; + +const struct udev_builtin udev_builtin_pci_db = { + .name = "pci-db", + .cmd = builtin_pci_db, + .help = "PCI vendor/product database", + .run_once = true, +}; diff --git a/src/udev/udev-builtin-input_id.c b/src/udev/udev-builtin-input_id.c new file mode 100644 index 0000000000..a062ef7c7a --- /dev/null +++ b/src/udev/udev-builtin-input_id.c @@ -0,0 +1,218 @@ +/* + * compose persistent device path + * + * Copyright (C) 2009 Martin Pitt + * Portions Copyright (C) 2004 David Zeuthen, + * Copyright (C) 2011 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +/* we must use this kernel-compatible implementation */ +#define BITS_PER_LONG (sizeof(unsigned long) * 8) +#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) +#define OFF(x) ((x)%BITS_PER_LONG) +#define BIT(x) (1UL<> OFF(bit)) & 1) + +/* + * Read a capability attribute and return bitmask. + * @param dev udev_device + * @param attr sysfs attribute name (e. g. "capabilities/key") + * @param bitmask: Output array which has a sizeof of bitmask_size + */ +static void get_cap_mask(struct udev_device *dev, + struct udev_device *pdev, const char* attr, + unsigned long *bitmask, size_t bitmask_size, + bool test) +{ + struct udev *udev = udev_device_get_udev(dev); + char text[4096]; + unsigned i; + char* word; + unsigned long val; + + snprintf(text, sizeof(text), "%s", udev_device_get_sysattr_value(pdev, attr)); + info(udev, "%s raw kernel attribute: %s\n", attr, text); + + memset (bitmask, 0, bitmask_size); + i = 0; + while ((word = strrchr(text, ' ')) != NULL) { + val = strtoul (word+1, NULL, 16); + if (i < bitmask_size/sizeof(unsigned long)) + bitmask[i] = val; + else + info(udev, "ignoring %s block %lX which is larger than maximum size\n", attr, val); + *word = '\0'; + ++i; + } + val = strtoul (text, NULL, 16); + if (i < bitmask_size / sizeof(unsigned long)) + bitmask[i] = val; + else + info(udev, "ignoring %s block %lX which is larger than maximum size\n", attr, val); + + if (test) { + /* printf pattern with the right unsigned long number of hex chars */ + snprintf(text, sizeof(text), " bit %%4u: %%0%zilX\n", 2 * sizeof(unsigned long)); + info(udev, "%s decoded bit map:\n", attr); + val = bitmask_size / sizeof (unsigned long); + /* skip over leading zeros */ + while (bitmask[val-1] == 0 && val > 0) + --val; + for (i = 0; i < val; ++i) + info(udev, text, i * BITS_PER_LONG, bitmask[i]); + } +} + +/* pointer devices */ +static void test_pointers (struct udev_device *dev, + const unsigned long* bitmask_ev, + const unsigned long* bitmask_abs, + const unsigned long* bitmask_key, + const unsigned long* bitmask_rel, + bool test) +{ + int is_mouse = 0; + int is_touchpad = 0; + + if (!test_bit (EV_KEY, bitmask_ev)) { + if (test_bit (EV_ABS, bitmask_ev) && + test_bit (ABS_X, bitmask_abs) && + test_bit (ABS_Y, bitmask_abs) && + test_bit (ABS_Z, bitmask_abs)) + udev_builtin_add_property(dev, test, "ID_INPUT_ACCELEROMETER", "1"); + return; + } + + if (test_bit (EV_ABS, bitmask_ev) && + test_bit (ABS_X, bitmask_abs) && test_bit (ABS_Y, bitmask_abs)) { + if (test_bit (BTN_STYLUS, bitmask_key) || test_bit (BTN_TOOL_PEN, bitmask_key)) + udev_builtin_add_property(dev, test, "ID_INPUT_TABLET", "1"); + else if (test_bit (BTN_TOOL_FINGER, bitmask_key) && !test_bit (BTN_TOOL_PEN, bitmask_key)) + is_touchpad = 1; + else if (test_bit (BTN_TRIGGER, bitmask_key) || + test_bit (BTN_A, bitmask_key) || + test_bit (BTN_1, bitmask_key)) + udev_builtin_add_property(dev, test, "ID_INPUT_JOYSTICK", "1"); + else if (test_bit (BTN_MOUSE, bitmask_key)) + /* This path is taken by VMware's USB mouse, which has + * absolute axes, but no touch/pressure button. */ + is_mouse = 1; + else if (test_bit (BTN_TOUCH, bitmask_key)) + udev_builtin_add_property(dev, test, "ID_INPUT_TOUCHSCREEN", "1"); + } + + if (test_bit (EV_REL, bitmask_ev) && + test_bit (REL_X, bitmask_rel) && test_bit (REL_Y, bitmask_rel) && + test_bit (BTN_MOUSE, bitmask_key)) + is_mouse = 1; + + if (is_mouse) + udev_builtin_add_property(dev, test, "ID_INPUT_MOUSE", "1"); + if (is_touchpad) + udev_builtin_add_property(dev, test, "ID_INPUT_TOUCHPAD", "1"); +} + +/* key like devices */ +static void test_key (struct udev_device *dev, + const unsigned long* bitmask_ev, + const unsigned long* bitmask_key, + bool test) +{ + struct udev *udev = udev_device_get_udev(dev); + unsigned i; + unsigned long found; + unsigned long mask; + + /* do we have any KEY_* capability? */ + if (!test_bit (EV_KEY, bitmask_ev)) { + info(udev, "test_key: no EV_KEY capability\n"); + return; + } + + /* only consider KEY_* here, not BTN_* */ + found = 0; + for (i = 0; i < BTN_MISC/BITS_PER_LONG; ++i) { + found |= bitmask_key[i]; + info(udev, "test_key: checking bit block %lu for any keys; found=%i\n", i*BITS_PER_LONG, found > 0); + } + /* If there are no keys in the lower block, check the higher block */ + if (!found) { + for (i = KEY_OK; i < BTN_TRIGGER_HAPPY; ++i) { + if (test_bit (i, bitmask_key)) { + info(udev, "test_key: Found key %x in high block\n", i); + found = 1; + break; + } + } + } + + if (found > 0) + udev_builtin_add_property(dev, test, "ID_INPUT_KEY", "1"); + + /* the first 32 bits are ESC, numbers, and Q to D; if we have all of + * those, consider it a full keyboard; do not test KEY_RESERVED, though */ + mask = 0xFFFFFFFE; + if ((bitmask_key[0] & mask) == mask) + udev_builtin_add_property(dev, test, "ID_INPUT_KEYBOARD", "1"); +} + +static int builtin_input_id(struct udev_device *dev, int argc, char *argv[], bool test) +{ + struct udev_device *pdev; + unsigned long bitmask_ev[NBITS(EV_MAX)]; + unsigned long bitmask_abs[NBITS(ABS_MAX)]; + unsigned long bitmask_key[NBITS(KEY_MAX)]; + unsigned long bitmask_rel[NBITS(REL_MAX)]; + + /* walk up the parental chain until we find the real input device; the + * argument is very likely a subdevice of this, like eventN */ + pdev = dev; + while (pdev != NULL && udev_device_get_sysattr_value(pdev, "capabilities/ev") == NULL) + pdev = udev_device_get_parent_with_subsystem_devtype(pdev, "input", NULL); + + /* not an "input" class device */ + if (pdev == NULL) + return EXIT_SUCCESS; + + /* Use this as a flag that input devices were detected, so that this + * program doesn't need to be called more than once per device */ + udev_builtin_add_property(dev, test, "ID_INPUT", "1"); + get_cap_mask(dev, pdev, "capabilities/ev", bitmask_ev, sizeof(bitmask_ev), test); + get_cap_mask(dev, pdev, "capabilities/abs", bitmask_abs, sizeof(bitmask_abs), test); + get_cap_mask(dev, pdev, "capabilities/rel", bitmask_rel, sizeof(bitmask_rel), test); + get_cap_mask(dev, pdev, "capabilities/key", bitmask_key, sizeof(bitmask_key), test); + test_pointers(dev, bitmask_ev, bitmask_abs, bitmask_key, bitmask_rel, test); + test_key(dev, bitmask_ev, bitmask_key, test); + return EXIT_SUCCESS; +} + +const struct udev_builtin udev_builtin_input_id = { + .name = "input_id", + .cmd = builtin_input_id, + .help = "input device properties", +}; diff --git a/src/udev/udev-builtin-kmod.c b/src/udev/udev-builtin-kmod.c new file mode 100644 index 0000000000..57e813f863 --- /dev/null +++ b/src/udev/udev-builtin-kmod.c @@ -0,0 +1,142 @@ +/* + * load kernel modules + * + * Copyright (C) 2011 Kay Sievers + * Copyright (C) 2011 ProFUSION embedded systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +static struct kmod_ctx *ctx; + +static int load_module(struct udev *udev, const char *alias) +{ + struct kmod_list *list = NULL; + struct kmod_list *l; + int err; + + err = kmod_module_new_from_lookup(ctx, alias, &list); + if (err < 0) + return err; + + if (list == NULL) + info(udev, "no module matches '%s'\n", alias); + + kmod_list_foreach(l, list) { + struct kmod_module *mod = kmod_module_get_module(l); + + err = kmod_module_probe_insert_module(mod, KMOD_PROBE_APPLY_BLACKLIST, NULL, NULL, NULL, NULL); + if (err == KMOD_PROBE_APPLY_BLACKLIST) + info(udev, "module '%s' is blacklisted\n", kmod_module_get_name(mod)); + else if (err == 0) + info(udev, "inserted '%s'\n", kmod_module_get_name(mod)); + else + info(udev, "failed to insert '%s'\n", kmod_module_get_name(mod)); + + kmod_module_unref(mod); + } + + kmod_module_unref_list(list); + return err; +} + +static void udev_kmod_log(void *data, int priority, const char *file, int line, + const char *fn, const char *format, va_list args) +{ + udev_main_log(data, priority, file, line, fn, format, args); +} + +/* needs to re-instantiate the context after a reload */ +static int builtin_kmod(struct udev_device *dev, int argc, char *argv[], bool test) +{ + struct udev *udev = udev_device_get_udev(dev); + int i; + + if (!ctx) { + ctx = kmod_new(NULL, NULL); + if (!ctx) + return -ENOMEM; + + info(udev, "load module index\n"); + kmod_set_log_fn(ctx, udev_kmod_log, udev); + kmod_load_resources(ctx); + } + + if (argc < 3 || strcmp(argv[1], "load")) { + err(udev, "expect: %s load \n", argv[0]); + return EXIT_FAILURE; + } + + for (i = 2; argv[i]; i++) { + info(udev, "execute '%s' '%s'\n", argv[1], argv[i]); + load_module(udev, argv[i]); + } + + return EXIT_SUCCESS; +} + +/* called at udev startup */ +static int builtin_kmod_init(struct udev *udev) +{ + if (ctx) + return 0; + + ctx = kmod_new(NULL, NULL); + if (!ctx) + return -ENOMEM; + + info(udev, "load module index\n"); + kmod_set_log_fn(ctx, udev_kmod_log, udev); + kmod_load_resources(ctx); + return 0; +} + +/* called on udev shutdown and reload request */ +static void builtin_kmod_exit(struct udev *udev) +{ + info(udev, "unload module index\n"); + ctx = kmod_unref(ctx); +} + +/* called every couple of seconds during event activity; 'true' if config has changed */ +static bool builtin_kmod_validate(struct udev *udev) +{ + info(udev, "validate module index\n"); + if (kmod_validate_resources(ctx) != KMOD_RESOURCES_OK) + return true; + return false; +} + +const struct udev_builtin udev_builtin_kmod = { + .name = "kmod", + .cmd = builtin_kmod, + .init = builtin_kmod_init, + .exit = builtin_kmod_exit, + .validate = builtin_kmod_validate, + .help = "kernel module loader", + .run_once = false, +}; diff --git a/src/udev/udev-builtin-path_id.c b/src/udev/udev-builtin-path_id.c new file mode 100644 index 0000000000..a8559d2dd4 --- /dev/null +++ b/src/udev/udev-builtin-path_id.c @@ -0,0 +1,498 @@ +/* + * compose persistent device path + * + * Copyright (C) 2009-2011 Kay Sievers + * + * Logic based on Hannes Reinecke's shell script. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +static int path_prepend(char **path, const char *fmt, ...) +{ + va_list va; + char *pre; + int err = 0; + + va_start(va, fmt); + err = vasprintf(&pre, fmt, va); + va_end(va); + if (err < 0) + goto out; + + if (*path != NULL) { + char *new; + + err = asprintf(&new, "%s-%s", pre, *path); + free(pre); + if (err < 0) + goto out; + free(*path); + *path = new; + } else { + *path = pre; + } +out: + return err; +} + +/* +** Linux only supports 32 bit luns. +** See drivers/scsi/scsi_scan.c::scsilun_to_int() for more details. +*/ +static int format_lun_number(struct udev_device *dev, char **path) +{ + unsigned long lun = strtoul(udev_device_get_sysnum(dev), NULL, 10); + + /* address method 0, peripheral device addressing with bus id of zero */ + if (lun < 256) + return path_prepend(path, "lun-%d", lun); + /* handle all other lun addressing methods by using a variant of the original lun format */ + return path_prepend(path, "lun-0x%04x%04x00000000", (lun & 0xffff), (lun >> 16) & 0xffff); +} + +static struct udev_device *skip_subsystem(struct udev_device *dev, const char *subsys) +{ + struct udev_device *parent = dev; + + while (parent != NULL) { + const char *subsystem; + + subsystem = udev_device_get_subsystem(parent); + if (subsystem == NULL || strcmp(subsystem, subsys) != 0) + break; + dev = parent; + parent = udev_device_get_parent(parent); + } + return dev; +} + +static struct udev_device *handle_scsi_fibre_channel(struct udev_device *parent, char **path) +{ + struct udev *udev = udev_device_get_udev(parent); + struct udev_device *targetdev; + struct udev_device *fcdev = NULL; + const char *port; + char *lun = NULL;; + + targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target"); + if (targetdev == NULL) + return NULL; + + fcdev = udev_device_new_from_subsystem_sysname(udev, "fc_transport", udev_device_get_sysname(targetdev)); + if (fcdev == NULL) + return NULL; + port = udev_device_get_sysattr_value(fcdev, "port_name"); + if (port == NULL) { + parent = NULL; + goto out; + } + + format_lun_number(parent, &lun); + path_prepend(path, "fc-%s-%s", port, lun); + if (lun) + free(lun); +out: + udev_device_unref(fcdev); + return parent; +} + +static struct udev_device *handle_scsi_sas(struct udev_device *parent, char **path) +{ + struct udev *udev = udev_device_get_udev(parent); + struct udev_device *targetdev; + struct udev_device *target_parent; + struct udev_device *sasdev; + const char *sas_address; + char *lun = NULL; + + targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target"); + if (targetdev == NULL) + return NULL; + + target_parent = udev_device_get_parent(targetdev); + if (target_parent == NULL) + return NULL; + + sasdev = udev_device_new_from_subsystem_sysname(udev, "sas_device", + udev_device_get_sysname(target_parent)); + if (sasdev == NULL) + return NULL; + + sas_address = udev_device_get_sysattr_value(sasdev, "sas_address"); + if (sas_address == NULL) { + parent = NULL; + goto out; + } + + format_lun_number(parent, &lun); + path_prepend(path, "sas-%s-%s", sas_address, lun); + if (lun) + free(lun); +out: + udev_device_unref(sasdev); + return parent; +} + +static struct udev_device *handle_scsi_iscsi(struct udev_device *parent, char **path) +{ + struct udev *udev = udev_device_get_udev(parent); + struct udev_device *transportdev; + struct udev_device *sessiondev = NULL; + const char *target; + char *connname; + struct udev_device *conndev = NULL; + const char *addr; + const char *port; + char *lun = NULL; + + /* find iscsi session */ + transportdev = parent; + for (;;) { + transportdev = udev_device_get_parent(transportdev); + if (transportdev == NULL) + return NULL; + if (strncmp(udev_device_get_sysname(transportdev), "session", 7) == 0) + break; + } + + /* find iscsi session device */ + sessiondev = udev_device_new_from_subsystem_sysname(udev, "iscsi_session", udev_device_get_sysname(transportdev)); + if (sessiondev == NULL) + return NULL; + target = udev_device_get_sysattr_value(sessiondev, "targetname"); + if (target == NULL) { + parent = NULL; + goto out; + } + + if (asprintf(&connname, "connection%s:0", udev_device_get_sysnum(transportdev)) < 0) { + parent = NULL; + goto out; + } + conndev = udev_device_new_from_subsystem_sysname(udev, "iscsi_connection", connname); + free(connname); + if (conndev == NULL) { + parent = NULL; + goto out; + } + addr = udev_device_get_sysattr_value(conndev, "persistent_address"); + port = udev_device_get_sysattr_value(conndev, "persistent_port"); + if (addr == NULL || port == NULL) { + parent = NULL; + goto out; + } + + format_lun_number(parent, &lun); + path_prepend(path, "ip-%s:%s-iscsi-%s-%s", addr, port, target, lun); + if (lun) + free(lun); +out: + udev_device_unref(sessiondev); + udev_device_unref(conndev); + return parent; +} + +static struct udev_device *handle_scsi_default(struct udev_device *parent, char **path) +{ + struct udev_device *hostdev; + int host, bus, target, lun; + const char *name; + char *base; + char *pos; + DIR *dir; + struct dirent *dent; + int basenum; + + hostdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host"); + if (hostdev == NULL) + return NULL; + + name = udev_device_get_sysname(parent); + if (sscanf(name, "%d:%d:%d:%d", &host, &bus, &target, &lun) != 4) + return NULL; + + /* rebase host offset to get the local relative number */ + basenum = -1; + base = strdup(udev_device_get_syspath(hostdev)); + if (base == NULL) + return NULL; + pos = strrchr(base, '/'); + if (pos == NULL) { + parent = NULL; + goto out; + } + pos[0] = '\0'; + dir = opendir(base); + if (dir == NULL) { + parent = NULL; + goto out; + } + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + char *rest; + int i; + + if (dent->d_name[0] == '.') + continue; + if (dent->d_type != DT_DIR && dent->d_type != DT_LNK) + continue; + if (strncmp(dent->d_name, "host", 4) != 0) + continue; + i = strtoul(&dent->d_name[4], &rest, 10); + if (rest[0] != '\0') + continue; + /* + * find the smallest number; the host really needs to export its + * own instance number per parent device; relying on the global host + * enumeration and plainly rebasing the numbers sounds unreliable + */ + if (basenum == -1 || i < basenum) + basenum = i; + } + closedir(dir); + if (basenum == -1) { + parent = NULL; + goto out; + } + host -= basenum; + + path_prepend(path, "scsi-%u:%u:%u:%u", host, bus, target, lun); +out: + free(base); + return hostdev; +} + +static struct udev_device *handle_scsi(struct udev_device *parent, char **path) +{ + const char *devtype; + const char *name; + const char *id; + + devtype = udev_device_get_devtype(parent); + if (devtype == NULL || strcmp(devtype, "scsi_device") != 0) + return parent; + + /* firewire */ + id = udev_device_get_sysattr_value(parent, "ieee1394_id"); + if (id != NULL) { + parent = skip_subsystem(parent, "scsi"); + path_prepend(path, "ieee1394-0x%s", id); + goto out; + } + + /* lousy scsi sysfs does not have a "subsystem" for the transport */ + name = udev_device_get_syspath(parent); + + if (strstr(name, "/rport-") != NULL) { + parent = handle_scsi_fibre_channel(parent, path); + goto out; + } + + if (strstr(name, "/end_device-") != NULL) { + parent = handle_scsi_sas(parent, path); + goto out; + } + + if (strstr(name, "/session") != NULL) { + parent = handle_scsi_iscsi(parent, path); + goto out; + } + + /* + * We do not support the ATA transport class, it creates duplicated link + * names as the fake SCSI host adapters are all separated, they are all + * re-based as host == 0. ATA should just stop faking two duplicated + * hierarchies for a single topology and leave the SCSI stuff alone; + * until that happens, there are no by-path/ links for ATA devices behind + * an ATA transport class. + */ + if (strstr(name, "/ata") != NULL) { + parent = NULL; + goto out; + } + + parent = handle_scsi_default(parent, path); +out: + return parent; +} + +static void handle_scsi_tape(struct udev_device *dev, char **path) +{ + const char *name; + + /* must be the last device in the syspath */ + if (*path != NULL) + return; + + name = udev_device_get_sysname(dev); + if (strncmp(name, "nst", 3) == 0 && strchr("lma", name[3]) != NULL) + path_prepend(path, "nst%c", name[3]); + else if (strncmp(name, "st", 2) == 0 && strchr("lma", name[2]) != NULL) + path_prepend(path, "st%c", name[2]); +} + +static struct udev_device *handle_usb(struct udev_device *parent, char **path) +{ + const char *devtype; + const char *str; + const char *port; + + devtype = udev_device_get_devtype(parent); + if (devtype == NULL) + return parent; + if (strcmp(devtype, "usb_interface") != 0 && strcmp(devtype, "usb_device") != 0) + return parent; + + str = udev_device_get_sysname(parent); + port = strchr(str, '-'); + if (port == NULL) + return parent; + port++; + + parent = skip_subsystem(parent, "usb"); + path_prepend(path, "usb-0:%s", port); + return parent; +} + +static struct udev_device *handle_ccw(struct udev_device *parent, struct udev_device *dev, char **path) +{ + struct udev_device *scsi_dev; + + scsi_dev = udev_device_get_parent_with_subsystem_devtype(dev, "scsi", "scsi_device"); + if (scsi_dev != NULL) { + const char *wwpn; + const char *lun; + const char *hba_id; + + hba_id = udev_device_get_sysattr_value(scsi_dev, "hba_id"); + wwpn = udev_device_get_sysattr_value(scsi_dev, "wwpn"); + lun = udev_device_get_sysattr_value(scsi_dev, "fcp_lun"); + if (hba_id != NULL && lun != NULL && wwpn != NULL) { + path_prepend(path, "ccw-%s-zfcp-%s:%s", hba_id, wwpn, lun); + goto out; + } + } + + path_prepend(path, "ccw-%s", udev_device_get_sysname(parent)); +out: + parent = skip_subsystem(parent, "ccw"); + return parent; +} + +static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool test) +{ + struct udev_device *parent; + char *path = NULL; + + /* S390 ccw bus */ + parent = udev_device_get_parent_with_subsystem_devtype(dev, "ccw", NULL); + if (parent != NULL) { + handle_ccw(parent, dev, &path); + goto out; + } + + /* walk up the chain of devices and compose path */ + parent = dev; + while (parent != NULL) { + const char *subsys; + + subsys = udev_device_get_subsystem(parent); + if (subsys == NULL) { + ; + } else if (strcmp(subsys, "scsi_tape") == 0) { + handle_scsi_tape(parent, &path); + } else if (strcmp(subsys, "scsi") == 0) { + parent = handle_scsi(parent, &path); + } else if (strcmp(subsys, "usb") == 0) { + parent = handle_usb(parent, &path); + } else if (strcmp(subsys, "serio") == 0) { + path_prepend(&path, "serio-%s", udev_device_get_sysnum(parent)); + parent = skip_subsystem(parent, "serio"); + } else if (strcmp(subsys, "pci") == 0) { + path_prepend(&path, "pci-%s", udev_device_get_sysname(parent)); + parent = skip_subsystem(parent, "pci"); + } else if (strcmp(subsys, "platform") == 0) { + path_prepend(&path, "platform-%s", udev_device_get_sysname(parent)); + parent = skip_subsystem(parent, "platform"); + } else if (strcmp(subsys, "acpi") == 0) { + path_prepend(&path, "acpi-%s", udev_device_get_sysname(parent)); + parent = skip_subsystem(parent, "acpi"); + } else if (strcmp(subsys, "xen") == 0) { + path_prepend(&path, "xen-%s", udev_device_get_sysname(parent)); + parent = skip_subsystem(parent, "xen"); + } else if (strcmp(subsys, "virtio") == 0) { + path_prepend(&path, "virtio-pci-%s", udev_device_get_sysname(parent)); + parent = skip_subsystem(parent, "virtio"); + } + + parent = udev_device_get_parent(parent); + } +out: + if (path != NULL) { + char tag[UTIL_NAME_SIZE]; + size_t i; + const char *p; + + /* compose valid udev tag name */ + for (p = path, i = 0; *p; p++) { + if ((*p >= '0' && *p <= '9') || + (*p >= 'A' && *p <= 'Z') || + (*p >= 'a' && *p <= 'z') || + *p == '-') { + tag[i++] = *p; + continue; + } + + /* skip all leading '_' */ + if (i == 0) + continue; + + /* avoid second '_' */ + if (tag[i-1] == '_') + continue; + + tag[i++] = '_'; + } + /* strip trailing '_' */ + while (i > 0 && tag[i-1] == '_') + i--; + tag[i] = '\0'; + + udev_builtin_add_property(dev, test, "ID_PATH", path); + udev_builtin_add_property(dev, test, "ID_PATH_TAG", tag); + free(path); + return EXIT_SUCCESS; + } + return EXIT_FAILURE; +} + +const struct udev_builtin udev_builtin_path_id = { + .name = "path_id", + .cmd = builtin_path_id, + .help = "compose persistent device path", + .run_once = true, +}; diff --git a/src/udev/udev-builtin-usb_id.c b/src/udev/udev-builtin-usb_id.c new file mode 100644 index 0000000000..85828e32d7 --- /dev/null +++ b/src/udev/udev-builtin-usb_id.c @@ -0,0 +1,482 @@ +/* + * USB device properties and persistent device path + * + * Copyright (c) 2005 SUSE Linux Products GmbH, Germany + * Author: Hannes Reinecke + * + * Copyright (C) 2005-2011 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +static void set_usb_iftype(char *to, int if_class_num, size_t len) +{ + char *type = "generic"; + + switch (if_class_num) { + case 1: + type = "audio"; + break; + case 2: /* CDC-Control */ + break; + case 3: + type = "hid"; + break; + case 5: /* Physical */ + break; + case 6: + type = "media"; + break; + case 7: + type = "printer"; + break; + case 8: + type = "storage"; + break; + case 9: + type = "hub"; + break; + case 0x0a: /* CDC-Data */ + break; + case 0x0b: /* Chip/Smart Card */ + break; + case 0x0d: /* Content Security */ + break; + case 0x0e: + type = "video"; + break; + case 0xdc: /* Diagnostic Device */ + break; + case 0xe0: /* Wireless Controller */ + break; + case 0xfe: /* Application-specific */ + break; + case 0xff: /* Vendor-specific */ + break; + default: + break; + } + strncpy(to, type, len); + to[len-1] = '\0'; +} + +static int set_usb_mass_storage_ifsubtype(char *to, const char *from, size_t len) +{ + int type_num = 0; + char *eptr; + char *type = "generic"; + + type_num = strtoul(from, &eptr, 0); + if (eptr != from) { + switch (type_num) { + case 2: + type = "atapi"; + break; + case 3: + type = "tape"; + break; + case 4: /* UFI */ + case 5: /* SFF-8070i */ + type = "floppy"; + break; + case 1: /* RBC devices */ + type = "rbc"; + break; + case 6: /* Transparent SPC-2 devices */ + type = "scsi"; + break; + default: + break; + } + } + util_strscpy(to, len, type); + return type_num; +} + +static void set_scsi_type(char *to, const char *from, size_t len) +{ + int type_num; + char *eptr; + char *type = "generic"; + + type_num = strtoul(from, &eptr, 0); + if (eptr != from) { + switch (type_num) { + case 0: + case 0xe: + type = "disk"; + break; + case 1: + type = "tape"; + break; + case 4: + case 7: + case 0xf: + type = "optical"; + break; + case 5: + type = "cd"; + break; + default: + break; + } + } + util_strscpy(to, len, type); +} + +#define USB_DT_DEVICE 0x01 +#define USB_DT_INTERFACE 0x04 + +static int dev_if_packed_info(struct udev_device *dev, char *ifs_str, size_t len) +{ + char *filename = NULL; + int fd; + ssize_t size; + unsigned char buf[18 + 65535]; + unsigned int pos, strpos; + struct usb_interface_descriptor { + u_int8_t bLength; + u_int8_t bDescriptorType; + u_int8_t bInterfaceNumber; + u_int8_t bAlternateSetting; + u_int8_t bNumEndpoints; + u_int8_t bInterfaceClass; + u_int8_t bInterfaceSubClass; + u_int8_t bInterfaceProtocol; + u_int8_t iInterface; + } __attribute__((packed)); + int err = 0; + + if (asprintf(&filename, "%s/descriptors", udev_device_get_syspath(dev)) < 0) { + err = -1; + goto out; + } + fd = open(filename, O_RDONLY|O_CLOEXEC); + if (fd < 0) { + fprintf(stderr, "error opening USB device 'descriptors' file\n"); + err = -1; + goto out; + } + size = read(fd, buf, sizeof(buf)); + close(fd); + if (size < 18 || size == sizeof(buf)) { + err = -1; + goto out; + } + + pos = 0; + strpos = 0; + ifs_str[0] = '\0'; + while (pos < sizeof(buf) && strpos+7 < len-2) { + struct usb_interface_descriptor *desc; + char if_str[8]; + + desc = (struct usb_interface_descriptor *) &buf[pos]; + if (desc->bLength < 3) + break; + pos += desc->bLength; + + if (desc->bDescriptorType != USB_DT_INTERFACE) + continue; + + if (snprintf(if_str, 8, ":%02x%02x%02x", + desc->bInterfaceClass, + desc->bInterfaceSubClass, + desc->bInterfaceProtocol) != 7) + continue; + + if (strstr(ifs_str, if_str) != NULL) + continue; + + memcpy(&ifs_str[strpos], if_str, 8), + strpos += 7; + } + if (strpos > 0) { + ifs_str[strpos++] = ':'; + ifs_str[strpos++] = '\0'; + } +out: + free(filename); + return err; +} + +/* + * A unique USB identification is generated like this: + * + * 1.) Get the USB device type from InterfaceClass and InterfaceSubClass + * 2.) If the device type is 'Mass-Storage/SPC-2' or 'Mass-Storage/RBC' + * use the SCSI vendor and model as USB-Vendor and USB-model. + * 3.) Otherwise use the USB manufacturer and product as + * USB-Vendor and USB-model. Any non-printable characters + * in those strings will be skipped; a slash '/' will be converted + * into a full stop '.'. + * 4.) If that fails, too, we will use idVendor and idProduct + * as USB-Vendor and USB-model. + * 5.) The USB identification is the USB-vendor and USB-model + * string concatenated with an underscore '_'. + * 6.) If the device supplies a serial number, this number + * is concatenated with the identification with an underscore '_'. + */ +static int builtin_usb_id(struct udev_device *dev, int argc, char *argv[], bool test) +{ + char vendor_str[64]; + char vendor_str_enc[256]; + const char *vendor_id; + char model_str[64]; + char model_str_enc[256]; + const char *product_id; + char serial_str[UTIL_NAME_SIZE]; + char packed_if_str[UTIL_NAME_SIZE]; + char revision_str[64]; + char type_str[64]; + char instance_str[64]; + const char *ifnum = NULL; + const char *driver = NULL; + char serial[256]; + + struct udev *udev = udev_device_get_udev(dev); + struct udev_device *dev_interface = NULL; + struct udev_device *dev_usb = NULL; + const char *if_class, *if_subclass; + int if_class_num; + int protocol = 0; + size_t l; + char *s; + + vendor_str[0] = '\0'; + model_str[0] = '\0'; + serial_str[0] = '\0'; + packed_if_str[0] = '\0'; + revision_str[0] = '\0'; + type_str[0] = '\0'; + instance_str[0] = '\0'; + + dbg(udev, "syspath %s\n", udev_device_get_syspath(dev)); + + /* shortcut, if we are called directly for a "usb_device" type */ + if (udev_device_get_devtype(dev) != NULL && strcmp(udev_device_get_devtype(dev), "usb_device") == 0) { + dev_if_packed_info(dev, packed_if_str, sizeof(packed_if_str)); + dev_usb = dev; + goto fallback; + } + + /* usb interface directory */ + dev_interface = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_interface"); + if (dev_interface == NULL) { + info(udev, "unable to access usb_interface device of '%s'\n", + udev_device_get_syspath(dev)); + return EXIT_FAILURE; + } + + ifnum = udev_device_get_sysattr_value(dev_interface, "bInterfaceNumber"); + driver = udev_device_get_sysattr_value(dev_interface, "driver"); + + if_class = udev_device_get_sysattr_value(dev_interface, "bInterfaceClass"); + if (!if_class) { + info(udev, "%s: cannot get bInterfaceClass attribute\n", + udev_device_get_sysname(dev)); + return EXIT_FAILURE; + } + + if_class_num = strtoul(if_class, NULL, 16); + if (if_class_num == 8) { + /* mass storage */ + if_subclass = udev_device_get_sysattr_value(dev_interface, "bInterfaceSubClass"); + if (if_subclass != NULL) + protocol = set_usb_mass_storage_ifsubtype(type_str, if_subclass, sizeof(type_str)-1); + } else { + set_usb_iftype(type_str, if_class_num, sizeof(type_str)-1); + } + + info(udev, "%s: if_class %d protocol %d\n", + udev_device_get_syspath(dev_interface), if_class_num, protocol); + + /* usb device directory */ + dev_usb = udev_device_get_parent_with_subsystem_devtype(dev_interface, "usb", "usb_device"); + if (!dev_usb) { + info(udev, "unable to find parent 'usb' device of '%s'\n", + udev_device_get_syspath(dev)); + return EXIT_FAILURE; + } + + /* all interfaces of the device in a single string */ + dev_if_packed_info(dev_usb, packed_if_str, sizeof(packed_if_str)); + + /* mass storage : SCSI or ATAPI */ + if ((protocol == 6 || protocol == 2)) { + struct udev_device *dev_scsi; + const char *scsi_model, *scsi_vendor, *scsi_type, *scsi_rev; + int host, bus, target, lun; + + /* get scsi device */ + dev_scsi = udev_device_get_parent_with_subsystem_devtype(dev, "scsi", "scsi_device"); + if (dev_scsi == NULL) { + info(udev, "unable to find parent 'scsi' device of '%s'\n", + udev_device_get_syspath(dev)); + goto fallback; + } + if (sscanf(udev_device_get_sysname(dev_scsi), "%d:%d:%d:%d", &host, &bus, &target, &lun) != 4) { + info(udev, "invalid scsi device '%s'\n", udev_device_get_sysname(dev_scsi)); + goto fallback; + } + + /* Generic SPC-2 device */ + scsi_vendor = udev_device_get_sysattr_value(dev_scsi, "vendor"); + if (!scsi_vendor) { + info(udev, "%s: cannot get SCSI vendor attribute\n", + udev_device_get_sysname(dev_scsi)); + goto fallback; + } + udev_util_encode_string(scsi_vendor, vendor_str_enc, sizeof(vendor_str_enc)); + util_replace_whitespace(scsi_vendor, vendor_str, sizeof(vendor_str)-1); + util_replace_chars(vendor_str, NULL); + + scsi_model = udev_device_get_sysattr_value(dev_scsi, "model"); + if (!scsi_model) { + info(udev, "%s: cannot get SCSI model attribute\n", + udev_device_get_sysname(dev_scsi)); + goto fallback; + } + udev_util_encode_string(scsi_model, model_str_enc, sizeof(model_str_enc)); + util_replace_whitespace(scsi_model, model_str, sizeof(model_str)-1); + util_replace_chars(model_str, NULL); + + scsi_type = udev_device_get_sysattr_value(dev_scsi, "type"); + if (!scsi_type) { + info(udev, "%s: cannot get SCSI type attribute\n", + udev_device_get_sysname(dev_scsi)); + goto fallback; + } + set_scsi_type(type_str, scsi_type, sizeof(type_str)-1); + + scsi_rev = udev_device_get_sysattr_value(dev_scsi, "rev"); + if (!scsi_rev) { + info(udev, "%s: cannot get SCSI revision attribute\n", + udev_device_get_sysname(dev_scsi)); + goto fallback; + } + util_replace_whitespace(scsi_rev, revision_str, sizeof(revision_str)-1); + util_replace_chars(revision_str, NULL); + + /* + * some broken devices have the same identifiers + * for all luns, export the target:lun number + */ + sprintf(instance_str, "%d:%d", target, lun); + } + +fallback: + vendor_id = udev_device_get_sysattr_value(dev_usb, "idVendor"); + product_id = udev_device_get_sysattr_value(dev_usb, "idProduct"); + + /* fallback to USB vendor & device */ + if (vendor_str[0] == '\0') { + const char *usb_vendor = NULL; + + usb_vendor = udev_device_get_sysattr_value(dev_usb, "manufacturer"); + if (!usb_vendor) + usb_vendor = vendor_id; + if (!usb_vendor) { + info(udev, "No USB vendor information available\n"); + return EXIT_FAILURE; + } + udev_util_encode_string(usb_vendor, vendor_str_enc, sizeof(vendor_str_enc)); + util_replace_whitespace(usb_vendor, vendor_str, sizeof(vendor_str)-1); + util_replace_chars(vendor_str, NULL); + } + + if (model_str[0] == '\0') { + const char *usb_model = NULL; + + usb_model = udev_device_get_sysattr_value(dev_usb, "product"); + if (!usb_model) + usb_model = product_id; + if (!usb_model) { + dbg(udev, "No USB model information available\n"); + return EXIT_FAILURE; + } + udev_util_encode_string(usb_model, model_str_enc, sizeof(model_str_enc)); + util_replace_whitespace(usb_model, model_str, sizeof(model_str)-1); + util_replace_chars(model_str, NULL); + } + + if (revision_str[0] == '\0') { + const char *usb_rev; + + usb_rev = udev_device_get_sysattr_value(dev_usb, "bcdDevice"); + if (usb_rev) { + util_replace_whitespace(usb_rev, revision_str, sizeof(revision_str)-1); + util_replace_chars(revision_str, NULL); + } + } + + if (serial_str[0] == '\0') { + const char *usb_serial; + + usb_serial = udev_device_get_sysattr_value(dev_usb, "serial"); + if (usb_serial) { + util_replace_whitespace(usb_serial, serial_str, sizeof(serial_str)-1); + util_replace_chars(serial_str, NULL); + } + } + + s = serial; + l = util_strpcpyl(&s, sizeof(serial), vendor_str, "_", model_str, NULL); + if (serial_str[0] != '\0') + l = util_strpcpyl(&s, l, "_", serial_str, NULL); + + if (instance_str[0] != '\0') + util_strpcpyl(&s, l, "-", instance_str, NULL); + + udev_builtin_add_property(dev, test, "ID_VENDOR", vendor_str); + udev_builtin_add_property(dev, test, "ID_VENDOR_ENC", vendor_str_enc); + udev_builtin_add_property(dev, test, "ID_VENDOR_ID", vendor_id); + udev_builtin_add_property(dev, test, "ID_MODEL", model_str); + udev_builtin_add_property(dev, test, "ID_MODEL_ENC", model_str_enc); + udev_builtin_add_property(dev, test, "ID_MODEL_ID", product_id); + udev_builtin_add_property(dev, test, "ID_REVISION", revision_str); + udev_builtin_add_property(dev, test, "ID_SERIAL", serial); + if (serial_str[0] != '\0') + udev_builtin_add_property(dev, test, "ID_SERIAL_SHORT", serial_str); + if (type_str[0] != '\0') + udev_builtin_add_property(dev, test, "ID_TYPE", type_str); + if (instance_str[0] != '\0') + udev_builtin_add_property(dev, test, "ID_INSTANCE", instance_str); + udev_builtin_add_property(dev, test, "ID_BUS", "usb"); + if (packed_if_str[0] != '\0') + udev_builtin_add_property(dev, test, "ID_USB_INTERFACES", packed_if_str); + if (ifnum != NULL) + udev_builtin_add_property(dev, test, "ID_USB_INTERFACE_NUM", ifnum); + if (driver != NULL) + udev_builtin_add_property(dev, test, "ID_USB_DRIVER", driver); + return EXIT_SUCCESS; +} + +const struct udev_builtin udev_builtin_usb_id = { + .name = "usb_id", + .cmd = builtin_usb_id, + .help = "usb device properties", + .run_once = true, +}; diff --git a/src/udev/udev-builtin.c b/src/udev/udev-builtin.c new file mode 100644 index 0000000000..5bc5fa68f6 --- /dev/null +++ b/src/udev/udev-builtin.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2007-2009 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +static const struct udev_builtin *builtins[] = { + [UDEV_BUILTIN_BLKID] = &udev_builtin_blkid, + [UDEV_BUILTIN_FIRMWARE] = &udev_builtin_firmware, + [UDEV_BUILTIN_INPUT_ID] = &udev_builtin_input_id, + [UDEV_BUILTIN_KMOD] = &udev_builtin_kmod, + [UDEV_BUILTIN_PATH_ID] = &udev_builtin_path_id, + [UDEV_BUILTIN_PCI_DB] = &udev_builtin_pci_db, + [UDEV_BUILTIN_USB_DB] = &udev_builtin_usb_db, + [UDEV_BUILTIN_USB_ID] = &udev_builtin_usb_id, +}; + +int udev_builtin_init(struct udev *udev) +{ + unsigned int i; + int err; + + for (i = 0; i < ARRAY_SIZE(builtins); i++) { + if (builtins[i]->init) { + err = builtins[i]->init(udev); + if (err < 0) + break; + } + } + return err; +} + +void udev_builtin_exit(struct udev *udev) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(builtins); i++) + if (builtins[i]->exit) + builtins[i]->exit(udev); +} + +bool udev_builtin_validate(struct udev *udev) +{ + unsigned int i; + bool change = false; + + for (i = 0; i < ARRAY_SIZE(builtins); i++) + if (builtins[i]->validate) + if (builtins[i]->validate(udev)) + change = true; + return change; +} + +void udev_builtin_list(struct udev *udev) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(builtins); i++) + fprintf(stderr, " %-12s %s\n", builtins[i]->name, builtins[i]->help); +} + +const char *udev_builtin_name(enum udev_builtin_cmd cmd) +{ + return builtins[cmd]->name; +} + +bool udev_builtin_run_once(enum udev_builtin_cmd cmd) +{ + return builtins[cmd]->run_once; +} + +enum udev_builtin_cmd udev_builtin_lookup(const char *command) +{ + char name[UTIL_PATH_SIZE]; + enum udev_builtin_cmd i; + char *pos; + + util_strscpy(name, sizeof(name), command); + pos = strchr(name, ' '); + if (pos) + pos[0] = '\0'; + for (i = 0; i < ARRAY_SIZE(builtins); i++) + if (strcmp(builtins[i]->name, name) == 0) + return i; + return UDEV_BUILTIN_MAX; +} + +int udev_builtin_run(struct udev_device *dev, enum udev_builtin_cmd cmd, const char *command, bool test) +{ + char arg[UTIL_PATH_SIZE]; + int argc; + char *argv[128]; + + optind = 0; + util_strscpy(arg, sizeof(arg), command); + udev_build_argv(udev_device_get_udev(dev), arg, &argc, argv); + return builtins[cmd]->cmd(dev, argc, argv, test); +} + +int udev_builtin_add_property(struct udev_device *dev, bool test, const char *key, const char *val) +{ + struct udev_list_entry *entry; + + entry = udev_device_add_property(dev, key, val); + /* store in db, skip private keys */ + if (key[0] != '.') + udev_list_entry_set_num(entry, true); + + info(udev_device_get_udev(dev), "%s=%s\n", key, val); + if (test) + printf("%s=%s\n", key, val); + return 0; +} diff --git a/src/udev/udev-ctrl.c b/src/udev/udev-ctrl.c new file mode 100644 index 0000000000..5556f1a77c --- /dev/null +++ b/src/udev/udev-ctrl.c @@ -0,0 +1,494 @@ +/* + * libudev - interface to udev device information + * + * Copyright (C) 2008 Kay Sievers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +/* wire protocol magic must match */ +#define UDEV_CTRL_MAGIC 0xdead1dea + +enum udev_ctrl_msg_type { + UDEV_CTRL_UNKNOWN, + UDEV_CTRL_SET_LOG_LEVEL, + UDEV_CTRL_STOP_EXEC_QUEUE, + UDEV_CTRL_START_EXEC_QUEUE, + UDEV_CTRL_RELOAD, + UDEV_CTRL_SET_ENV, + UDEV_CTRL_SET_CHILDREN_MAX, + UDEV_CTRL_PING, + UDEV_CTRL_EXIT, +}; + +struct udev_ctrl_msg_wire { + char version[16]; + unsigned int magic; + enum udev_ctrl_msg_type type; + union { + int intval; + char buf[256]; + }; +}; + +struct udev_ctrl_msg { + int refcount; + struct udev_ctrl_connection *conn; + struct udev_ctrl_msg_wire ctrl_msg_wire; +}; + +struct udev_ctrl { + int refcount; + struct udev *udev; + int sock; + struct sockaddr_un saddr; + socklen_t addrlen; + bool bound; + bool cleanup_socket; + bool connected; +}; + +struct udev_ctrl_connection { + int refcount; + struct udev_ctrl *uctrl; + int sock; +}; + +struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd) +{ + struct udev_ctrl *uctrl; + + uctrl = calloc(1, sizeof(struct udev_ctrl)); + if (uctrl == NULL) + return NULL; + uctrl->refcount = 1; + uctrl->udev = udev; + + if (fd < 0) { + uctrl->sock = socket(AF_LOCAL, SOCK_SEQPACKET|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); + if (uctrl->sock < 0) { + err(udev, "error getting socket: %m\n"); + udev_ctrl_unref(uctrl); + return NULL; + } + } else { + uctrl->bound = true; + uctrl->sock = fd; + } + + uctrl->saddr.sun_family = AF_LOCAL; + util_strscpyl(uctrl->saddr.sun_path, sizeof(uctrl->saddr.sun_path), + udev_get_run_path(udev), "/control", NULL); + uctrl->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(uctrl->saddr.sun_path); + return uctrl; +} + +struct udev_ctrl *udev_ctrl_new(struct udev *udev) +{ + return udev_ctrl_new_from_fd(udev, -1); +} + +int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl) +{ + int err; + + if (!uctrl->bound) { + err = bind(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen); + if (err < 0 && errno == EADDRINUSE) { + unlink(uctrl->saddr.sun_path); + err = bind(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen); + } + + if (err < 0) { + err = -errno; + err(uctrl->udev, "bind failed: %m\n"); + return err; + } + + err = listen(uctrl->sock, 0); + if (err < 0) { + err = -errno; + err(uctrl->udev, "listen failed: %m\n"); + return err; + } + + uctrl->bound = true; + uctrl->cleanup_socket = true; + } + return 0; +} + +struct udev *udev_ctrl_get_udev(struct udev_ctrl *uctrl) +{ + return uctrl->udev; +} + +struct udev_ctrl *udev_ctrl_ref(struct udev_ctrl *uctrl) +{ + if (uctrl == NULL) + return NULL; + uctrl->refcount++; + return uctrl; +} + +struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl) +{ + if (uctrl == NULL) + return NULL; + uctrl->refcount--; + if (uctrl->refcount > 0) + return uctrl; + if (uctrl->sock >= 0) + close(uctrl->sock); + free(uctrl); + return NULL; +} + +int udev_ctrl_cleanup(struct udev_ctrl *uctrl) +{ + if (uctrl == NULL) + return 0; + if (uctrl->cleanup_socket) + unlink(uctrl->saddr.sun_path); + return 0; +} + +int udev_ctrl_get_fd(struct udev_ctrl *uctrl) +{ + if (uctrl == NULL) + return -EINVAL; + return uctrl->sock; +} + +struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl) +{ + struct udev_ctrl_connection *conn; + struct ucred ucred; + socklen_t slen; + const int on = 1; + + conn = calloc(1, sizeof(struct udev_ctrl_connection)); + if (conn == NULL) + return NULL; + conn->refcount = 1; + conn->uctrl = uctrl; + + conn->sock = accept4(uctrl->sock, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK); + if (conn->sock < 0) { + if (errno != EINTR) + err(uctrl->udev, "unable to receive ctrl connection: %m\n"); + goto err; + } + + /* check peer credential of connection */ + slen = sizeof(ucred); + if (getsockopt(conn->sock, SOL_SOCKET, SO_PEERCRED, &ucred, &slen) < 0) { + err(uctrl->udev, "unable to receive credentials of ctrl connection: %m\n"); + goto err; + } + if (ucred.uid > 0) { + err(uctrl->udev, "sender uid=%i, message ignored\n", ucred.uid); + goto err; + } + + /* enable receiving of the sender credentials in the messages */ + setsockopt(conn->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); + udev_ctrl_ref(uctrl); + return conn; +err: + if (conn->sock >= 0) + close(conn->sock); + free(conn); + return NULL; +} + +struct udev_ctrl_connection *udev_ctrl_connection_ref(struct udev_ctrl_connection *conn) +{ + if (conn == NULL) + return NULL; + conn->refcount++; + return conn; +} + +struct udev_ctrl_connection *udev_ctrl_connection_unref(struct udev_ctrl_connection *conn) +{ + if (conn == NULL) + return NULL; + conn->refcount--; + if (conn->refcount > 0) + return conn; + if (conn->sock >= 0) + close(conn->sock); + udev_ctrl_unref(conn->uctrl); + free(conn); + return NULL; +} + +static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf, int timeout) +{ + struct udev_ctrl_msg_wire ctrl_msg_wire; + int err = 0; + + memset(&ctrl_msg_wire, 0x00, sizeof(struct udev_ctrl_msg_wire)); + strcpy(ctrl_msg_wire.version, "udev-" VERSION); + ctrl_msg_wire.magic = UDEV_CTRL_MAGIC; + ctrl_msg_wire.type = type; + + if (buf != NULL) + util_strscpy(ctrl_msg_wire.buf, sizeof(ctrl_msg_wire.buf), buf); + else + ctrl_msg_wire.intval = intval; + + if (!uctrl->connected) { + if (connect(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen) < 0) { + err = -errno; + goto out; + } + uctrl->connected = true; + } + if (send(uctrl->sock, &ctrl_msg_wire, sizeof(ctrl_msg_wire), 0) < 0) { + err = -errno; + goto out; + } + + /* wait for peer message handling or disconnect */ + for (;;) { + struct pollfd pfd[1]; + int r; + + pfd[0].fd = uctrl->sock; + pfd[0].events = POLLIN; + r = poll(pfd, 1, timeout * 1000); + if (r < 0) { + if (errno == EINTR) + continue; + err = -errno; + break; + } + + if (r > 0 && pfd[0].revents & POLLERR) { + err = -EIO; + break; + } + + if (r == 0) + err = -ETIMEDOUT; + break; + } +out: + return err; +} + +int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, int timeout) +{ + return ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, priority, NULL, timeout); +} + +int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, int timeout) +{ + return ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, 0, NULL, timeout); +} + +int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, int timeout) +{ + return ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL, timeout); +} + +int udev_ctrl_send_reload(struct udev_ctrl *uctrl, int timeout) +{ + return ctrl_send(uctrl, UDEV_CTRL_RELOAD, 0, NULL, timeout); +} + +int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, int timeout) +{ + return ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key, timeout); +} + +int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, int timeout) +{ + return ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL, timeout); +} + +int udev_ctrl_send_ping(struct udev_ctrl *uctrl, int timeout) +{ + return ctrl_send(uctrl, UDEV_CTRL_PING, 0, NULL, timeout); +} + +int udev_ctrl_send_exit(struct udev_ctrl *uctrl, int timeout) +{ + return ctrl_send(uctrl, UDEV_CTRL_EXIT, 0, NULL, timeout); +} + +struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) +{ + struct udev *udev = conn->uctrl->udev; + struct udev_ctrl_msg *uctrl_msg; + ssize_t size; + struct msghdr smsg; + struct cmsghdr *cmsg; + struct iovec iov; + struct ucred *cred; + char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; + + uctrl_msg = calloc(1, sizeof(struct udev_ctrl_msg)); + if (uctrl_msg == NULL) + return NULL; + uctrl_msg->refcount = 1; + uctrl_msg->conn = conn; + udev_ctrl_connection_ref(conn); + + /* wait for the incoming message */ + for(;;) { + struct pollfd pfd[1]; + int r; + + pfd[0].fd = conn->sock; + pfd[0].events = POLLIN; + + r = poll(pfd, 1, 10000); + if (r < 0) { + if (errno == EINTR) + continue; + goto err; + } else if (r == 0) { + err(udev, "timeout waiting for ctrl message\n"); + goto err; + } else { + if (!(pfd[0].revents & POLLIN)) { + err(udev, "ctrl connection error: %m\n"); + goto err; + } + } + + break; + } + + iov.iov_base = &uctrl_msg->ctrl_msg_wire; + iov.iov_len = sizeof(struct udev_ctrl_msg_wire); + memset(&smsg, 0x00, sizeof(struct msghdr)); + smsg.msg_iov = &iov; + smsg.msg_iovlen = 1; + smsg.msg_control = cred_msg; + smsg.msg_controllen = sizeof(cred_msg); + size = recvmsg(conn->sock, &smsg, 0); + if (size < 0) { + err(udev, "unable to receive ctrl message: %m\n"); + goto err; + } + cmsg = CMSG_FIRSTHDR(&smsg); + cred = (struct ucred *) CMSG_DATA(cmsg); + + if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { + err(udev, "no sender credentials received, message ignored\n"); + goto err; + } + + if (cred->uid != 0) { + err(udev, "sender uid=%i, message ignored\n", cred->uid); + goto err; + } + + if (uctrl_msg->ctrl_msg_wire.magic != UDEV_CTRL_MAGIC) { + err(udev, "message magic 0x%08x doesn't match, ignore it\n", uctrl_msg->ctrl_msg_wire.magic); + goto err; + } + + dbg(udev, "created ctrl_msg %p (%i)\n", uctrl_msg, uctrl_msg->ctrl_msg_wire.type); + return uctrl_msg; +err: + udev_ctrl_msg_unref(uctrl_msg); + return NULL; +} + +struct udev_ctrl_msg *udev_ctrl_msg_ref(struct udev_ctrl_msg *ctrl_msg) +{ + if (ctrl_msg == NULL) + return NULL; + ctrl_msg->refcount++; + return ctrl_msg; +} + +struct udev_ctrl_msg *udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg) +{ + if (ctrl_msg == NULL) + return NULL; + ctrl_msg->refcount--; + if (ctrl_msg->refcount > 0) + return ctrl_msg; + dbg(ctrl_msg->conn->uctrl->udev, "release ctrl_msg %p\n", ctrl_msg); + udev_ctrl_connection_unref(ctrl_msg->conn); + free(ctrl_msg); + return NULL; +} + +int udev_ctrl_get_set_log_level(struct udev_ctrl_msg *ctrl_msg) +{ + if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_LOG_LEVEL) + return ctrl_msg->ctrl_msg_wire.intval; + return -1; +} + +int udev_ctrl_get_stop_exec_queue(struct udev_ctrl_msg *ctrl_msg) +{ + if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_STOP_EXEC_QUEUE) + return 1; + return -1; +} + +int udev_ctrl_get_start_exec_queue(struct udev_ctrl_msg *ctrl_msg) +{ + if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_START_EXEC_QUEUE) + return 1; + return -1; +} + +int udev_ctrl_get_reload(struct udev_ctrl_msg *ctrl_msg) +{ + if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_RELOAD) + return 1; + return -1; +} + +const char *udev_ctrl_get_set_env(struct udev_ctrl_msg *ctrl_msg) +{ + if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_ENV) + return ctrl_msg->ctrl_msg_wire.buf; + return NULL; +} + +int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg) +{ + if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_CHILDREN_MAX) + return ctrl_msg->ctrl_msg_wire.intval; + return -1; +} + +int udev_ctrl_get_ping(struct udev_ctrl_msg *ctrl_msg) +{ + if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_PING) + return 1; + return -1; +} + +int udev_ctrl_get_exit(struct udev_ctrl_msg *ctrl_msg) +{ + if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_EXIT) + return 1; + return -1; +} diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c new file mode 100644 index 0000000000..40a6b7d33e --- /dev/null +++ b/src/udev/udev-event.c @@ -0,0 +1,1011 @@ +/* + * Copyright (C) 2003-2010 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +struct udev_event *udev_event_new(struct udev_device *dev) +{ + struct udev *udev = udev_device_get_udev(dev); + struct udev_event *event; + + event = calloc(1, sizeof(struct udev_event)); + if (event == NULL) + return NULL; + event->dev = dev; + event->udev = udev; + udev_list_init(udev, &event->run_list, false); + event->fd_signal = -1; + event->birth_usec = now_usec(); + event->timeout_usec = 30 * 1000 * 1000; + dbg(event->udev, "allocated event %p\n", event); + return event; +} + +void udev_event_unref(struct udev_event *event) +{ + if (event == NULL) + return; + udev_list_cleanup(&event->run_list); + free(event->program_result); + free(event->name); + dbg(event->udev, "free event %p\n", event); + free(event); +} + +size_t udev_event_apply_format(struct udev_event *event, const char *src, char *dest, size_t size) +{ + struct udev_device *dev = event->dev; + enum subst_type { + SUBST_UNKNOWN, + SUBST_DEVNODE, + SUBST_ATTR, + SUBST_ENV, + SUBST_KERNEL, + SUBST_KERNEL_NUMBER, + SUBST_DRIVER, + SUBST_DEVPATH, + SUBST_ID, + SUBST_MAJOR, + SUBST_MINOR, + SUBST_RESULT, + SUBST_PARENT, + SUBST_NAME, + SUBST_LINKS, + SUBST_ROOT, + SUBST_SYS, + }; + static const struct subst_map { + char *name; + char fmt; + enum subst_type type; + } map[] = { + { .name = "devnode", .fmt = 'N', .type = SUBST_DEVNODE }, + { .name = "tempnode", .fmt = 'N', .type = SUBST_DEVNODE }, + { .name = "attr", .fmt = 's', .type = SUBST_ATTR }, + { .name = "sysfs", .fmt = 's', .type = SUBST_ATTR }, + { .name = "env", .fmt = 'E', .type = SUBST_ENV }, + { .name = "kernel", .fmt = 'k', .type = SUBST_KERNEL }, + { .name = "number", .fmt = 'n', .type = SUBST_KERNEL_NUMBER }, + { .name = "driver", .fmt = 'd', .type = SUBST_DRIVER }, + { .name = "devpath", .fmt = 'p', .type = SUBST_DEVPATH }, + { .name = "id", .fmt = 'b', .type = SUBST_ID }, + { .name = "major", .fmt = 'M', .type = SUBST_MAJOR }, + { .name = "minor", .fmt = 'm', .type = SUBST_MINOR }, + { .name = "result", .fmt = 'c', .type = SUBST_RESULT }, + { .name = "parent", .fmt = 'P', .type = SUBST_PARENT }, + { .name = "name", .fmt = 'D', .type = SUBST_NAME }, + { .name = "links", .fmt = 'L', .type = SUBST_LINKS }, + { .name = "root", .fmt = 'r', .type = SUBST_ROOT }, + { .name = "sys", .fmt = 'S', .type = SUBST_SYS }, + }; + const char *from; + char *s; + size_t l; + + from = src; + s = dest; + l = size; + + for (;;) { + enum subst_type type = SUBST_UNKNOWN; + char attrbuf[UTIL_PATH_SIZE]; + char *attr = NULL; + + while (from[0] != '\0') { + if (from[0] == '$') { + /* substitute named variable */ + unsigned int i; + + if (from[1] == '$') { + from++; + goto copy; + } + + for (i = 0; i < ARRAY_SIZE(map); i++) { + if (strncmp(&from[1], map[i].name, strlen(map[i].name)) == 0) { + type = map[i].type; + from += strlen(map[i].name)+1; + dbg(event->udev, "will substitute format name '%s'\n", map[i].name); + goto subst; + } + } + } else if (from[0] == '%') { + /* substitute format char */ + unsigned int i; + + if (from[1] == '%') { + from++; + goto copy; + } + + for (i = 0; i < ARRAY_SIZE(map); i++) { + if (from[1] == map[i].fmt) { + type = map[i].type; + from += 2; + dbg(event->udev, "will substitute format char '%c'\n", map[i].fmt); + goto subst; + } + } + } +copy: + /* copy char */ + if (l == 0) + goto out; + s[0] = from[0]; + from++; + s++; + l--; + } + + goto out; +subst: + /* extract possible $format{attr} */ + if (from[0] == '{') { + unsigned int i; + + from++; + for (i = 0; from[i] != '}'; i++) { + if (from[i] == '\0') { + err(event->udev, "missing closing brace for format '%s'\n", src); + goto out; + } + } + if (i >= sizeof(attrbuf)) + goto out; + memcpy(attrbuf, from, i); + attrbuf[i] = '\0'; + from += i+1; + attr = attrbuf; + } else { + attr = NULL; + } + + switch (type) { + case SUBST_DEVPATH: + l = util_strpcpy(&s, l, udev_device_get_devpath(dev)); + dbg(event->udev, "substitute devpath '%s'\n", udev_device_get_devpath(dev)); + break; + case SUBST_KERNEL: + l = util_strpcpy(&s, l, udev_device_get_sysname(dev)); + dbg(event->udev, "substitute kernel name '%s'\n", udev_device_get_sysname(dev)); + break; + case SUBST_KERNEL_NUMBER: + if (udev_device_get_sysnum(dev) == NULL) + break; + l = util_strpcpy(&s, l, udev_device_get_sysnum(dev)); + dbg(event->udev, "substitute kernel number '%s'\n", udev_device_get_sysnum(dev)); + break; + case SUBST_ID: + if (event->dev_parent == NULL) + break; + l = util_strpcpy(&s, l, udev_device_get_sysname(event->dev_parent)); + dbg(event->udev, "substitute id '%s'\n", udev_device_get_sysname(event->dev_parent)); + break; + case SUBST_DRIVER: { + const char *driver; + + if (event->dev_parent == NULL) + break; + + driver = udev_device_get_driver(event->dev_parent); + if (driver == NULL) + break; + l = util_strpcpy(&s, l, driver); + dbg(event->udev, "substitute driver '%s'\n", driver); + break; + } + case SUBST_MAJOR: { + char num[UTIL_PATH_SIZE]; + + sprintf(num, "%d", major(udev_device_get_devnum(dev))); + l = util_strpcpy(&s, l, num); + dbg(event->udev, "substitute major number '%s'\n", num); + break; + } + case SUBST_MINOR: { + char num[UTIL_PATH_SIZE]; + + sprintf(num, "%d", minor(udev_device_get_devnum(dev))); + l = util_strpcpy(&s, l, num); + dbg(event->udev, "substitute minor number '%s'\n", num); + break; + } + case SUBST_RESULT: { + char *rest; + int i; + + if (event->program_result == NULL) + break; + /* get part part of the result string */ + i = 0; + if (attr != NULL) + i = strtoul(attr, &rest, 10); + if (i > 0) { + char result[UTIL_PATH_SIZE]; + char tmp[UTIL_PATH_SIZE]; + char *cpos; + + dbg(event->udev, "request part #%d of result string\n", i); + util_strscpy(result, sizeof(result), event->program_result); + cpos = result; + while (--i) { + while (cpos[0] != '\0' && !isspace(cpos[0])) + cpos++; + while (isspace(cpos[0])) + cpos++; + } + if (i > 0) { + err(event->udev, "requested part of result string not found\n"); + break; + } + util_strscpy(tmp, sizeof(tmp), cpos); + /* %{2+}c copies the whole string from the second part on */ + if (rest[0] != '+') { + cpos = strchr(tmp, ' '); + if (cpos) + cpos[0] = '\0'; + } + l = util_strpcpy(&s, l, tmp); + dbg(event->udev, "substitute part of result string '%s'\n", tmp); + } else { + l = util_strpcpy(&s, l, event->program_result); + dbg(event->udev, "substitute result string '%s'\n", event->program_result); + } + break; + } + case SUBST_ATTR: { + const char *value = NULL; + char vbuf[UTIL_NAME_SIZE]; + size_t len; + int count; + + if (attr == NULL) { + err(event->udev, "missing file parameter for attr\n"); + break; + } + + /* try to read the value specified by "[dmi/id]product_name" */ + if (util_resolve_subsys_kernel(event->udev, attr, vbuf, sizeof(vbuf), 1) == 0) + value = vbuf; + + /* try to read the attribute the device */ + if (value == NULL) + value = udev_device_get_sysattr_value(event->dev, attr); + + /* try to read the attribute of the parent device, other matches have selected */ + if (value == NULL && event->dev_parent != NULL && event->dev_parent != event->dev) + value = udev_device_get_sysattr_value(event->dev_parent, attr); + + if (value == NULL) + break; + + /* strip trailing whitespace, and replace unwanted characters */ + if (value != vbuf) + util_strscpy(vbuf, sizeof(vbuf), value); + len = strlen(vbuf); + while (len > 0 && isspace(vbuf[--len])) + vbuf[len] = '\0'; + count = util_replace_chars(vbuf, UDEV_ALLOWED_CHARS_INPUT); + if (count > 0) + info(event->udev, "%i character(s) replaced\n" , count); + l = util_strpcpy(&s, l, vbuf); + dbg(event->udev, "substitute sysfs value '%s'\n", vbuf); + break; + } + case SUBST_PARENT: { + struct udev_device *dev_parent; + const char *devnode; + + dev_parent = udev_device_get_parent(event->dev); + if (dev_parent == NULL) + break; + devnode = udev_device_get_devnode(dev_parent); + if (devnode != NULL) { + size_t devlen = strlen(udev_get_dev_path(event->udev))+1; + + l = util_strpcpy(&s, l, &devnode[devlen]); + dbg(event->udev, "found parent '%s', got node name '%s'\n", + udev_device_get_syspath(dev_parent), &devnode[devlen]); + } + break; + } + case SUBST_DEVNODE: + if (udev_device_get_devnode(dev) != NULL) + l = util_strpcpy(&s, l, udev_device_get_devnode(dev)); + break; + case SUBST_NAME: { + if (event->name != NULL) { + l = util_strpcpy(&s, l, event->name); + dbg(event->udev, "substitute custom node name '%s'\n", event->name); + } else if (udev_device_get_devnode(dev) != NULL) { + size_t devlen = strlen(udev_get_dev_path(event->udev))+1; + + l = util_strpcpy(&s, l, &udev_device_get_devnode(dev)[devlen]); + dbg(event->udev, "substitute node name'%s'\n", &udev_device_get_devnode(dev)[devlen]); + } else { + l = util_strpcpy(&s, l, udev_device_get_sysname(dev)); + dbg(event->udev, "substitute device name'%s'\n", udev_device_get_sysname(dev)); + } + break; + } + case SUBST_LINKS: { + size_t devlen = strlen(udev_get_dev_path(event->udev))+1; + struct udev_list_entry *list_entry; + + list_entry = udev_device_get_devlinks_list_entry(dev); + if (list_entry == NULL) + break; + l = util_strpcpy(&s, l, &udev_list_entry_get_name(list_entry)[devlen]); + udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry)) + l = util_strpcpyl(&s, l, " ", &udev_list_entry_get_name(list_entry)[devlen], NULL); + break; + } + case SUBST_ROOT: + l = util_strpcpy(&s, l, udev_get_dev_path(event->udev)); + dbg(event->udev, "substitute udev_root '%s'\n", udev_get_dev_path(event->udev)); + break; + case SUBST_SYS: + l = util_strpcpy(&s, l, udev_get_sys_path(event->udev)); + dbg(event->udev, "substitute sys_path '%s'\n", udev_get_sys_path(event->udev)); + break; + case SUBST_ENV: + if (attr == NULL) { + dbg(event->udev, "missing attribute\n"); + break; + } else { + const char *value; + + value = udev_device_get_property_value(event->dev, attr); + if (value == NULL) + break; + dbg(event->udev, "substitute env '%s=%s'\n", attr, value); + l = util_strpcpy(&s, l, value); + break; + } + default: + err(event->udev, "unknown substitution type=%i\n", type); + break; + } + } + +out: + s[0] = '\0'; + dbg(event->udev, "'%s' -> '%s' (%zu)\n", src, dest, l); + return l; +} + +static int spawn_exec(struct udev_event *event, + const char *cmd, char *const argv[], char **envp, const sigset_t *sigmask, + int fd_stdout, int fd_stderr) +{ + struct udev *udev = event->udev; + int err; + int fd; + + /* discard child output or connect to pipe */ + fd = open("/dev/null", O_RDWR); + if (fd >= 0) { + dup2(fd, STDIN_FILENO); + if (fd_stdout < 0) + dup2(fd, STDOUT_FILENO); + if (fd_stderr < 0) + dup2(fd, STDERR_FILENO); + close(fd); + } else { + err(udev, "open /dev/null failed: %m\n"); + } + + /* connect pipes to std{out,err} */ + if (fd_stdout >= 0) { + dup2(fd_stdout, STDOUT_FILENO); + close(fd_stdout); + } + if (fd_stderr >= 0) { + dup2(fd_stderr, STDERR_FILENO); + close(fd_stderr); + } + + /* terminate child in case parent goes away */ + prctl(PR_SET_PDEATHSIG, SIGTERM); + + /* restore original udev sigmask before exec */ + if (sigmask) + sigprocmask(SIG_SETMASK, sigmask, NULL); + + execve(argv[0], argv, envp); + + /* exec failed */ + err = -errno; + err(udev, "failed to execute '%s' '%s': %m\n", argv[0], cmd); + return err; +} + +static void spawn_read(struct udev_event *event, + const char *cmd, + int fd_stdout, int fd_stderr, + char *result, size_t ressize) +{ + struct udev *udev = event->udev; + size_t respos = 0; + int fd_ep = -1; + struct epoll_event ep_outpipe, ep_errpipe; + + /* read from child if requested */ + if (fd_stdout < 0 && fd_stderr < 0) + return; + + fd_ep = epoll_create1(EPOLL_CLOEXEC); + if (fd_ep < 0) { + err(udev, "error creating epoll fd: %m\n"); + goto out; + } + + if (fd_stdout >= 0) { + memset(&ep_outpipe, 0, sizeof(struct epoll_event)); + ep_outpipe.events = EPOLLIN; + ep_outpipe.data.ptr = &fd_stdout; + if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stdout, &ep_outpipe) < 0) { + err(udev, "fail to add fd to epoll: %m\n"); + goto out; + } + } + + if (fd_stderr >= 0) { + memset(&ep_errpipe, 0, sizeof(struct epoll_event)); + ep_errpipe.events = EPOLLIN; + ep_errpipe.data.ptr = &fd_stderr; + if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stderr, &ep_errpipe) < 0) { + err(udev, "fail to add fd to epoll: %m\n"); + goto out; + } + } + + /* read child output */ + while (fd_stdout >= 0 || fd_stderr >= 0) { + int timeout; + int fdcount; + struct epoll_event ev[4]; + int i; + + if (event->timeout_usec > 0) { + unsigned long long age_usec; + + age_usec = now_usec() - event->birth_usec; + if (age_usec >= event->timeout_usec) { + err(udev, "timeout '%s'\n", cmd); + goto out; + } + timeout = ((event->timeout_usec - age_usec) / 1000) + 1000; + } else { + timeout = -1; + } + + fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), timeout); + if (fdcount < 0) { + if (errno == EINTR) + continue; + err(udev, "failed to poll: %m\n"); + goto out; + } + if (fdcount == 0) { + err(udev, "timeout '%s'\n", cmd); + goto out; + } + + for (i = 0; i < fdcount; i++) { + int *fd = (int *)ev[i].data.ptr; + + if (ev[i].events & EPOLLIN) { + ssize_t count; + char buf[4096]; + + count = read(*fd, buf, sizeof(buf)-1); + if (count <= 0) + continue; + buf[count] = '\0'; + + /* store stdout result */ + if (result != NULL && *fd == fd_stdout) { + if (respos + count < ressize) { + memcpy(&result[respos], buf, count); + respos += count; + } else { + err(udev, "'%s' ressize %zd too short\n", cmd, ressize); + } + } + + /* log debug output only if we watch stderr */ + if (fd_stderr >= 0) { + char *pos; + char *line; + + pos = buf; + while ((line = strsep(&pos, "\n"))) { + if (pos != NULL || line[0] != '\0') + info(udev, "'%s'(%s) '%s'\n", cmd, *fd == fd_stdout ? "out" : "err" , line); + } + } + } else if (ev[i].events & EPOLLHUP) { + if (epoll_ctl(fd_ep, EPOLL_CTL_DEL, *fd, NULL) < 0) { + err(udev, "failed to remove fd from epoll: %m\n"); + goto out; + } + *fd = -1; + } + } + } + + /* return the child's stdout string */ + if (result != NULL) { + result[respos] = '\0'; + dbg(udev, "result='%s'\n", result); + } +out: + if (fd_ep >= 0) + close(fd_ep); +} + +static int spawn_wait(struct udev_event *event, const char *cmd, pid_t pid) +{ + struct udev *udev = event->udev; + struct pollfd pfd[1]; + int err = 0; + + pfd[0].events = POLLIN; + pfd[0].fd = event->fd_signal; + + while (pid > 0) { + int timeout; + int fdcount; + + if (event->timeout_usec > 0) { + unsigned long long age_usec; + + age_usec = now_usec() - event->birth_usec; + if (age_usec >= event->timeout_usec) + timeout = 1000; + else + timeout = ((event->timeout_usec - age_usec) / 1000) + 1000; + } else { + timeout = -1; + } + + fdcount = poll(pfd, 1, timeout); + if (fdcount < 0) { + if (errno == EINTR) + continue; + err = -errno; + err(udev, "failed to poll: %m\n"); + goto out; + } + if (fdcount == 0) { + err(udev, "timeout: killing '%s' [%u]\n", cmd, pid); + kill(pid, SIGKILL); + } + + if (pfd[0].revents & POLLIN) { + struct signalfd_siginfo fdsi; + int status; + ssize_t size; + + size = read(event->fd_signal, &fdsi, sizeof(struct signalfd_siginfo)); + if (size != sizeof(struct signalfd_siginfo)) + continue; + + switch (fdsi.ssi_signo) { + case SIGTERM: + event->sigterm = true; + break; + case SIGCHLD: + if (waitpid(pid, &status, WNOHANG) < 0) + break; + if (WIFEXITED(status)) { + info(udev, "'%s' [%u] exit with return code %i\n", cmd, pid, WEXITSTATUS(status)); + if (WEXITSTATUS(status) != 0) + err = -1; + } else if (WIFSIGNALED(status)) { + err(udev, "'%s' [%u] terminated by signal %i (%s)\n", cmd, pid, WTERMSIG(status), strsignal(WTERMSIG(status))); + err = -1; + } else if (WIFSTOPPED(status)) { + err(udev, "'%s' [%u] stopped\n", cmd, pid); + err = -1; + } else if (WIFCONTINUED(status)) { + err(udev, "'%s' [%u] continued\n", cmd, pid); + err = -1; + } else { + err(udev, "'%s' [%u] exit with status 0x%04x\n", cmd, pid, status); + err = -1; + } + pid = 0; + break; + } + } + } +out: + return err; +} + +int udev_build_argv(struct udev *udev, char *cmd, int *argc, char *argv[]) +{ + int i = 0; + char *pos; + + if (strchr(cmd, ' ') == NULL) { + argv[i++] = cmd; + goto out; + } + + pos = cmd; + while (pos != NULL && pos[0] != '\0') { + if (pos[0] == '\'') { + /* do not separate quotes */ + pos++; + argv[i] = strsep(&pos, "\'"); + if (pos != NULL) + while (pos[0] == ' ') + pos++; + } else { + argv[i] = strsep(&pos, " "); + if (pos != NULL) + while (pos[0] == ' ') + pos++; + } + dbg(udev, "argv[%i] '%s'\n", i, argv[i]); + i++; + } +out: + argv[i] = NULL; + if (argc) + *argc = i; + return 0; +} + +int udev_event_spawn(struct udev_event *event, + const char *cmd, char **envp, const sigset_t *sigmask, + char *result, size_t ressize) +{ + struct udev *udev = event->udev; + int outpipe[2] = {-1, -1}; + int errpipe[2] = {-1, -1}; + pid_t pid; + char arg[UTIL_PATH_SIZE]; + char *argv[128]; + char program[UTIL_PATH_SIZE]; + int err = 0; + + util_strscpy(arg, sizeof(arg), cmd); + udev_build_argv(event->udev, arg, NULL, argv); + + /* pipes from child to parent */ + if (result != NULL || udev_get_log_priority(udev) >= LOG_INFO) { + if (pipe2(outpipe, O_NONBLOCK) != 0) { + err = -errno; + err(udev, "pipe failed: %m\n"); + goto out; + } + } + if (udev_get_log_priority(udev) >= LOG_INFO) { + if (pipe2(errpipe, O_NONBLOCK) != 0) { + err = -errno; + err(udev, "pipe failed: %m\n"); + goto out; + } + } + + /* allow programs in /usr/lib/udev/ to be called without the path */ + if (argv[0][0] != '/') { + util_strscpyl(program, sizeof(program), UDEVLIBEXECDIR "/", argv[0], NULL); + argv[0] = program; + } + + pid = fork(); + switch(pid) { + case 0: + /* child closes parent's ends of pipes */ + if (outpipe[READ_END] >= 0) { + close(outpipe[READ_END]); + outpipe[READ_END] = -1; + } + if (errpipe[READ_END] >= 0) { + close(errpipe[READ_END]); + errpipe[READ_END] = -1; + } + + info(udev, "starting '%s'\n", cmd); + + err = spawn_exec(event, cmd, argv, envp, sigmask, + outpipe[WRITE_END], errpipe[WRITE_END]); + + _exit(2 ); + case -1: + err(udev, "fork of '%s' failed: %m\n", cmd); + err = -1; + goto out; + default: + /* parent closed child's ends of pipes */ + if (outpipe[WRITE_END] >= 0) { + close(outpipe[WRITE_END]); + outpipe[WRITE_END] = -1; + } + if (errpipe[WRITE_END] >= 0) { + close(errpipe[WRITE_END]); + errpipe[WRITE_END] = -1; + } + + spawn_read(event, cmd, + outpipe[READ_END], errpipe[READ_END], + result, ressize); + + err = spawn_wait(event, cmd, pid); + } + +out: + if (outpipe[READ_END] >= 0) + close(outpipe[READ_END]); + if (outpipe[WRITE_END] >= 0) + close(outpipe[WRITE_END]); + if (errpipe[READ_END] >= 0) + close(errpipe[READ_END]); + if (errpipe[WRITE_END] >= 0) + close(errpipe[WRITE_END]); + return err; +} + +static void rename_netif_kernel_log(struct ifreq ifr) +{ + int klog; + FILE *f; + + klog = open("/dev/kmsg", O_WRONLY); + if (klog < 0) + return; + + f = fdopen(klog, "w"); + if (f == NULL) { + close(klog); + return; + } + + fprintf(f, "<30>udevd[%u]: renamed network interface %s to %s\n", + getpid(), ifr.ifr_name, ifr.ifr_newname); + fclose(f); +} + +static int rename_netif(struct udev_event *event) +{ + struct udev_device *dev = event->dev; + int sk; + struct ifreq ifr; + int loop; + int err; + + info(event->udev, "changing net interface name from '%s' to '%s'\n", + udev_device_get_sysname(dev), event->name); + + sk = socket(PF_INET, SOCK_DGRAM, 0); + if (sk < 0) { + err = -errno; + err(event->udev, "error opening socket: %m\n"); + return err; + } + + memset(&ifr, 0x00, sizeof(struct ifreq)); + util_strscpy(ifr.ifr_name, IFNAMSIZ, udev_device_get_sysname(dev)); + util_strscpy(ifr.ifr_newname, IFNAMSIZ, event->name); + err = ioctl(sk, SIOCSIFNAME, &ifr); + if (err == 0) { + rename_netif_kernel_log(ifr); + goto out; + } + + /* keep trying if the destination interface name already exists */ + err = -errno; + if (err != -EEXIST) + goto out; + + /* free our own name, another process may wait for us */ + snprintf(ifr.ifr_newname, IFNAMSIZ, "rename%u", udev_device_get_ifindex(dev)); + err = ioctl(sk, SIOCSIFNAME, &ifr); + if (err < 0) { + err = -errno; + goto out; + } + + /* log temporary name */ + rename_netif_kernel_log(ifr); + + /* wait a maximum of 90 seconds for our target to become available */ + util_strscpy(ifr.ifr_name, IFNAMSIZ, ifr.ifr_newname); + util_strscpy(ifr.ifr_newname, IFNAMSIZ, event->name); + loop = 90 * 20; + while (loop--) { + const struct timespec duration = { 0, 1000 * 1000 * 1000 / 20 }; + + dbg(event->udev, "wait for netif '%s' to become free, loop=%i\n", + event->name, (90 * 20) - loop); + nanosleep(&duration, NULL); + + err = ioctl(sk, SIOCSIFNAME, &ifr); + if (err == 0) { + rename_netif_kernel_log(ifr); + break; + } + err = -errno; + if (err != -EEXIST) + break; + } + +out: + if (err < 0) + err(event->udev, "error changing net interface name %s to %s: %m\n", ifr.ifr_name, ifr.ifr_newname); + close(sk); + return err; +} + +int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules, const sigset_t *sigmask) +{ + struct udev_device *dev = event->dev; + int err = 0; + + if (udev_device_get_subsystem(dev) == NULL) + return -1; + + if (strcmp(udev_device_get_action(dev), "remove") == 0) { + udev_device_read_db(dev, NULL); + udev_device_delete_db(dev); + udev_device_tag_index(dev, NULL, false); + + if (major(udev_device_get_devnum(dev)) != 0) + udev_watch_end(event->udev, dev); + + udev_rules_apply_to_event(rules, event, sigmask); + + if (major(udev_device_get_devnum(dev)) != 0) + udev_node_remove(dev); + } else { + event->dev_db = udev_device_new_from_syspath(event->udev, udev_device_get_syspath(dev)); + if (event->dev_db != NULL) { + udev_device_read_db(event->dev_db, NULL); + udev_device_set_info_loaded(event->dev_db); + + /* disable watch during event processing */ + if (major(udev_device_get_devnum(dev)) != 0) + udev_watch_end(event->udev, event->dev_db); + } + + udev_rules_apply_to_event(rules, event, sigmask); + + /* rename a new network interface, if needed */ + if (udev_device_get_ifindex(dev) > 0 && strcmp(udev_device_get_action(dev), "add") == 0 && + event->name != NULL && strcmp(event->name, udev_device_get_sysname(dev)) != 0) { + char syspath[UTIL_PATH_SIZE]; + char *pos; + + err = rename_netif(event); + if (err == 0) { + info(event->udev, "renamed netif to '%s'\n", event->name); + + /* remember old name */ + udev_device_add_property(dev, "INTERFACE_OLD", udev_device_get_sysname(dev)); + + /* now change the devpath, because the kernel device name has changed */ + util_strscpy(syspath, sizeof(syspath), udev_device_get_syspath(dev)); + pos = strrchr(syspath, '/'); + if (pos != NULL) { + pos++; + util_strscpy(pos, sizeof(syspath) - (pos - syspath), event->name); + udev_device_set_syspath(event->dev, syspath); + udev_device_add_property(dev, "INTERFACE", udev_device_get_sysname(dev)); + info(event->udev, "changed devpath to '%s'\n", udev_device_get_devpath(dev)); + } + } + } + + if (major(udev_device_get_devnum(dev)) > 0) { + /* remove/update possible left-over symlinks from old database entry */ + if (event->dev_db != NULL) + udev_node_update_old_links(dev, event->dev_db); + + if (!event->mode_set) { + if (udev_device_get_devnode_mode(dev) > 0) { + /* kernel supplied value */ + event->mode = udev_device_get_devnode_mode(dev); + } else if (event->gid > 0) { + /* default 0660 if a group is assigned */ + event->mode = 0660; + } else { + /* default 0600 */ + event->mode = 0600; + } + } + + udev_node_add(dev, event->mode, event->uid, event->gid); + } + + /* preserve old, or get new initialization timestamp */ + if (event->dev_db != NULL && udev_device_get_usec_initialized(event->dev_db) > 0) + udev_device_set_usec_initialized(event->dev, udev_device_get_usec_initialized(event->dev_db)); + else if (udev_device_get_usec_initialized(event->dev) == 0) + udev_device_set_usec_initialized(event->dev, now_usec()); + + /* (re)write database file */ + udev_device_update_db(dev); + udev_device_tag_index(dev, event->dev_db, true); + udev_device_set_is_initialized(dev); + + udev_device_unref(event->dev_db); + event->dev_db = NULL; + } +out: + return err; +} + +int udev_event_execute_run(struct udev_event *event, const sigset_t *sigmask) +{ + struct udev_list_entry *list_entry; + int err = 0; + + dbg(event->udev, "executing run list\n"); + udev_list_entry_foreach(list_entry, udev_list_get_entry(&event->run_list)) { + const char *cmd = udev_list_entry_get_name(list_entry); + + if (strncmp(cmd, "socket:", strlen("socket:")) == 0) { + struct udev_monitor *monitor; + + monitor = udev_monitor_new_from_socket(event->udev, &cmd[strlen("socket:")]); + if (monitor == NULL) + continue; + udev_monitor_send_device(monitor, NULL, event->dev); + udev_monitor_unref(monitor); + } else { + char program[UTIL_PATH_SIZE]; + char **envp; + + if (event->exec_delay > 0) { + info(event->udev, "delay execution of '%s'\n", program); + sleep(event->exec_delay); + } + + udev_event_apply_format(event, cmd, program, sizeof(program)); + envp = udev_device_get_properties_envp(event->dev); + if (udev_event_spawn(event, program, envp, sigmask, NULL, 0) < 0) { + if (udev_list_entry_get_num(list_entry)) + err = -1; + } + } + } + return err; +} diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c new file mode 100644 index 0000000000..7a01a479ee --- /dev/null +++ b/src/udev/udev-node.c @@ -0,0 +1,379 @@ +/* + * Copyright (C) 2003-2010 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +#define TMP_FILE_EXT ".udev-tmp" + +static int node_symlink(struct udev *udev, const char *node, const char *slink) +{ + struct stat stats; + char target[UTIL_PATH_SIZE]; + char *s; + size_t l; + char slink_tmp[UTIL_PATH_SIZE + sizeof(TMP_FILE_EXT)]; + int i = 0; + int tail = 0; + int err = 0; + + /* use relative link */ + target[0] = '\0'; + while (node[i] && (node[i] == slink[i])) { + if (node[i] == '/') + tail = i+1; + i++; + } + s = target; + l = sizeof(target); + while (slink[i] != '\0') { + if (slink[i] == '/') + l = util_strpcpy(&s, l, "../"); + i++; + } + l = util_strscpy(s, l, &node[tail]); + if (l == 0) { + err = -EINVAL; + goto exit; + } + + /* preserve link with correct target, do not replace node of other device */ + if (lstat(slink, &stats) == 0) { + if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) { + struct stat stats2; + + info(udev, "found existing node instead of symlink '%s'\n", slink); + if (lstat(node, &stats2) == 0) { + if ((stats.st_mode & S_IFMT) == (stats2.st_mode & S_IFMT) && + stats.st_rdev == stats2.st_rdev && stats.st_ino != stats2.st_ino) { + info(udev, "replace device node '%s' with symlink to our node '%s'\n", + slink, node); + } else { + err(udev, "device node '%s' already exists, " + "link to '%s' will not overwrite it\n", + slink, node); + goto exit; + } + } + } else if (S_ISLNK(stats.st_mode)) { + char buf[UTIL_PATH_SIZE]; + int len; + + dbg(udev, "found existing symlink '%s'\n", slink); + len = readlink(slink, buf, sizeof(buf)); + if (len > 0 && len < (int)sizeof(buf)) { + buf[len] = '\0'; + if (strcmp(target, buf) == 0) { + info(udev, "preserve already existing symlink '%s' to '%s'\n", + slink, target); + udev_selinux_lsetfilecon(udev, slink, S_IFLNK); + utimensat(AT_FDCWD, slink, NULL, AT_SYMLINK_NOFOLLOW); + goto exit; + } + } + } + } else { + info(udev, "creating symlink '%s' to '%s'\n", slink, target); + do { + err = util_create_path_selinux(udev, slink); + if (err != 0 && err != -ENOENT) + break; + udev_selinux_setfscreatecon(udev, slink, S_IFLNK); + err = symlink(target, slink); + if (err != 0) + err = -errno; + udev_selinux_resetfscreatecon(udev); + } while (err == -ENOENT); + if (err == 0) + goto exit; + } + + info(udev, "atomically replace '%s'\n", slink); + util_strscpyl(slink_tmp, sizeof(slink_tmp), slink, TMP_FILE_EXT, NULL); + unlink(slink_tmp); + do { + err = util_create_path_selinux(udev, slink_tmp); + if (err != 0 && err != -ENOENT) + break; + udev_selinux_setfscreatecon(udev, slink_tmp, S_IFLNK); + err = symlink(target, slink_tmp); + if (err != 0) + err = -errno; + udev_selinux_resetfscreatecon(udev); + } while (err == -ENOENT); + if (err != 0) { + err(udev, "symlink '%s' '%s' failed: %m\n", target, slink_tmp); + goto exit; + } + err = rename(slink_tmp, slink); + if (err != 0) { + err(udev, "rename '%s' '%s' failed: %m\n", slink_tmp, slink); + unlink(slink_tmp); + } +exit: + return err; +} + +/* find device node of device with highest priority */ +static const char *link_find_prioritized(struct udev_device *dev, bool add, const char *stackdir, char *buf, size_t bufsize) +{ + struct udev *udev = udev_device_get_udev(dev); + DIR *dir; + int priority = 0; + const char *target = NULL; + + if (add) { + priority = udev_device_get_devlink_priority(dev); + util_strscpy(buf, bufsize, udev_device_get_devnode(dev)); + target = buf; + } + + dir = opendir(stackdir); + if (dir == NULL) + return target; + for (;;) { + struct udev_device *dev_db; + struct dirent *dent; + + dent = readdir(dir); + if (dent == NULL || dent->d_name[0] == '\0') + break; + if (dent->d_name[0] == '.') + continue; + + info(udev, "found '%s' claiming '%s'\n", dent->d_name, stackdir); + + /* did we find ourself? */ + if (strcmp(dent->d_name, udev_device_get_id_filename(dev)) == 0) + continue; + + dev_db = udev_device_new_from_id_filename(udev, dent->d_name); + if (dev_db != NULL) { + const char *devnode; + + devnode = udev_device_get_devnode(dev_db); + if (devnode != NULL) { + dbg(udev, "compare priority of '%s'(%i) > '%s'(%i)\n", target, priority, + udev_device_get_devnode(dev_db), udev_device_get_devlink_priority(dev_db)); + if (target == NULL || udev_device_get_devlink_priority(dev_db) > priority) { + info(udev, "'%s' claims priority %i for '%s'\n", + udev_device_get_syspath(dev_db), udev_device_get_devlink_priority(dev_db), stackdir); + priority = udev_device_get_devlink_priority(dev_db); + util_strscpy(buf, bufsize, devnode); + target = buf; + } + } + udev_device_unref(dev_db); + } + } + closedir(dir); + return target; +} + +/* manage "stack of names" with possibly specified device priorities */ +static void link_update(struct udev_device *dev, const char *slink, bool add) +{ + struct udev *udev = udev_device_get_udev(dev); + char name_enc[UTIL_PATH_SIZE]; + char filename[UTIL_PATH_SIZE * 2]; + char dirname[UTIL_PATH_SIZE]; + const char *target; + char buf[UTIL_PATH_SIZE]; + + dbg(udev, "update symlink '%s' of '%s'\n", slink, udev_device_get_syspath(dev)); + + util_path_encode(&slink[strlen(udev_get_dev_path(udev))+1], name_enc, sizeof(name_enc)); + util_strscpyl(dirname, sizeof(dirname), udev_get_run_path(udev), "/links/", name_enc, NULL); + util_strscpyl(filename, sizeof(filename), dirname, "/", udev_device_get_id_filename(dev), NULL); + + if (!add) { + dbg(udev, "removing index: '%s'\n", filename); + if (unlink(filename) == 0) + rmdir(dirname); + } + + target = link_find_prioritized(dev, add, dirname, buf, sizeof(buf)); + if (target == NULL) { + info(udev, "no reference left, remove '%s'\n", slink); + if (unlink(slink) == 0) + util_delete_path(udev, slink); + } else { + info(udev, "creating link '%s' to '%s'\n", slink, target); + node_symlink(udev, target, slink); + } + + if (add) { + int err; + + dbg(udev, "creating index: '%s'\n", filename); + do { + int fd; + + err = util_create_path(udev, filename); + if (err != 0 && err != -ENOENT) + break; + fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444); + if (fd >= 0) + close(fd); + else + err = -errno; + } while (err == -ENOENT); + } +} + +void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old) +{ + struct udev *udev = udev_device_get_udev(dev); + struct udev_list_entry *list_entry; + + /* update possible left-over symlinks */ + udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev_old)) { + const char *name = udev_list_entry_get_name(list_entry); + struct udev_list_entry *list_entry_current; + int found; + + /* check if old link name still belongs to this device */ + found = 0; + udev_list_entry_foreach(list_entry_current, udev_device_get_devlinks_list_entry(dev)) { + const char *name_current = udev_list_entry_get_name(list_entry_current); + + if (strcmp(name, name_current) == 0) { + found = 1; + break; + } + } + if (found) + continue; + + info(udev, "update old name, '%s' no longer belonging to '%s'\n", + name, udev_device_get_devpath(dev)); + link_update(dev, name, 0); + } +} + +static int node_fixup(struct udev_device *dev, mode_t mode, uid_t uid, gid_t gid) +{ + struct udev *udev = udev_device_get_udev(dev); + const char *devnode = udev_device_get_devnode(dev); + dev_t devnum = udev_device_get_devnum(dev); + struct stat stats; + int err = 0; + + if (strcmp(udev_device_get_subsystem(dev), "block") == 0) + mode |= S_IFBLK; + else + mode |= S_IFCHR; + + if (lstat(devnode, &stats) != 0) { + err = -errno; + info(udev, "can not stat() node '%s' (%m)\n", devnode); + goto out; + } + + if (((stats.st_mode & S_IFMT) != (mode & S_IFMT)) || (stats.st_rdev != devnum)) { + err = -EEXIST; + info(udev, "found node '%s' with non-matching devnum %s, skip handling\n", + udev_device_get_devnode(dev), udev_device_get_id_filename(dev)); + goto out; + } + + if ((stats.st_mode & 0777) != (mode & 0777) || stats.st_uid != uid || stats.st_gid != gid) { + info(udev, "set permissions %s, %#o, uid=%u, gid=%u\n", devnode, mode, uid, gid); + chmod(devnode, mode); + chown(devnode, uid, gid); + } else { + info(udev, "preserve permissions %s, %#o, uid=%u, gid=%u\n", devnode, mode, uid, gid); + } + + /* + * Set initial selinux file context only on add events. + * We set the proper context on bootup (triger) or for newly + * added devices, but we don't change it later, in case + * something else has set a custom context in the meantime. + */ + if (strcmp(udev_device_get_action(dev), "add") == 0) + udev_selinux_lsetfilecon(udev, devnode, mode); + + /* always update timestamp when we re-use the node, like on media change events */ + utimensat(AT_FDCWD, devnode, NULL, 0); +out: + return err; +} + +void udev_node_add(struct udev_device *dev, mode_t mode, uid_t uid, gid_t gid) +{ + struct udev *udev = udev_device_get_udev(dev); + char filename[UTIL_PATH_SIZE]; + struct udev_list_entry *list_entry; + int err = 0; + + info(udev, "handling device node '%s', devnum=%s, mode=%#o, uid=%d, gid=%d\n", + udev_device_get_devnode(dev), udev_device_get_id_filename(dev), mode, uid, gid); + + if (node_fixup(dev, mode, uid, gid) < 0) + return; + + /* always add /dev/{block,char}/$major:$minor */ + snprintf(filename, sizeof(filename), "%s/%s/%u:%u", + udev_get_dev_path(udev), + strcmp(udev_device_get_subsystem(dev), "block") == 0 ? "block" : "char", + major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev))); + node_symlink(udev, udev_device_get_devnode(dev), filename); + + /* create/update symlinks, add symlinks to name index */ + udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) { + if (udev_list_entry_get_num(list_entry)) + /* simple unmanaged link name */ + node_symlink(udev, udev_device_get_devnode(dev), udev_list_entry_get_name(list_entry)); + else + link_update(dev, udev_list_entry_get_name(list_entry), 1); + } +} + +void udev_node_remove(struct udev_device *dev) +{ + struct udev *udev = udev_device_get_udev(dev); + struct udev_list_entry *list_entry; + const char *devnode; + struct stat stats; + struct udev_device *dev_check; + char filename[UTIL_PATH_SIZE]; + + /* remove/update symlinks, remove symlinks from name index */ + udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) + link_update(dev, udev_list_entry_get_name(list_entry), 0); + + /* remove /dev/{block,char}/$major:$minor */ + snprintf(filename, sizeof(filename), "%s/%s/%u:%u", + udev_get_dev_path(udev), + strcmp(udev_device_get_subsystem(dev), "block") == 0 ? "block" : "char", + major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev))); + unlink(filename); +} diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c new file mode 100644 index 0000000000..8a85eae717 --- /dev/null +++ b/src/udev/udev-rules.c @@ -0,0 +1,2767 @@ +/* + * Copyright (C) 2003-2010 Kay Sievers + * Copyright (C) 2008 Alan Jenkins + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +#define PREALLOC_TOKEN 2048 +#define PREALLOC_STRBUF 32 * 1024 +#define PREALLOC_TRIE 256 + +struct uid_gid { + unsigned int name_off; + union { + uid_t uid; + gid_t gid; + }; +}; + +struct trie_node { + /* this node's first child */ + unsigned int child_idx; + /* the next child of our parent node's child list */ + unsigned int next_child_idx; + /* this node's last child (shortcut for append) */ + unsigned int last_child_idx; + unsigned int value_off; + unsigned short value_len; + unsigned char key; +}; + +struct udev_rules { + struct udev *udev; + int resolve_names; + + /* every key in the rules file becomes a token */ + struct token *tokens; + unsigned int token_cur; + unsigned int token_max; + + /* all key strings are copied to a single string buffer */ + char *buf; + size_t buf_cur; + size_t buf_max; + unsigned int buf_count; + + /* during rule parsing, strings are indexed to find duplicates */ + struct trie_node *trie_nodes; + unsigned int trie_nodes_cur; + unsigned int trie_nodes_max; + + /* during rule parsing, uid/gid lookup results are cached */ + struct uid_gid *uids; + unsigned int uids_cur; + unsigned int uids_max; + struct uid_gid *gids; + unsigned int gids_cur; + unsigned int gids_max; +}; + +/* KEY=="", KEY!="", KEY+="", KEY="", KEY:="" */ +enum operation_type { + OP_UNSET, + + OP_MATCH, + OP_NOMATCH, + OP_MATCH_MAX, + + OP_ADD, + OP_ASSIGN, + OP_ASSIGN_FINAL, +}; + +enum string_glob_type { + GL_UNSET, + GL_PLAIN, /* no special chars */ + GL_GLOB, /* shell globs ?,*,[] */ + GL_SPLIT, /* multi-value A|B */ + GL_SPLIT_GLOB, /* multi-value with glob A*|B* */ + GL_SOMETHING, /* commonly used "?*" */ +}; + +enum string_subst_type { + SB_UNSET, + SB_NONE, + SB_FORMAT, + SB_SUBSYS, +}; + +/* tokens of a rule are sorted/handled in this order */ +enum token_type { + TK_UNSET, + TK_RULE, + + TK_M_ACTION, /* val */ + TK_M_DEVPATH, /* val */ + TK_M_KERNEL, /* val */ + TK_M_DEVLINK, /* val */ + TK_M_NAME, /* val */ + TK_M_ENV, /* val, attr */ + TK_M_TAG, /* val */ + TK_M_SUBSYSTEM, /* val */ + TK_M_DRIVER, /* val */ + TK_M_WAITFOR, /* val */ + TK_M_ATTR, /* val, attr */ + + TK_M_PARENTS_MIN, + TK_M_KERNELS, /* val */ + TK_M_SUBSYSTEMS, /* val */ + TK_M_DRIVERS, /* val */ + TK_M_ATTRS, /* val, attr */ + TK_M_TAGS, /* val */ + TK_M_PARENTS_MAX, + + TK_M_TEST, /* val, mode_t */ + TK_M_EVENT_TIMEOUT, /* int */ + TK_M_PROGRAM, /* val */ + TK_M_IMPORT_FILE, /* val */ + TK_M_IMPORT_PROG, /* val */ + TK_M_IMPORT_BUILTIN, /* val */ + TK_M_IMPORT_DB, /* val */ + TK_M_IMPORT_CMDLINE, /* val */ + TK_M_IMPORT_PARENT, /* val */ + TK_M_RESULT, /* val */ + TK_M_MAX, + + TK_A_STRING_ESCAPE_NONE, + TK_A_STRING_ESCAPE_REPLACE, + TK_A_DB_PERSIST, + TK_A_INOTIFY_WATCH, /* int */ + TK_A_DEVLINK_PRIO, /* int */ + TK_A_OWNER, /* val */ + TK_A_GROUP, /* val */ + TK_A_MODE, /* val */ + TK_A_OWNER_ID, /* uid_t */ + TK_A_GROUP_ID, /* gid_t */ + TK_A_MODE_ID, /* mode_t */ + TK_A_STATIC_NODE, /* val */ + TK_A_ENV, /* val, attr */ + TK_A_TAG, /* val */ + TK_A_NAME, /* val */ + TK_A_DEVLINK, /* val */ + TK_A_ATTR, /* val, attr */ + TK_A_RUN, /* val, bool */ + TK_A_GOTO, /* size_t */ + + TK_END, +}; + +/* we try to pack stuff in a way that we take only 12 bytes per token */ +struct token { + union { + unsigned char type; /* same in rule and key */ + struct { + enum token_type type:8; + bool can_set_name:1; + bool has_static_node:1; + unsigned int unused:6; + unsigned short token_count; + unsigned int label_off; + unsigned short filename_off; + unsigned short filename_line; + } rule; + struct { + enum token_type type:8; + enum operation_type op:8; + enum string_glob_type glob:8; + enum string_subst_type subst:4; + enum string_subst_type attrsubst:4; + unsigned int value_off; + union { + unsigned int attr_off; + int devlink_unique; + unsigned int rule_goto; + mode_t mode; + uid_t uid; + gid_t gid; + int devlink_prio; + int event_timeout; + int watch; + enum udev_builtin_cmd builtin_cmd; + }; + } key; + }; +}; + +#define MAX_TK 64 +struct rule_tmp { + struct udev_rules *rules; + struct token rule; + struct token token[MAX_TK]; + unsigned int token_cur; +}; + +#ifdef ENABLE_DEBUG +static const char *operation_str(enum operation_type type) +{ + static const char *operation_strs[] = { + [OP_UNSET] = "UNSET", + [OP_MATCH] = "match", + [OP_NOMATCH] = "nomatch", + [OP_MATCH_MAX] = "MATCH_MAX", + + [OP_ADD] = "add", + [OP_ASSIGN] = "assign", + [OP_ASSIGN_FINAL] = "assign-final", +} ; + + return operation_strs[type]; +} + +static const char *string_glob_str(enum string_glob_type type) +{ + static const char *string_glob_strs[] = { + [GL_UNSET] = "UNSET", + [GL_PLAIN] = "plain", + [GL_GLOB] = "glob", + [GL_SPLIT] = "split", + [GL_SPLIT_GLOB] = "split-glob", + [GL_SOMETHING] = "split-glob", + }; + + return string_glob_strs[type]; +} + +static const char *token_str(enum token_type type) +{ + static const char *token_strs[] = { + [TK_UNSET] = "UNSET", + [TK_RULE] = "RULE", + + [TK_M_ACTION] = "M ACTION", + [TK_M_DEVPATH] = "M DEVPATH", + [TK_M_KERNEL] = "M KERNEL", + [TK_M_DEVLINK] = "M DEVLINK", + [TK_M_NAME] = "M NAME", + [TK_M_ENV] = "M ENV", + [TK_M_TAG] = "M TAG", + [TK_M_SUBSYSTEM] = "M SUBSYSTEM", + [TK_M_DRIVER] = "M DRIVER", + [TK_M_WAITFOR] = "M WAITFOR", + [TK_M_ATTR] = "M ATTR", + + [TK_M_PARENTS_MIN] = "M PARENTS_MIN", + [TK_M_KERNELS] = "M KERNELS", + [TK_M_SUBSYSTEMS] = "M SUBSYSTEMS", + [TK_M_DRIVERS] = "M DRIVERS", + [TK_M_ATTRS] = "M ATTRS", + [TK_M_TAGS] = "M TAGS", + [TK_M_PARENTS_MAX] = "M PARENTS_MAX", + + [TK_M_TEST] = "M TEST", + [TK_M_EVENT_TIMEOUT] = "M EVENT_TIMEOUT", + [TK_M_PROGRAM] = "M PROGRAM", + [TK_M_IMPORT_FILE] = "M IMPORT_FILE", + [TK_M_IMPORT_PROG] = "M IMPORT_PROG", + [TK_M_IMPORT_BUILTIN] = "M IMPORT_BUILTIN", + [TK_M_IMPORT_DB] = "M IMPORT_DB", + [TK_M_IMPORT_CMDLINE] = "M IMPORT_CMDLINE", + [TK_M_IMPORT_PARENT] = "M IMPORT_PARENT", + [TK_M_RESULT] = "M RESULT", + [TK_M_MAX] = "M MAX", + + [TK_A_STRING_ESCAPE_NONE] = "A STRING_ESCAPE_NONE", + [TK_A_STRING_ESCAPE_REPLACE] = "A STRING_ESCAPE_REPLACE", + [TK_A_DB_PERSIST] = "A DB_PERSIST", + [TK_A_INOTIFY_WATCH] = "A INOTIFY_WATCH", + [TK_A_DEVLINK_PRIO] = "A DEVLINK_PRIO", + [TK_A_OWNER] = "A OWNER", + [TK_A_GROUP] = "A GROUP", + [TK_A_MODE] = "A MODE", + [TK_A_OWNER_ID] = "A OWNER_ID", + [TK_A_GROUP_ID] = "A GROUP_ID", + [TK_A_STATIC_NODE] = "A STATIC_NODE", + [TK_A_MODE_ID] = "A MODE_ID", + [TK_A_ENV] = "A ENV", + [TK_A_TAG] = "A ENV", + [TK_A_NAME] = "A NAME", + [TK_A_DEVLINK] = "A DEVLINK", + [TK_A_ATTR] = "A ATTR", + [TK_A_RUN] = "A RUN", + [TK_A_GOTO] = "A GOTO", + + [TK_END] = "END", + }; + + return token_strs[type]; +} + +static void dump_token(struct udev_rules *rules, struct token *token) +{ + enum token_type type = token->type; + enum operation_type op = token->key.op; + enum string_glob_type glob = token->key.glob; + const char *value = &rules->buf[token->key.value_off]; + const char *attr = &rules->buf[token->key.attr_off]; + + switch (type) { + case TK_RULE: + { + const char *tks_ptr = (char *)rules->tokens; + const char *tk_ptr = (char *)token; + unsigned int idx = (tk_ptr - tks_ptr) / sizeof(struct token); + + dbg(rules->udev, "* RULE %s:%u, token: %u, count: %u, label: '%s'\n", + &rules->buf[token->rule.filename_off], token->rule.filename_line, + idx, token->rule.token_count, + &rules->buf[token->rule.label_off]); + break; + } + case TK_M_ACTION: + case TK_M_DEVPATH: + case TK_M_KERNEL: + case TK_M_SUBSYSTEM: + case TK_M_DRIVER: + case TK_M_WAITFOR: + case TK_M_DEVLINK: + case TK_M_NAME: + case TK_M_KERNELS: + case TK_M_SUBSYSTEMS: + case TK_M_DRIVERS: + case TK_M_TAGS: + case TK_M_PROGRAM: + case TK_M_IMPORT_FILE: + case TK_M_IMPORT_PROG: + case TK_M_IMPORT_DB: + case TK_M_IMPORT_CMDLINE: + case TK_M_IMPORT_PARENT: + case TK_M_RESULT: + case TK_A_NAME: + case TK_A_DEVLINK: + case TK_A_OWNER: + case TK_A_GROUP: + case TK_A_MODE: + case TK_A_RUN: + dbg(rules->udev, "%s %s '%s'(%s)\n", + token_str(type), operation_str(op), value, string_glob_str(glob)); + break; + case TK_M_IMPORT_BUILTIN: + dbg(rules->udev, "%s %i '%s'\n", token_str(type), token->key.builtin_cmd, value); + break; + case TK_M_ATTR: + case TK_M_ATTRS: + case TK_M_ENV: + case TK_A_ATTR: + case TK_A_ENV: + dbg(rules->udev, "%s %s '%s' '%s'(%s)\n", + token_str(type), operation_str(op), attr, value, string_glob_str(glob)); + break; + case TK_M_TAG: + case TK_A_TAG: + dbg(rules->udev, "%s %s '%s'\n", token_str(type), operation_str(op), value); + break; + case TK_A_STRING_ESCAPE_NONE: + case TK_A_STRING_ESCAPE_REPLACE: + case TK_A_DB_PERSIST: + dbg(rules->udev, "%s\n", token_str(type)); + break; + case TK_M_TEST: + dbg(rules->udev, "%s %s '%s'(%s) %#o\n", + token_str(type), operation_str(op), value, string_glob_str(glob), token->key.mode); + break; + case TK_A_INOTIFY_WATCH: + dbg(rules->udev, "%s %u\n", token_str(type), token->key.watch); + break; + case TK_A_DEVLINK_PRIO: + dbg(rules->udev, "%s %u\n", token_str(type), token->key.devlink_prio); + break; + case TK_A_OWNER_ID: + dbg(rules->udev, "%s %s %u\n", token_str(type), operation_str(op), token->key.uid); + break; + case TK_A_GROUP_ID: + dbg(rules->udev, "%s %s %u\n", token_str(type), operation_str(op), token->key.gid); + break; + case TK_A_MODE_ID: + dbg(rules->udev, "%s %s %#o\n", token_str(type), operation_str(op), token->key.mode); + break; + case TK_A_STATIC_NODE: + dbg(rules->udev, "%s '%s'\n", token_str(type), value); + break; + case TK_M_EVENT_TIMEOUT: + dbg(rules->udev, "%s %u\n", token_str(type), token->key.event_timeout); + break; + case TK_A_GOTO: + dbg(rules->udev, "%s '%s' %u\n", token_str(type), value, token->key.rule_goto); + break; + case TK_END: + dbg(rules->udev, "* %s\n", token_str(type)); + break; + case TK_M_PARENTS_MIN: + case TK_M_PARENTS_MAX: + case TK_M_MAX: + case TK_UNSET: + dbg(rules->udev, "unknown type %u\n", type); + break; + } +} + +static void dump_rules(struct udev_rules *rules) +{ + unsigned int i; + + dbg(rules->udev, "dumping %u (%zu bytes) tokens, %u (%zu bytes) strings\n", + rules->token_cur, + rules->token_cur * sizeof(struct token), + rules->buf_count, + rules->buf_cur); + for(i = 0; i < rules->token_cur; i++) + dump_token(rules, &rules->tokens[i]); +} +#else +static inline const char *operation_str(enum operation_type type) { return NULL; } +static inline const char *token_str(enum token_type type) { return NULL; } +static inline void dump_token(struct udev_rules *rules, struct token *token) {} +static inline void dump_rules(struct udev_rules *rules) {} +#endif /* ENABLE_DEBUG */ + +static int add_new_string(struct udev_rules *rules, const char *str, size_t bytes) +{ + int off; + + /* grow buffer if needed */ + if (rules->buf_cur + bytes+1 >= rules->buf_max) { + char *buf; + unsigned int add; + + /* double the buffer size */ + add = rules->buf_max; + if (add < bytes * 8) + add = bytes * 8; + + buf = realloc(rules->buf, rules->buf_max + add); + if (buf == NULL) + return -1; + dbg(rules->udev, "extend buffer from %zu to %zu\n", rules->buf_max, rules->buf_max + add); + rules->buf = buf; + rules->buf_max += add; + } + off = rules->buf_cur; + memcpy(&rules->buf[rules->buf_cur], str, bytes); + rules->buf_cur += bytes; + rules->buf_count++; + return off; +} + +static int add_string(struct udev_rules *rules, const char *str) +{ + unsigned int node_idx; + struct trie_node *new_node; + unsigned int new_node_idx; + unsigned char key; + unsigned short len; + unsigned int depth; + unsigned int off; + struct trie_node *parent; + + /* walk trie, start from last character of str to find matching tails */ + len = strlen(str); + key = str[len-1]; + node_idx = 0; + for (depth = 0; depth <= len; depth++) { + struct trie_node *node; + unsigned int child_idx; + + node = &rules->trie_nodes[node_idx]; + off = node->value_off + node->value_len - len; + + /* match against current node */ + if (depth == len || (node->value_len >= len && memcmp(&rules->buf[off], str, len) == 0)) + return off; + + /* lookup child node */ + key = str[len - 1 - depth]; + child_idx = node->child_idx; + while (child_idx > 0) { + struct trie_node *child; + + child = &rules->trie_nodes[child_idx]; + if (child->key == key) + break; + child_idx = child->next_child_idx; + } + if (child_idx == 0) + break; + node_idx = child_idx; + } + + /* string not found, add it */ + off = add_new_string(rules, str, len + 1); + + /* grow trie nodes if needed */ + if (rules->trie_nodes_cur >= rules->trie_nodes_max) { + struct trie_node *nodes; + unsigned int add; + + /* double the buffer size */ + add = rules->trie_nodes_max; + if (add < 8) + add = 8; + + nodes = realloc(rules->trie_nodes, (rules->trie_nodes_max + add) * sizeof(struct trie_node)); + if (nodes == NULL) + return -1; + dbg(rules->udev, "extend trie nodes from %u to %u\n", + rules->trie_nodes_max, rules->trie_nodes_max + add); + rules->trie_nodes = nodes; + rules->trie_nodes_max += add; + } + + /* get a new node */ + new_node_idx = rules->trie_nodes_cur; + rules->trie_nodes_cur++; + new_node = &rules->trie_nodes[new_node_idx]; + memset(new_node, 0x00, sizeof(struct trie_node)); + new_node->value_off = off; + new_node->value_len = len; + new_node->key = key; + + /* join the parent's child list */ + parent = &rules->trie_nodes[node_idx]; + if (parent->child_idx == 0) { + parent->child_idx = new_node_idx; + } else { + struct trie_node *last_child; + + last_child = &rules->trie_nodes[parent->last_child_idx]; + last_child->next_child_idx = new_node_idx; + } + parent->last_child_idx = new_node_idx; + return off; +} + +static int add_token(struct udev_rules *rules, struct token *token) +{ + /* grow buffer if needed */ + if (rules->token_cur+1 >= rules->token_max) { + struct token *tokens; + unsigned int add; + + /* double the buffer size */ + add = rules->token_max; + if (add < 8) + add = 8; + + tokens = realloc(rules->tokens, (rules->token_max + add ) * sizeof(struct token)); + if (tokens == NULL) + return -1; + dbg(rules->udev, "extend tokens from %u to %u\n", rules->token_max, rules->token_max + add); + rules->tokens = tokens; + rules->token_max += add; + } + memcpy(&rules->tokens[rules->token_cur], token, sizeof(struct token)); + rules->token_cur++; + return 0; +} + +static uid_t add_uid(struct udev_rules *rules, const char *owner) +{ + unsigned int i; + uid_t uid; + unsigned int off; + + /* lookup, if we know it already */ + for (i = 0; i < rules->uids_cur; i++) { + off = rules->uids[i].name_off; + if (strcmp(&rules->buf[off], owner) == 0) { + uid = rules->uids[i].uid; + dbg(rules->udev, "return existing %u for '%s'\n", uid, owner); + return uid; + } + } + uid = util_lookup_user(rules->udev, owner); + + /* grow buffer if needed */ + if (rules->uids_cur+1 >= rules->uids_max) { + struct uid_gid *uids; + unsigned int add; + + /* double the buffer size */ + add = rules->uids_max; + if (add < 1) + add = 8; + + uids = realloc(rules->uids, (rules->uids_max + add ) * sizeof(struct uid_gid)); + if (uids == NULL) + return uid; + dbg(rules->udev, "extend uids from %u to %u\n", rules->uids_max, rules->uids_max + add); + rules->uids = uids; + rules->uids_max += add; + } + rules->uids[rules->uids_cur].uid = uid; + off = add_string(rules, owner); + if (off <= 0) + return uid; + rules->uids[rules->uids_cur].name_off = off; + rules->uids_cur++; + return uid; +} + +static gid_t add_gid(struct udev_rules *rules, const char *group) +{ + unsigned int i; + gid_t gid; + unsigned int off; + + /* lookup, if we know it already */ + for (i = 0; i < rules->gids_cur; i++) { + off = rules->gids[i].name_off; + if (strcmp(&rules->buf[off], group) == 0) { + gid = rules->gids[i].gid; + dbg(rules->udev, "return existing %u for '%s'\n", gid, group); + return gid; + } + } + gid = util_lookup_group(rules->udev, group); + + /* grow buffer if needed */ + if (rules->gids_cur+1 >= rules->gids_max) { + struct uid_gid *gids; + unsigned int add; + + /* double the buffer size */ + add = rules->gids_max; + if (add < 1) + add = 8; + + gids = realloc(rules->gids, (rules->gids_max + add ) * sizeof(struct uid_gid)); + if (gids == NULL) + return gid; + dbg(rules->udev, "extend gids from %u to %u\n", rules->gids_max, rules->gids_max + add); + rules->gids = gids; + rules->gids_max += add; + } + rules->gids[rules->gids_cur].gid = gid; + off = add_string(rules, group); + if (off <= 0) + return gid; + rules->gids[rules->gids_cur].name_off = off; + rules->gids_cur++; + return gid; +} + +static int import_property_from_string(struct udev_device *dev, char *line) +{ + struct udev *udev = udev_device_get_udev(dev); + char *key; + char *val; + size_t len; + + /* find key */ + key = line; + while (isspace(key[0])) + key++; + + /* comment or empty line */ + if (key[0] == '#' || key[0] == '\0') + return -1; + + /* split key/value */ + val = strchr(key, '='); + if (val == NULL) + return -1; + val[0] = '\0'; + val++; + + /* find value */ + while (isspace(val[0])) + val++; + + /* terminate key */ + len = strlen(key); + if (len == 0) + return -1; + while (isspace(key[len-1])) + len--; + key[len] = '\0'; + + /* terminate value */ + len = strlen(val); + if (len == 0) + return -1; + while (isspace(val[len-1])) + len--; + val[len] = '\0'; + + if (len == 0) + return -1; + + /* unquote */ + if (val[0] == '"' || val[0] == '\'') { + if (val[len-1] != val[0]) { + info(udev, "inconsistent quoting: '%s', skip\n", line); + return -1; + } + val[len-1] = '\0'; + val++; + } + + dbg(udev, "adding '%s'='%s'\n", key, val); + + /* handle device, renamed by external tool, returning new path */ + if (strcmp(key, "DEVPATH") == 0) { + char syspath[UTIL_PATH_SIZE]; + + info(udev, "updating devpath from '%s' to '%s'\n", + udev_device_get_devpath(dev), val); + util_strscpyl(syspath, sizeof(syspath), udev_get_sys_path(udev), val, NULL); + udev_device_set_syspath(dev, syspath); + } else { + struct udev_list_entry *entry; + + entry = udev_device_add_property(dev, key, val); + /* store in db, skip private keys */ + if (key[0] != '.') + udev_list_entry_set_num(entry, true); + } + return 0; +} + +static int import_file_into_properties(struct udev_device *dev, const char *filename) +{ + FILE *f; + char line[UTIL_LINE_SIZE]; + + f = fopen(filename, "r"); + if (f == NULL) + return -1; + while (fgets(line, sizeof(line), f) != NULL) + import_property_from_string(dev, line); + fclose(f); + return 0; +} + +static int import_program_into_properties(struct udev_event *event, const char *program, const sigset_t *sigmask) +{ + struct udev_device *dev = event->dev; + char **envp; + char result[UTIL_LINE_SIZE]; + char *line; + int err; + + envp = udev_device_get_properties_envp(dev); + err = udev_event_spawn(event, program, envp, sigmask, result, sizeof(result)); + if (err < 0) + return err; + + line = result; + while (line != NULL) { + char *pos; + + pos = strchr(line, '\n'); + if (pos != NULL) { + pos[0] = '\0'; + pos = &pos[1]; + } + import_property_from_string(dev, line); + line = pos; + } + return 0; +} + +static int import_parent_into_properties(struct udev_device *dev, const char *filter) +{ + struct udev *udev = udev_device_get_udev(dev); + struct udev_device *dev_parent; + struct udev_list_entry *list_entry; + + dev_parent = udev_device_get_parent(dev); + if (dev_parent == NULL) + return -1; + + dbg(udev, "found parent '%s', get the node name\n", udev_device_get_syspath(dev_parent)); + udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(dev_parent)) { + const char *key = udev_list_entry_get_name(list_entry); + const char *val = udev_list_entry_get_value(list_entry); + + if (fnmatch(filter, key, 0) == 0) { + struct udev_list_entry *entry; + + dbg(udev, "import key '%s=%s'\n", key, val); + entry = udev_device_add_property(dev, key, val); + /* store in db, skip private keys */ + if (key[0] != '.') + udev_list_entry_set_num(entry, true); + } + } + return 0; +} + +#define WAIT_LOOP_PER_SECOND 50 +static int wait_for_file(struct udev_device *dev, const char *file, int timeout) +{ + struct udev *udev = udev_device_get_udev(dev); + char filepath[UTIL_PATH_SIZE]; + char devicepath[UTIL_PATH_SIZE]; + struct stat stats; + int loop = timeout * WAIT_LOOP_PER_SECOND; + + /* a relative path is a device attribute */ + devicepath[0] = '\0'; + if (file[0] != '/') { + util_strscpyl(devicepath, sizeof(devicepath), + udev_get_sys_path(udev), udev_device_get_devpath(dev), NULL); + util_strscpyl(filepath, sizeof(filepath), devicepath, "/", file, NULL); + file = filepath; + } + + dbg(udev, "will wait %i sec for '%s'\n", timeout, file); + while (--loop) { + const struct timespec duration = { 0, 1000 * 1000 * 1000 / WAIT_LOOP_PER_SECOND }; + + /* lookup file */ + if (stat(file, &stats) == 0) { + info(udev, "file '%s' appeared after %i loops\n", file, (timeout * WAIT_LOOP_PER_SECOND) - loop-1); + return 0; + } + /* make sure, the device did not disappear in the meantime */ + if (devicepath[0] != '\0' && stat(devicepath, &stats) != 0) { + info(udev, "device disappeared while waiting for '%s'\n", file); + return -2; + } + info(udev, "wait for '%s' for %i mseconds\n", file, 1000 / WAIT_LOOP_PER_SECOND); + nanosleep(&duration, NULL); + } + info(udev, "waiting for '%s' failed\n", file); + return -1; +} + +static int attr_subst_subdir(char *attr, size_t len) +{ + bool found = false; + + if (strstr(attr, "/*/")) { + char *pos; + char dirname[UTIL_PATH_SIZE]; + const char *tail; + DIR *dir; + + util_strscpy(dirname, sizeof(dirname), attr); + pos = strstr(dirname, "/*/"); + if (pos == NULL) + return -1; + pos[0] = '\0'; + tail = &pos[2]; + dir = opendir(dirname); + if (dir != NULL) { + struct dirent *dent; + + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + struct stat stats; + + if (dent->d_name[0] == '.') + continue; + util_strscpyl(attr, len, dirname, "/", dent->d_name, tail, NULL); + if (stat(attr, &stats) == 0) { + found = true; + break; + } + } + closedir(dir); + } + } + + return found; +} + +static int get_key(struct udev *udev, char **line, char **key, enum operation_type *op, char **value) +{ + char *linepos; + char *temp; + + linepos = *line; + if (linepos == NULL || linepos[0] == '\0') + return -1; + + /* skip whitespace */ + while (isspace(linepos[0]) || linepos[0] == ',') + linepos++; + + /* get the key */ + if (linepos[0] == '\0') + return -1; + *key = linepos; + + for (;;) { + linepos++; + if (linepos[0] == '\0') + return -1; + if (isspace(linepos[0])) + break; + if (linepos[0] == '=') + break; + if ((linepos[0] == '+') || (linepos[0] == '!') || (linepos[0] == ':')) + if (linepos[1] == '=') + break; + } + + /* remember end of key */ + temp = linepos; + + /* skip whitespace after key */ + while (isspace(linepos[0])) + linepos++; + if (linepos[0] == '\0') + return -1; + + /* get operation type */ + if (linepos[0] == '=' && linepos[1] == '=') { + *op = OP_MATCH; + linepos += 2; + } else if (linepos[0] == '!' && linepos[1] == '=') { + *op = OP_NOMATCH; + linepos += 2; + } else if (linepos[0] == '+' && linepos[1] == '=') { + *op = OP_ADD; + linepos += 2; + } else if (linepos[0] == '=') { + *op = OP_ASSIGN; + linepos++; + } else if (linepos[0] == ':' && linepos[1] == '=') { + *op = OP_ASSIGN_FINAL; + linepos += 2; + } else + return -1; + + /* terminate key */ + temp[0] = '\0'; + + /* skip whitespace after operator */ + while (isspace(linepos[0])) + linepos++; + if (linepos[0] == '\0') + return -1; + + /* get the value */ + if (linepos[0] == '"') + linepos++; + else + return -1; + *value = linepos; + + /* terminate */ + temp = strchr(linepos, '"'); + if (!temp) + return -1; + temp[0] = '\0'; + temp++; + dbg(udev, "%s '%s'-'%s'\n", operation_str(*op), *key, *value); + + /* move line to next key */ + *line = temp; + return 0; +} + +/* extract possible KEY{attr} */ +static char *get_key_attribute(struct udev *udev, char *str) +{ + char *pos; + char *attr; + + attr = strchr(str, '{'); + if (attr != NULL) { + attr++; + pos = strchr(attr, '}'); + if (pos == NULL) { + err(udev, "missing closing brace for format\n"); + return NULL; + } + pos[0] = '\0'; + dbg(udev, "attribute='%s'\n", attr); + return attr; + } + return NULL; +} + +static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type, + enum operation_type op, + const char *value, const void *data) +{ + struct token *token = &rule_tmp->token[rule_tmp->token_cur]; + const char *attr = NULL; + + memset(token, 0x00, sizeof(struct token)); + + switch (type) { + case TK_M_ACTION: + case TK_M_DEVPATH: + case TK_M_KERNEL: + case TK_M_SUBSYSTEM: + case TK_M_DRIVER: + case TK_M_WAITFOR: + case TK_M_DEVLINK: + case TK_M_NAME: + case TK_M_KERNELS: + case TK_M_SUBSYSTEMS: + case TK_M_DRIVERS: + case TK_M_TAGS: + case TK_M_PROGRAM: + case TK_M_IMPORT_FILE: + case TK_M_IMPORT_PROG: + case TK_M_IMPORT_DB: + case TK_M_IMPORT_CMDLINE: + case TK_M_IMPORT_PARENT: + case TK_M_RESULT: + case TK_A_OWNER: + case TK_A_GROUP: + case TK_A_MODE: + case TK_A_NAME: + case TK_A_GOTO: + case TK_M_TAG: + case TK_A_TAG: + token->key.value_off = add_string(rule_tmp->rules, value); + break; + case TK_M_IMPORT_BUILTIN: + token->key.value_off = add_string(rule_tmp->rules, value); + token->key.builtin_cmd = *(enum udev_builtin_cmd *)data; + break; + case TK_M_ENV: + case TK_M_ATTR: + case TK_M_ATTRS: + case TK_A_ATTR: + case TK_A_ENV: + attr = data; + token->key.value_off = add_string(rule_tmp->rules, value); + token->key.attr_off = add_string(rule_tmp->rules, attr); + break; + case TK_A_DEVLINK: + token->key.value_off = add_string(rule_tmp->rules, value); + token->key.devlink_unique = *(int *)data; + break; + case TK_M_TEST: + token->key.value_off = add_string(rule_tmp->rules, value); + if (data != NULL) + token->key.mode = *(mode_t *)data; + break; + case TK_A_STRING_ESCAPE_NONE: + case TK_A_STRING_ESCAPE_REPLACE: + case TK_A_DB_PERSIST: + break; + case TK_A_RUN: + token->key.value_off = add_string(rule_tmp->rules, value); + break; + case TK_A_INOTIFY_WATCH: + case TK_A_DEVLINK_PRIO: + token->key.devlink_prio = *(int *)data; + break; + case TK_A_OWNER_ID: + token->key.uid = *(uid_t *)data; + break; + case TK_A_GROUP_ID: + token->key.gid = *(gid_t *)data; + break; + case TK_A_MODE_ID: + token->key.mode = *(mode_t *)data; + break; + case TK_A_STATIC_NODE: + token->key.value_off = add_string(rule_tmp->rules, value); + break; + case TK_M_EVENT_TIMEOUT: + token->key.event_timeout = *(int *)data; + break; + case TK_RULE: + case TK_M_PARENTS_MIN: + case TK_M_PARENTS_MAX: + case TK_M_MAX: + case TK_END: + case TK_UNSET: + err(rule_tmp->rules->udev, "wrong type %u\n", type); + return -1; + } + + if (value != NULL && type < TK_M_MAX) { + /* check if we need to split or call fnmatch() while matching rules */ + enum string_glob_type glob; + int has_split; + int has_glob; + + has_split = (strchr(value, '|') != NULL); + has_glob = (strchr(value, '*') != NULL || strchr(value, '?') != NULL || strchr(value, '[') != NULL); + if (has_split && has_glob) { + glob = GL_SPLIT_GLOB; + } else if (has_split) { + glob = GL_SPLIT; + } else if (has_glob) { + if (strcmp(value, "?*") == 0) + glob = GL_SOMETHING; + else + glob = GL_GLOB; + } else { + glob = GL_PLAIN; + } + token->key.glob = glob; + } + + if (value != NULL && type > TK_M_MAX) { + /* check if assigned value has substitution chars */ + if (value[0] == '[') + token->key.subst = SB_SUBSYS; + else if (strchr(value, '%') != NULL || strchr(value, '$') != NULL) + token->key.subst = SB_FORMAT; + else + token->key.subst = SB_NONE; + } + + if (attr != NULL) { + /* check if property/attribut name has substitution chars */ + if (attr[0] == '[') + token->key.attrsubst = SB_SUBSYS; + else if (strchr(attr, '%') != NULL || strchr(attr, '$') != NULL) + token->key.attrsubst = SB_FORMAT; + else + token->key.attrsubst = SB_NONE; + } + + token->key.type = type; + token->key.op = op; + rule_tmp->token_cur++; + if (rule_tmp->token_cur >= ARRAY_SIZE(rule_tmp->token)) { + err(rule_tmp->rules->udev, "temporary rule array too small\n"); + return -1; + } + return 0; +} + +static int sort_token(struct udev_rules *rules, struct rule_tmp *rule_tmp) +{ + unsigned int i; + unsigned int start = 0; + unsigned int end = rule_tmp->token_cur; + + for (i = 0; i < rule_tmp->token_cur; i++) { + enum token_type next_val = TK_UNSET; + unsigned int next_idx = 0; + unsigned int j; + + /* find smallest value */ + for (j = start; j < end; j++) { + if (rule_tmp->token[j].type == TK_UNSET) + continue; + if (next_val == TK_UNSET || rule_tmp->token[j].type < next_val) { + next_val = rule_tmp->token[j].type; + next_idx = j; + } + } + + /* add token and mark done */ + if (add_token(rules, &rule_tmp->token[next_idx]) != 0) + return -1; + rule_tmp->token[next_idx].type = TK_UNSET; + + /* shrink range */ + if (next_idx == start) + start++; + if (next_idx+1 == end) + end--; + } + return 0; +} + +static int add_rule(struct udev_rules *rules, char *line, + const char *filename, unsigned int filename_off, unsigned int lineno) +{ + char *linepos; + char *attr; + struct rule_tmp rule_tmp; + + memset(&rule_tmp, 0x00, sizeof(struct rule_tmp)); + rule_tmp.rules = rules; + rule_tmp.rule.type = TK_RULE; + rule_tmp.rule.rule.filename_off = filename_off; + rule_tmp.rule.rule.filename_line = lineno; + + linepos = line; + for (;;) { + char *key; + char *value; + enum operation_type op; + + if (get_key(rules->udev, &linepos, &key, &op, &value) != 0) + break; + + if (strcmp(key, "ACTION") == 0) { + if (op > OP_MATCH_MAX) { + err(rules->udev, "invalid ACTION operation\n"); + goto invalid; + } + rule_add_key(&rule_tmp, TK_M_ACTION, op, value, NULL); + continue; + } + + if (strcmp(key, "DEVPATH") == 0) { + if (op > OP_MATCH_MAX) { + err(rules->udev, "invalid DEVPATH operation\n"); + goto invalid; + } + rule_add_key(&rule_tmp, TK_M_DEVPATH, op, value, NULL); + continue; + } + + if (strcmp(key, "KERNEL") == 0) { + if (op > OP_MATCH_MAX) { + err(rules->udev, "invalid KERNEL operation\n"); + goto invalid; + } + rule_add_key(&rule_tmp, TK_M_KERNEL, op, value, NULL); + continue; + } + + if (strcmp(key, "SUBSYSTEM") == 0) { + if (op > OP_MATCH_MAX) { + err(rules->udev, "invalid SUBSYSTEM operation\n"); + goto invalid; + } + /* bus, class, subsystem events should all be the same */ + if (strcmp(value, "subsystem") == 0 || + strcmp(value, "bus") == 0 || + strcmp(value, "class") == 0) { + if (strcmp(value, "bus") == 0 || strcmp(value, "class") == 0) + err(rules->udev, "'%s' must be specified as 'subsystem' \n" + "please fix it in %s:%u", value, filename, lineno); + rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, "subsystem|class|bus", NULL); + } else + rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, value, NULL); + continue; + } + + if (strcmp(key, "DRIVER") == 0) { + if (op > OP_MATCH_MAX) { + err(rules->udev, "invalid DRIVER operation\n"); + goto invalid; + } + rule_add_key(&rule_tmp, TK_M_DRIVER, op, value, NULL); + continue; + } + + if (strncmp(key, "ATTR{", sizeof("ATTR{")-1) == 0) { + attr = get_key_attribute(rules->udev, key + sizeof("ATTR")-1); + if (attr == NULL) { + err(rules->udev, "error parsing ATTR attribute\n"); + goto invalid; + } + if (op < OP_MATCH_MAX) { + rule_add_key(&rule_tmp, TK_M_ATTR, op, value, attr); + } else { + rule_add_key(&rule_tmp, TK_A_ATTR, op, value, attr); + } + continue; + } + + if (strcmp(key, "KERNELS") == 0) { + if (op > OP_MATCH_MAX) { + err(rules->udev, "invalid KERNELS operation\n"); + goto invalid; + } + rule_add_key(&rule_tmp, TK_M_KERNELS, op, value, NULL); + continue; + } + + if (strcmp(key, "SUBSYSTEMS") == 0) { + if (op > OP_MATCH_MAX) { + err(rules->udev, "invalid SUBSYSTEMS operation\n"); + goto invalid; + } + rule_add_key(&rule_tmp, TK_M_SUBSYSTEMS, op, value, NULL); + continue; + } + + if (strcmp(key, "DRIVERS") == 0) { + if (op > OP_MATCH_MAX) { + err(rules->udev, "invalid DRIVERS operation\n"); + goto invalid; + } + rule_add_key(&rule_tmp, TK_M_DRIVERS, op, value, NULL); + continue; + } + + if (strncmp(key, "ATTRS{", sizeof("ATTRS{")-1) == 0) { + if (op > OP_MATCH_MAX) { + err(rules->udev, "invalid ATTRS operation\n"); + goto invalid; + } + attr = get_key_attribute(rules->udev, key + sizeof("ATTRS")-1); + if (attr == NULL) { + err(rules->udev, "error parsing ATTRS attribute\n"); + goto invalid; + } + if (strncmp(attr, "device/", 7) == 0) + err(rules->udev, "the 'device' link may not be available in a future kernel, " + "please fix it in %s:%u", filename, lineno); + else if (strstr(attr, "../") != NULL) + err(rules->udev, "do not reference parent sysfs directories directly, " + "it may break with a future kernel, please fix it in %s:%u", filename, lineno); + rule_add_key(&rule_tmp, TK_M_ATTRS, op, value, attr); + continue; + } + + if (strcmp(key, "TAGS") == 0) { + if (op > OP_MATCH_MAX) { + err(rules->udev, "invalid TAGS operation\n"); + goto invalid; + } + rule_add_key(&rule_tmp, TK_M_TAGS, op, value, NULL); + continue; + } + + if (strncmp(key, "ENV{", sizeof("ENV{")-1) == 0) { + attr = get_key_attribute(rules->udev, key + sizeof("ENV")-1); + if (attr == NULL) { + err(rules->udev, "error parsing ENV attribute\n"); + goto invalid; + } + if (op < OP_MATCH_MAX) { + if (rule_add_key(&rule_tmp, TK_M_ENV, op, value, attr) != 0) + goto invalid; + } else { + static const char *blacklist[] = { + "ACTION", + "SUBSYSTEM", + "DEVTYPE", + "MAJOR", + "MINOR", + "DRIVER", + "IFINDEX", + "DEVNAME", + "DEVLINKS", + "DEVPATH", + "TAGS", + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(blacklist); i++) + if (strcmp(attr, blacklist[i]) == 0) { + err(rules->udev, "invalid ENV attribute, '%s' can not be set %s:%u\n", attr, filename, lineno); + continue; + } + if (rule_add_key(&rule_tmp, TK_A_ENV, op, value, attr) != 0) + goto invalid; + } + continue; + } + + if (strcmp(key, "TAG") == 0) { + if (op < OP_MATCH_MAX) + rule_add_key(&rule_tmp, TK_M_TAG, op, value, NULL); + else + rule_add_key(&rule_tmp, TK_A_TAG, op, value, NULL); + continue; + } + + if (strcmp(key, "PROGRAM") == 0) { + rule_add_key(&rule_tmp, TK_M_PROGRAM, op, value, NULL); + continue; + } + + if (strcmp(key, "RESULT") == 0) { + if (op > OP_MATCH_MAX) { + err(rules->udev, "invalid RESULT operation\n"); + goto invalid; + } + rule_add_key(&rule_tmp, TK_M_RESULT, op, value, NULL); + continue; + } + + if (strncmp(key, "IMPORT", sizeof("IMPORT")-1) == 0) { + attr = get_key_attribute(rules->udev, key + sizeof("IMPORT")-1); + if (attr == NULL) { + err(rules->udev, "IMPORT{} type missing, ignoring IMPORT %s:%u\n", filename, lineno); + continue; + } + if (strstr(attr, "program")) { + /* find known built-in command */ + if (value[0] != '/') { + enum udev_builtin_cmd cmd; + + cmd = udev_builtin_lookup(value); + if (cmd < UDEV_BUILTIN_MAX) { + info(rules->udev, "IMPORT found builtin '%s', replacing %s:%u\n", + value, filename, lineno); + rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd); + continue; + } + } + dbg(rules->udev, "IMPORT will be executed\n"); + rule_add_key(&rule_tmp, TK_M_IMPORT_PROG, op, value, NULL); + } else if (strstr(attr, "builtin")) { + enum udev_builtin_cmd cmd = udev_builtin_lookup(value); + + dbg(rules->udev, "IMPORT execute builtin\n"); + if (cmd < UDEV_BUILTIN_MAX) + rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd); + else + err(rules->udev, "IMPORT{builtin}: '%s' unknown %s:%u\n", value, filename, lineno); + } else if (strstr(attr, "file")) { + dbg(rules->udev, "IMPORT will be included as file\n"); + rule_add_key(&rule_tmp, TK_M_IMPORT_FILE, op, value, NULL); + } else if (strstr(attr, "db")) { + dbg(rules->udev, "IMPORT will include db values\n"); + rule_add_key(&rule_tmp, TK_M_IMPORT_DB, op, value, NULL); + } else if (strstr(attr, "cmdline")) { + dbg(rules->udev, "IMPORT will include db values\n"); + rule_add_key(&rule_tmp, TK_M_IMPORT_CMDLINE, op, value, NULL); + } else if (strstr(attr, "parent")) { + dbg(rules->udev, "IMPORT will include the parent values\n"); + rule_add_key(&rule_tmp, TK_M_IMPORT_PARENT, op, value, NULL); + } + continue; + } + + if (strncmp(key, "TEST", sizeof("TEST")-1) == 0) { + mode_t mode = 0; + + if (op > OP_MATCH_MAX) { + err(rules->udev, "invalid TEST operation\n"); + goto invalid; + } + attr = get_key_attribute(rules->udev, key + sizeof("TEST")-1); + if (attr != NULL) { + mode = strtol(attr, NULL, 8); + rule_add_key(&rule_tmp, TK_M_TEST, op, value, &mode); + } else { + rule_add_key(&rule_tmp, TK_M_TEST, op, value, NULL); + } + continue; + } + + if (strcmp(key, "RUN") == 0) { + if (strncmp(value, "socket:", 7) == 0) + err(rules->udev, "RUN+=\"socket:...\" support will be removed from a future udev release. " + "Please remove it from: %s:%u and use libudev to subscribe to events.\n", filename, lineno); + rule_add_key(&rule_tmp, TK_A_RUN, op, value, NULL); + continue; + } + + if (strcmp(key, "WAIT_FOR") == 0 || strcmp(key, "WAIT_FOR_SYSFS") == 0) { + rule_add_key(&rule_tmp, TK_M_WAITFOR, 0, value, NULL); + continue; + } + + if (strcmp(key, "LABEL") == 0) { + rule_tmp.rule.rule.label_off = add_string(rules, value); + continue; + } + + if (strcmp(key, "GOTO") == 0) { + rule_add_key(&rule_tmp, TK_A_GOTO, 0, value, NULL); + continue; + } + + if (strncmp(key, "NAME", sizeof("NAME")-1) == 0) { + if (op < OP_MATCH_MAX) { + rule_add_key(&rule_tmp, TK_M_NAME, op, value, NULL); + } else { + if (strcmp(value, "%k") == 0) { + err(rules->udev, "NAME=\"%%k\" is ignored, because it breaks kernel supplied names, " + "please remove it from %s:%u\n", filename, lineno); + continue; + } + if (value[0] == '\0') { + info(rules->udev, "NAME=\"\" is ignored, because udev will not delete any device nodes, " + "please remove it from %s:%u\n", filename, lineno); + continue; + } + rule_add_key(&rule_tmp, TK_A_NAME, op, value, NULL); + } + rule_tmp.rule.rule.can_set_name = true; + continue; + } + + if (strncmp(key, "SYMLINK", sizeof("SYMLINK")-1) == 0) { + if (op < OP_MATCH_MAX) { + rule_add_key(&rule_tmp, TK_M_DEVLINK, op, value, NULL); + } else { + int flag = 0; + + attr = get_key_attribute(rules->udev, key + sizeof("SYMLINK")-1); + if (attr != NULL && strstr(attr, "unique") != NULL) + flag = 1; + rule_add_key(&rule_tmp, TK_A_DEVLINK, op, value, &flag); + } + rule_tmp.rule.rule.can_set_name = true; + continue; + } + + if (strcmp(key, "OWNER") == 0) { + uid_t uid; + char *endptr; + + uid = strtoul(value, &endptr, 10); + if (endptr[0] == '\0') { + rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid); + } else if ((rules->resolve_names > 0) && strchr("$%", value[0]) == NULL) { + uid = add_uid(rules, value); + rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid); + } else if (rules->resolve_names >= 0) { + rule_add_key(&rule_tmp, TK_A_OWNER, op, value, NULL); + } + rule_tmp.rule.rule.can_set_name = true; + continue; + } + + if (strcmp(key, "GROUP") == 0) { + gid_t gid; + char *endptr; + + gid = strtoul(value, &endptr, 10); + if (endptr[0] == '\0') { + rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid); + } else if ((rules->resolve_names > 0) && strchr("$%", value[0]) == NULL) { + gid = add_gid(rules, value); + rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid); + } else if (rules->resolve_names >= 0) { + rule_add_key(&rule_tmp, TK_A_GROUP, op, value, NULL); + } + rule_tmp.rule.rule.can_set_name = true; + continue; + } + + if (strcmp(key, "MODE") == 0) { + mode_t mode; + char *endptr; + + mode = strtol(value, &endptr, 8); + if (endptr[0] == '\0') + rule_add_key(&rule_tmp, TK_A_MODE_ID, op, NULL, &mode); + else + rule_add_key(&rule_tmp, TK_A_MODE, op, value, NULL); + rule_tmp.rule.rule.can_set_name = true; + continue; + } + + if (strcmp(key, "OPTIONS") == 0) { + const char *pos; + + pos = strstr(value, "link_priority="); + if (pos != NULL) { + int prio = atoi(&pos[strlen("link_priority=")]); + + rule_add_key(&rule_tmp, TK_A_DEVLINK_PRIO, op, NULL, &prio); + dbg(rules->udev, "link priority=%i\n", prio); + } + + pos = strstr(value, "event_timeout="); + if (pos != NULL) { + int tout = atoi(&pos[strlen("event_timeout=")]); + + rule_add_key(&rule_tmp, TK_M_EVENT_TIMEOUT, op, NULL, &tout); + dbg(rules->udev, "event timeout=%i\n", tout); + } + + pos = strstr(value, "string_escape="); + if (pos != NULL) { + pos = &pos[strlen("string_escape=")]; + if (strncmp(pos, "none", strlen("none")) == 0) + rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_NONE, op, NULL, NULL); + else if (strncmp(pos, "replace", strlen("replace")) == 0) + rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_REPLACE, op, NULL, NULL); + } + + pos = strstr(value, "db_persist"); + if (pos != NULL) + rule_add_key(&rule_tmp, TK_A_DB_PERSIST, op, NULL, NULL); + + pos = strstr(value, "nowatch"); + if (pos != NULL) { + const int off = 0; + + rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &off); + dbg(rules->udev, "inotify watch of device disabled\n"); + } else { + pos = strstr(value, "watch"); + if (pos != NULL) { + const int on = 1; + + rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, op, NULL, &on); + dbg(rules->udev, "inotify watch of device requested\n"); + } + } + + pos = strstr(value, "static_node="); + if (pos != NULL) { + rule_add_key(&rule_tmp, TK_A_STATIC_NODE, op, &pos[strlen("static_node=")], NULL); + rule_tmp.rule.rule.has_static_node = true; + } + + continue; + } + + err(rules->udev, "unknown key '%s' in %s:%u\n", key, filename, lineno); + goto invalid; + } + + /* add rule token */ + rule_tmp.rule.rule.token_count = 1 + rule_tmp.token_cur; + if (add_token(rules, &rule_tmp.rule) != 0) + goto invalid; + + /* add tokens to list, sorted by type */ + if (sort_token(rules, &rule_tmp) != 0) + goto invalid; + + return 0; +invalid: + err(rules->udev, "invalid rule '%s:%u'\n", filename, lineno); + return -1; +} + +static int parse_file(struct udev_rules *rules, const char *filename, unsigned short filename_off) +{ + FILE *f; + unsigned int first_token; + char line[UTIL_LINE_SIZE]; + int line_nr = 0; + unsigned int i; + + info(rules->udev, "reading '%s' as rules file\n", filename); + + f = fopen(filename, "r"); + if (f == NULL) + return -1; + + first_token = rules->token_cur; + + while (fgets(line, sizeof(line), f) != NULL) { + char *key; + size_t len; + + /* skip whitespace */ + line_nr++; + key = line; + while (isspace(key[0])) + key++; + + /* comment */ + if (key[0] == '#') + continue; + + len = strlen(line); + if (len < 3) + continue; + + /* continue reading if backslash+newline is found */ + while (line[len-2] == '\\') { + if (fgets(&line[len-2], (sizeof(line)-len)+2, f) == NULL) + break; + if (strlen(&line[len-2]) < 2) + break; + line_nr++; + len = strlen(line); + } + + if (len+1 >= sizeof(line)) { + err(rules->udev, "line too long '%s':%u, ignored\n", filename, line_nr); + continue; + } + add_rule(rules, key, filename, filename_off, line_nr); + } + fclose(f); + + /* link GOTOs to LABEL rules in this file to be able to fast-forward */ + for (i = first_token+1; i < rules->token_cur; i++) { + if (rules->tokens[i].type == TK_A_GOTO) { + char *label = &rules->buf[rules->tokens[i].key.value_off]; + unsigned int j; + + for (j = i+1; j < rules->token_cur; j++) { + if (rules->tokens[j].type != TK_RULE) + continue; + if (rules->tokens[j].rule.label_off == 0) + continue; + if (strcmp(label, &rules->buf[rules->tokens[j].rule.label_off]) != 0) + continue; + rules->tokens[i].key.rule_goto = j; + break; + } + if (rules->tokens[i].key.rule_goto == 0) + err(rules->udev, "GOTO '%s' has no matching label in: '%s'\n", label, filename); + } + } + return 0; +} + +static int add_matching_files(struct udev *udev, struct udev_list *file_list, const char *dirname, const char *suffix) +{ + DIR *dir; + struct dirent *dent; + char filename[UTIL_PATH_SIZE]; + + dbg(udev, "open directory '%s'\n", dirname); + dir = opendir(dirname); + if (dir == NULL) { + info(udev, "unable to open '%s': %m\n", dirname); + return -1; + } + + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + if (dent->d_name[0] == '.') + continue; + + /* look for file matching with specified suffix */ + if (suffix != NULL) { + const char *ext; + + ext = strrchr(dent->d_name, '.'); + if (ext == NULL) + continue; + if (strcmp(ext, suffix) != 0) + continue; + } + util_strscpyl(filename, sizeof(filename), dirname, "/", dent->d_name, NULL); + dbg(udev, "put file '%s' into list\n", filename); + /* + * the basename is the key, the filename the value + * identical basenames from different directories override each other + * entries are sorted after basename + */ + udev_list_entry_add(file_list, dent->d_name, filename); + } + + closedir(dir); + return 0; +} + +struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names) +{ + struct udev_rules *rules; + struct udev_list file_list; + struct udev_list_entry *file_loop; + struct token end_token; + char **s; + + rules = calloc(1, sizeof(struct udev_rules)); + if (rules == NULL) + return NULL; + rules->udev = udev; + rules->resolve_names = resolve_names; + udev_list_init(udev, &file_list, true); + + /* init token array and string buffer */ + rules->tokens = malloc(PREALLOC_TOKEN * sizeof(struct token)); + if (rules->tokens == NULL) { + free(rules); + return NULL; + } + rules->token_max = PREALLOC_TOKEN; + + rules->buf = malloc(PREALLOC_STRBUF); + if (rules->buf == NULL) { + free(rules->tokens); + free(rules); + return NULL; + } + rules->buf_max = PREALLOC_STRBUF; + /* offset 0 is always '\0' */ + rules->buf[0] = '\0'; + rules->buf_cur = 1; + dbg(udev, "prealloc %zu bytes tokens (%u * %zu bytes), %zu bytes buffer\n", + rules->token_max * sizeof(struct token), rules->token_max, sizeof(struct token), rules->buf_max); + + rules->trie_nodes = malloc(PREALLOC_TRIE * sizeof(struct trie_node)); + if (rules->trie_nodes == NULL) { + free(rules->buf); + free(rules->tokens); + free(rules); + return NULL; + } + rules->trie_nodes_max = PREALLOC_TRIE; + /* offset 0 is the trie root, with an empty string */ + memset(rules->trie_nodes, 0x00, sizeof(struct trie_node)); + rules->trie_nodes_cur = 1; + + for (udev_get_rules_path(udev, &s, NULL); *s != NULL; s++) + add_matching_files(udev, &file_list, *s, ".rules"); + + /* add all filenames to the string buffer */ + udev_list_entry_foreach(file_loop, udev_list_get_entry(&file_list)) { + const char *filename = udev_list_entry_get_value(file_loop); + unsigned int filename_off; + + filename_off = add_string(rules, filename); + /* the offset in the rule is limited to unsigned short */ + if (filename_off < USHRT_MAX) + udev_list_entry_set_num(file_loop, filename_off); + } + + /* parse all rules files */ + udev_list_entry_foreach(file_loop, udev_list_get_entry(&file_list)) { + const char *filename = udev_list_entry_get_value(file_loop); + unsigned int filename_off = udev_list_entry_get_num(file_loop); + struct stat st; + + if (stat(filename, &st) != 0) { + err(udev, "can not find '%s': %m\n", filename); + continue; + } + if (S_ISREG(st.st_mode) && st.st_size <= 0) { + info(udev, "ignore empty '%s'\n", filename); + continue; + } + if (S_ISCHR(st.st_mode)) { + info(udev, "ignore masked '%s'\n", filename); + continue; + } + parse_file(rules, filename, filename_off); + } + udev_list_cleanup(&file_list); + + memset(&end_token, 0x00, sizeof(struct token)); + end_token.type = TK_END; + add_token(rules, &end_token); + + /* shrink allocated token and string buffer */ + if (rules->token_cur < rules->token_max) { + struct token *tokens; + + tokens = realloc(rules->tokens, rules->token_cur * sizeof(struct token)); + if (tokens != NULL || rules->token_cur == 0) { + rules->tokens = tokens; + rules->token_max = rules->token_cur; + } + } + if (rules->buf_cur < rules->buf_max) { + char *buf; + + buf = realloc(rules->buf, rules->buf_cur); + if (buf != NULL || rules->buf_cur == 0) { + rules->buf = buf; + rules->buf_max = rules->buf_cur; + } + } + info(udev, "rules use %zu bytes tokens (%u * %zu bytes), %zu bytes buffer\n", + rules->token_max * sizeof(struct token), rules->token_max, sizeof(struct token), rules->buf_max); + info(udev, "temporary index used %zu bytes (%u * %zu bytes)\n", + rules->trie_nodes_cur * sizeof(struct trie_node), + rules->trie_nodes_cur, sizeof(struct trie_node)); + + /* cleanup trie */ + free(rules->trie_nodes); + rules->trie_nodes = NULL; + rules->trie_nodes_cur = 0; + rules->trie_nodes_max = 0; + + /* cleanup uid/gid cache */ + free(rules->uids); + rules->uids = NULL; + rules->uids_cur = 0; + rules->uids_max = 0; + free(rules->gids); + rules->gids = NULL; + rules->gids_cur = 0; + rules->gids_max = 0; + + dump_rules(rules); + return rules; +} + +struct udev_rules *udev_rules_unref(struct udev_rules *rules) +{ + if (rules == NULL) + return NULL; + free(rules->tokens); + free(rules->buf); + free(rules->trie_nodes); + free(rules->uids); + free(rules->gids); + free(rules); + return NULL; +} + +static int match_key(struct udev_rules *rules, struct token *token, const char *val) +{ + char *key_value = &rules->buf[token->key.value_off]; + char *pos; + bool match = false; + + if (val == NULL) + val = ""; + + switch (token->key.glob) { + case GL_PLAIN: + match = (strcmp(key_value, val) == 0); + break; + case GL_GLOB: + match = (fnmatch(key_value, val, 0) == 0); + break; + case GL_SPLIT: + { + const char *split; + size_t len; + + split = &rules->buf[token->key.value_off]; + len = strlen(val); + for (;;) { + const char *next; + + next = strchr(split, '|'); + if (next != NULL) { + size_t matchlen = (size_t)(next - split); + + match = (matchlen == len && strncmp(split, val, matchlen) == 0); + if (match) + break; + } else { + match = (strcmp(split, val) == 0); + break; + } + split = &next[1]; + } + break; + } + case GL_SPLIT_GLOB: + { + char value[UTIL_PATH_SIZE]; + + util_strscpy(value, sizeof(value), &rules->buf[token->key.value_off]); + key_value = value; + while (key_value != NULL) { + pos = strchr(key_value, '|'); + if (pos != NULL) { + pos[0] = '\0'; + pos = &pos[1]; + } + dbg(rules->udev, "match %s '%s' <-> '%s'\n", token_str(token->type), key_value, val); + match = (fnmatch(key_value, val, 0) == 0); + if (match) + break; + key_value = pos; + } + break; + } + case GL_SOMETHING: + match = (val[0] != '\0'); + break; + case GL_UNSET: + return -1; + } + + if (match && (token->key.op == OP_MATCH)) { + dbg(rules->udev, "%s is true (matching value)\n", token_str(token->type)); + return 0; + } + if (!match && (token->key.op == OP_NOMATCH)) { + dbg(rules->udev, "%s is true (non-matching value)\n", token_str(token->type)); + return 0; + } + dbg(rules->udev, "%s is not true\n", token_str(token->type)); + return -1; +} + +static int match_attr(struct udev_rules *rules, struct udev_device *dev, struct udev_event *event, struct token *cur) +{ + const char *name; + char nbuf[UTIL_NAME_SIZE]; + const char *value; + char vbuf[UTIL_NAME_SIZE]; + size_t len; + + name = &rules->buf[cur->key.attr_off]; + switch (cur->key.attrsubst) { + case SB_FORMAT: + udev_event_apply_format(event, name, nbuf, sizeof(nbuf)); + name = nbuf; + /* fall through */ + case SB_NONE: + value = udev_device_get_sysattr_value(dev, name); + if (value == NULL) + return -1; + break; + case SB_SUBSYS: + if (util_resolve_subsys_kernel(event->udev, name, vbuf, sizeof(vbuf), 1) != 0) + return -1; + value = vbuf; + break; + default: + return -1; + } + + /* remove trailing whitespace, if not asked to match for it */ + len = strlen(value); + if (len > 0 && isspace(value[len-1])) { + const char *key_value; + size_t klen; + + key_value = &rules->buf[cur->key.value_off]; + klen = strlen(key_value); + if (klen > 0 && !isspace(key_value[klen-1])) { + if (value != vbuf) { + util_strscpy(vbuf, sizeof(vbuf), value); + value = vbuf; + } + while (len > 0 && isspace(vbuf[--len])) + vbuf[len] = '\0'; + dbg(rules->udev, "removed trailing whitespace from '%s'\n", value); + } + } + + return match_key(rules, cur, value); +} + +enum escape_type { + ESCAPE_UNSET, + ESCAPE_NONE, + ESCAPE_REPLACE, +}; + +int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event, const sigset_t *sigmask) +{ + struct token *cur; + struct token *rule; + enum escape_type esc = ESCAPE_UNSET; + bool can_set_name; + + if (rules->tokens == NULL) + return -1; + + can_set_name = ((strcmp(udev_device_get_action(event->dev), "remove") != 0) && + (major(udev_device_get_devnum(event->dev)) > 0 || + udev_device_get_ifindex(event->dev) > 0)); + + /* loop through token list, match, run actions or forward to next rule */ + cur = &rules->tokens[0]; + rule = cur; + for (;;) { + dump_token(rules, cur); + switch (cur->type) { + case TK_RULE: + /* current rule */ + rule = cur; + /* possibly skip rules which want to set NAME, SYMLINK, OWNER, GROUP, MODE */ + if (!can_set_name && rule->rule.can_set_name) + goto nomatch; + esc = ESCAPE_UNSET; + break; + case TK_M_ACTION: + if (match_key(rules, cur, udev_device_get_action(event->dev)) != 0) + goto nomatch; + break; + case TK_M_DEVPATH: + if (match_key(rules, cur, udev_device_get_devpath(event->dev)) != 0) + goto nomatch; + break; + case TK_M_KERNEL: + if (match_key(rules, cur, udev_device_get_sysname(event->dev)) != 0) + goto nomatch; + break; + case TK_M_DEVLINK: { + size_t devlen = strlen(udev_get_dev_path(event->udev))+1; + struct udev_list_entry *list_entry; + bool match = false; + + udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(event->dev)) { + const char *devlink; + + devlink = &udev_list_entry_get_name(list_entry)[devlen]; + if (match_key(rules, cur, devlink) == 0) { + match = true; + break; + } + } + if (!match) + goto nomatch; + break; + } + case TK_M_NAME: + if (match_key(rules, cur, event->name) != 0) + goto nomatch; + break; + case TK_M_ENV: { + const char *key_name = &rules->buf[cur->key.attr_off]; + const char *value; + + value = udev_device_get_property_value(event->dev, key_name); + if (value == NULL) { + dbg(event->udev, "ENV{%s} is not set, treat as empty\n", key_name); + value = ""; + } + if (match_key(rules, cur, value)) + goto nomatch; + break; + } + case TK_M_TAG: { + struct udev_list_entry *list_entry; + bool match = false; + + udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(event->dev)) { + if (strcmp(&rules->buf[cur->key.value_off], udev_list_entry_get_name(list_entry)) == 0) { + match = true; + break; + } + } + if (!match && (cur->key.op != OP_NOMATCH)) + goto nomatch; + break; + } + case TK_M_SUBSYSTEM: + if (match_key(rules, cur, udev_device_get_subsystem(event->dev)) != 0) + goto nomatch; + break; + case TK_M_DRIVER: + if (match_key(rules, cur, udev_device_get_driver(event->dev)) != 0) + goto nomatch; + break; + case TK_M_WAITFOR: { + char filename[UTIL_PATH_SIZE]; + int found; + + udev_event_apply_format(event, &rules->buf[cur->key.value_off], filename, sizeof(filename)); + found = (wait_for_file(event->dev, filename, 10) == 0); + if (!found && (cur->key.op != OP_NOMATCH)) + goto nomatch; + break; + } + case TK_M_ATTR: + if (match_attr(rules, event->dev, event, cur) != 0) + goto nomatch; + break; + case TK_M_KERNELS: + case TK_M_SUBSYSTEMS: + case TK_M_DRIVERS: + case TK_M_ATTRS: + case TK_M_TAGS: { + struct token *next; + + /* get whole sequence of parent matches */ + next = cur; + while (next->type > TK_M_PARENTS_MIN && next->type < TK_M_PARENTS_MAX) + next++; + + /* loop over parents */ + event->dev_parent = event->dev; + for (;;) { + struct token *key; + + dbg(event->udev, "parent: '%s'\n", udev_device_get_syspath(event->dev_parent)); + /* loop over sequence of parent match keys */ + for (key = cur; key < next; key++ ) { + dump_token(rules, key); + switch(key->type) { + case TK_M_KERNELS: + if (match_key(rules, key, udev_device_get_sysname(event->dev_parent)) != 0) + goto try_parent; + break; + case TK_M_SUBSYSTEMS: + if (match_key(rules, key, udev_device_get_subsystem(event->dev_parent)) != 0) + goto try_parent; + break; + case TK_M_DRIVERS: + if (match_key(rules, key, udev_device_get_driver(event->dev_parent)) != 0) + goto try_parent; + break; + case TK_M_ATTRS: + if (match_attr(rules, event->dev_parent, event, key) != 0) + goto try_parent; + break; + case TK_M_TAGS: { + bool match = udev_device_has_tag(event->dev_parent, &rules->buf[cur->key.value_off]); + + if (match && key->key.op == OP_NOMATCH) + goto try_parent; + if (!match && key->key.op == OP_MATCH) + goto try_parent; + break; + } + default: + goto nomatch; + } + dbg(event->udev, "parent key matched\n"); + } + dbg(event->udev, "all parent keys matched\n"); + break; + + try_parent: + event->dev_parent = udev_device_get_parent(event->dev_parent); + if (event->dev_parent == NULL) + goto nomatch; + } + /* move behind our sequence of parent match keys */ + cur = next; + continue; + } + case TK_M_TEST: { + char filename[UTIL_PATH_SIZE]; + struct stat statbuf; + int match; + + udev_event_apply_format(event, &rules->buf[cur->key.value_off], filename, sizeof(filename)); + if (util_resolve_subsys_kernel(event->udev, filename, filename, sizeof(filename), 0) != 0) { + if (filename[0] != '/') { + char tmp[UTIL_PATH_SIZE]; + + util_strscpy(tmp, sizeof(tmp), filename); + util_strscpyl(filename, sizeof(filename), + udev_device_get_syspath(event->dev), "/", tmp, NULL); + } + } + attr_subst_subdir(filename, sizeof(filename)); + + match = (stat(filename, &statbuf) == 0); + dbg(event->udev, "'%s' %s", filename, match ? "exists\n" : "does not exist\n"); + if (match && cur->key.mode > 0) { + match = ((statbuf.st_mode & cur->key.mode) > 0); + dbg(event->udev, "'%s' has mode=%#o and %s %#o\n", filename, statbuf.st_mode, + match ? "matches" : "does not match", cur->key.mode); + } + if (match && cur->key.op == OP_NOMATCH) + goto nomatch; + if (!match && cur->key.op == OP_MATCH) + goto nomatch; + break; + } + case TK_M_EVENT_TIMEOUT: + info(event->udev, "OPTIONS event_timeout=%u\n", cur->key.event_timeout); + event->timeout_usec = cur->key.event_timeout * 1000 * 1000; + break; + case TK_M_PROGRAM: { + char program[UTIL_PATH_SIZE]; + char **envp; + char result[UTIL_PATH_SIZE]; + + free(event->program_result); + event->program_result = NULL; + udev_event_apply_format(event, &rules->buf[cur->key.value_off], program, sizeof(program)); + envp = udev_device_get_properties_envp(event->dev); + info(event->udev, "PROGRAM '%s' %s:%u\n", + program, + &rules->buf[rule->rule.filename_off], + rule->rule.filename_line); + + if (udev_event_spawn(event, program, envp, sigmask, result, sizeof(result)) < 0) { + if (cur->key.op != OP_NOMATCH) + goto nomatch; + } else { + int count; + + util_remove_trailing_chars(result, '\n'); + if (esc == ESCAPE_UNSET || esc == ESCAPE_REPLACE) { + count = util_replace_chars(result, UDEV_ALLOWED_CHARS_INPUT); + if (count > 0) + info(event->udev, "%i character(s) replaced\n" , count); + } + event->program_result = strdup(result); + dbg(event->udev, "storing result '%s'\n", event->program_result); + if (cur->key.op == OP_NOMATCH) + goto nomatch; + } + break; + } + case TK_M_IMPORT_FILE: { + char import[UTIL_PATH_SIZE]; + + udev_event_apply_format(event, &rules->buf[cur->key.value_off], import, sizeof(import)); + if (import_file_into_properties(event->dev, import) != 0) + if (cur->key.op != OP_NOMATCH) + goto nomatch; + break; + } + case TK_M_IMPORT_PROG: { + char import[UTIL_PATH_SIZE]; + + udev_event_apply_format(event, &rules->buf[cur->key.value_off], import, sizeof(import)); + info(event->udev, "IMPORT '%s' %s:%u\n", + import, + &rules->buf[rule->rule.filename_off], + rule->rule.filename_line); + + if (import_program_into_properties(event, import, sigmask) != 0) + if (cur->key.op != OP_NOMATCH) + goto nomatch; + break; + } + case TK_M_IMPORT_BUILTIN: { + char command[UTIL_PATH_SIZE]; + + if (udev_builtin_run_once(cur->key.builtin_cmd)) { + /* check if we ran already */ + if (event->builtin_run & (1 << cur->key.builtin_cmd)) { + info(event->udev, "IMPORT builtin skip '%s' %s:%u\n", + udev_builtin_name(cur->key.builtin_cmd), + &rules->buf[rule->rule.filename_off], + rule->rule.filename_line); + /* return the result from earlier run */ + if (event->builtin_ret & (1 << cur->key.builtin_cmd)) + if (cur->key.op != OP_NOMATCH) + goto nomatch; + break; + } + /* mark as ran */ + event->builtin_run |= (1 << cur->key.builtin_cmd); + } + + udev_event_apply_format(event, &rules->buf[cur->key.value_off], command, sizeof(command)); + info(event->udev, "IMPORT builtin '%s' %s:%u\n", + udev_builtin_name(cur->key.builtin_cmd), + &rules->buf[rule->rule.filename_off], + rule->rule.filename_line); + + if (udev_builtin_run(event->dev, cur->key.builtin_cmd, command, false) != 0) { + /* remember failure */ + info(rules->udev, "IMPORT builtin '%s' returned non-zero\n", + udev_builtin_name(cur->key.builtin_cmd)); + event->builtin_ret |= (1 << cur->key.builtin_cmd); + if (cur->key.op != OP_NOMATCH) + goto nomatch; + } + break; + } + case TK_M_IMPORT_DB: { + const char *key = &rules->buf[cur->key.value_off]; + const char *value; + + value = udev_device_get_property_value(event->dev_db, key); + if (value != NULL) { + struct udev_list_entry *entry; + + entry = udev_device_add_property(event->dev, key, value); + udev_list_entry_set_num(entry, true); + } else { + if (cur->key.op != OP_NOMATCH) + goto nomatch; + } + break; + } + case TK_M_IMPORT_CMDLINE: { + FILE *f; + bool imported = false; + + f = fopen("/proc/cmdline", "r"); + if (f != NULL) { + char cmdline[4096]; + + if (fgets(cmdline, sizeof(cmdline), f) != NULL) { + const char *key = &rules->buf[cur->key.value_off]; + char *pos; + + pos = strstr(cmdline, key); + if (pos != NULL) { + struct udev_list_entry *entry; + + pos += strlen(key); + if (pos[0] == '\0' || isspace(pos[0])) { + /* we import simple flags as 'FLAG=1' */ + entry = udev_device_add_property(event->dev, key, "1"); + udev_list_entry_set_num(entry, true); + imported = true; + } else if (pos[0] == '=') { + const char *value; + + pos++; + value = pos; + while (pos[0] != '\0' && !isspace(pos[0])) + pos++; + pos[0] = '\0'; + entry = udev_device_add_property(event->dev, key, value); + udev_list_entry_set_num(entry, true); + imported = true; + } + } + } + fclose(f); + } + if (!imported && cur->key.op != OP_NOMATCH) + goto nomatch; + break; + } + case TK_M_IMPORT_PARENT: { + char import[UTIL_PATH_SIZE]; + + udev_event_apply_format(event, &rules->buf[cur->key.value_off], import, sizeof(import)); + if (import_parent_into_properties(event->dev, import) != 0) + if (cur->key.op != OP_NOMATCH) + goto nomatch; + break; + } + case TK_M_RESULT: + if (match_key(rules, cur, event->program_result) != 0) + goto nomatch; + break; + case TK_A_STRING_ESCAPE_NONE: + esc = ESCAPE_NONE; + break; + case TK_A_STRING_ESCAPE_REPLACE: + esc = ESCAPE_REPLACE; + break; + case TK_A_DB_PERSIST: + udev_device_set_db_persist(event->dev); + break; + case TK_A_INOTIFY_WATCH: + if (event->inotify_watch_final) + break; + if (cur->key.op == OP_ASSIGN_FINAL) + event->inotify_watch_final = true; + event->inotify_watch = cur->key.watch; + break; + case TK_A_DEVLINK_PRIO: + udev_device_set_devlink_priority(event->dev, cur->key.devlink_prio); + break; + case TK_A_OWNER: { + char owner[UTIL_NAME_SIZE]; + + if (event->owner_final) + break; + if (cur->key.op == OP_ASSIGN_FINAL) + event->owner_final = true; + udev_event_apply_format(event, &rules->buf[cur->key.value_off], owner, sizeof(owner)); + event->uid = util_lookup_user(event->udev, owner); + info(event->udev, "OWNER %u %s:%u\n", + event->uid, + &rules->buf[rule->rule.filename_off], + rule->rule.filename_line); + break; + } + case TK_A_GROUP: { + char group[UTIL_NAME_SIZE]; + + if (event->group_final) + break; + if (cur->key.op == OP_ASSIGN_FINAL) + event->group_final = true; + udev_event_apply_format(event, &rules->buf[cur->key.value_off], group, sizeof(group)); + event->gid = util_lookup_group(event->udev, group); + info(event->udev, "GROUP %u %s:%u\n", + event->gid, + &rules->buf[rule->rule.filename_off], + rule->rule.filename_line); + break; + } + case TK_A_MODE: { + char mode_str[UTIL_NAME_SIZE]; + mode_t mode; + char *endptr; + + if (event->mode_final) + break; + udev_event_apply_format(event, &rules->buf[cur->key.value_off], mode_str, sizeof(mode_str)); + mode = strtol(mode_str, &endptr, 8); + if (endptr[0] != '\0') { + err(event->udev, "ignoring invalid mode '%s'\n", mode_str); + break; + } + if (cur->key.op == OP_ASSIGN_FINAL) + event->mode_final = true; + event->mode_set = true; + event->mode = mode; + info(event->udev, "MODE %#o %s:%u\n", + event->mode, + &rules->buf[rule->rule.filename_off], + rule->rule.filename_line); + break; + } + case TK_A_OWNER_ID: + if (event->owner_final) + break; + if (cur->key.op == OP_ASSIGN_FINAL) + event->owner_final = true; + event->uid = cur->key.uid; + info(event->udev, "OWNER %u %s:%u\n", + event->uid, + &rules->buf[rule->rule.filename_off], + rule->rule.filename_line); + break; + case TK_A_GROUP_ID: + if (event->group_final) + break; + if (cur->key.op == OP_ASSIGN_FINAL) + event->group_final = true; + event->gid = cur->key.gid; + info(event->udev, "GROUP %u %s:%u\n", + event->gid, + &rules->buf[rule->rule.filename_off], + rule->rule.filename_line); + break; + case TK_A_MODE_ID: + if (event->mode_final) + break; + if (cur->key.op == OP_ASSIGN_FINAL) + event->mode_final = true; + event->mode_set = true; + event->mode = cur->key.mode; + info(event->udev, "MODE %#o %s:%u\n", + event->mode, + &rules->buf[rule->rule.filename_off], + rule->rule.filename_line); + break; + case TK_A_ENV: { + const char *name = &rules->buf[cur->key.attr_off]; + char *value = &rules->buf[cur->key.value_off]; + + if (value[0] != '\0') { + char temp_value[UTIL_NAME_SIZE]; + struct udev_list_entry *entry; + + udev_event_apply_format(event, value, temp_value, sizeof(temp_value)); + entry = udev_device_add_property(event->dev, name, temp_value); + /* store in db, skip private keys */ + if (name[0] != '.') + udev_list_entry_set_num(entry, true); + } else { + udev_device_add_property(event->dev, name, NULL); + } + break; + } + case TK_A_TAG: { + char tag[UTIL_PATH_SIZE]; + const char *p; + + udev_event_apply_format(event, &rules->buf[cur->key.value_off], tag, sizeof(tag)); + if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) + udev_device_cleanup_tags_list(event->dev); + for (p = tag; *p != '\0'; p++) { + if ((*p >= 'a' && *p <= 'z') || + (*p >= 'A' && *p <= 'Z') || + (*p >= '0' && *p <= '9') || + *p == '-' || *p == '_') + continue; + err(event->udev, "ignoring invalid tag name '%s'\n", tag); + break; + } + udev_device_add_tag(event->dev, tag); + break; + } + case TK_A_NAME: { + const char *name = &rules->buf[cur->key.value_off]; + + char name_str[UTIL_PATH_SIZE]; + int count; + + if (event->name_final) + break; + if (cur->key.op == OP_ASSIGN_FINAL) + event->name_final = true; + udev_event_apply_format(event, name, name_str, sizeof(name_str)); + if (esc == ESCAPE_UNSET || esc == ESCAPE_REPLACE) { + count = util_replace_chars(name_str, "/"); + if (count > 0) + info(event->udev, "%i character(s) replaced\n", count); + } + if (major(udev_device_get_devnum(event->dev))) { + size_t devlen = strlen(udev_get_dev_path(event->udev))+1; + + if (strcmp(name_str, &udev_device_get_devnode(event->dev)[devlen]) != 0) { + err(event->udev, "NAME=\"%s\" ignored, kernel device nodes " + "can not be renamed; please fix it in %s:%u\n", name, + &rules->buf[rule->rule.filename_off], rule->rule.filename_line); + break; + } + } + free(event->name); + event->name = strdup(name_str); + info(event->udev, "NAME '%s' %s:%u\n", + event->name, + &rules->buf[rule->rule.filename_off], + rule->rule.filename_line); + break; + } + case TK_A_DEVLINK: { + char temp[UTIL_PATH_SIZE]; + char filename[UTIL_PATH_SIZE]; + char *pos, *next; + int count = 0; + + if (event->devlink_final) + break; + if (major(udev_device_get_devnum(event->dev)) == 0) + break; + if (cur->key.op == OP_ASSIGN_FINAL) + event->devlink_final = true; + if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) + udev_device_cleanup_devlinks_list(event->dev); + + /* allow multiple symlinks separated by spaces */ + udev_event_apply_format(event, &rules->buf[cur->key.value_off], temp, sizeof(temp)); + if (esc == ESCAPE_UNSET) + count = util_replace_chars(temp, "/ "); + else if (esc == ESCAPE_REPLACE) + count = util_replace_chars(temp, "/"); + if (count > 0) + info(event->udev, "%i character(s) replaced\n" , count); + dbg(event->udev, "rule applied, added symlink(s) '%s'\n", temp); + pos = temp; + while (isspace(pos[0])) + pos++; + next = strchr(pos, ' '); + while (next != NULL) { + next[0] = '\0'; + info(event->udev, "LINK '%s' %s:%u\n", pos, + &rules->buf[rule->rule.filename_off], rule->rule.filename_line); + util_strscpyl(filename, sizeof(filename), udev_get_dev_path(event->udev), "/", pos, NULL); + udev_device_add_devlink(event->dev, filename, cur->key.devlink_unique); + while (isspace(next[1])) + next++; + pos = &next[1]; + next = strchr(pos, ' '); + } + if (pos[0] != '\0') { + info(event->udev, "LINK '%s' %s:%u\n", pos, + &rules->buf[rule->rule.filename_off], rule->rule.filename_line); + util_strscpyl(filename, sizeof(filename), udev_get_dev_path(event->udev), "/", pos, NULL); + udev_device_add_devlink(event->dev, filename, cur->key.devlink_unique); + } + break; + } + case TK_A_ATTR: { + const char *key_name = &rules->buf[cur->key.attr_off]; + char attr[UTIL_PATH_SIZE]; + char value[UTIL_NAME_SIZE]; + FILE *f; + + if (util_resolve_subsys_kernel(event->udev, key_name, attr, sizeof(attr), 0) != 0) + util_strscpyl(attr, sizeof(attr), udev_device_get_syspath(event->dev), "/", key_name, NULL); + attr_subst_subdir(attr, sizeof(attr)); + + udev_event_apply_format(event, &rules->buf[cur->key.value_off], value, sizeof(value)); + info(event->udev, "ATTR '%s' writing '%s' %s:%u\n", attr, value, + &rules->buf[rule->rule.filename_off], + rule->rule.filename_line); + f = fopen(attr, "w"); + if (f != NULL) { + if (fprintf(f, "%s", value) <= 0) + err(event->udev, "error writing ATTR{%s}: %m\n", attr); + fclose(f); + } else { + err(event->udev, "error opening ATTR{%s} for writing: %m\n", attr); + } + break; + } + case TK_A_RUN: { + if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) + udev_list_cleanup(&event->run_list); + info(event->udev, "RUN '%s' %s:%u\n", + &rules->buf[cur->key.value_off], + &rules->buf[rule->rule.filename_off], + rule->rule.filename_line); + udev_list_entry_add(&event->run_list, &rules->buf[cur->key.value_off], NULL); + break; + } + case TK_A_GOTO: + if (cur->key.rule_goto == 0) + break; + cur = &rules->tokens[cur->key.rule_goto]; + continue; + case TK_END: + return 0; + + case TK_M_PARENTS_MIN: + case TK_M_PARENTS_MAX: + case TK_M_MAX: + case TK_UNSET: + err(rules->udev, "wrong type %u\n", cur->type); + goto nomatch; + } + + cur++; + continue; + nomatch: + /* fast-forward to next rule */ + cur = rule + rule->rule.token_count; + dbg(rules->udev, "forward to rule: %u\n", + (unsigned int) (cur - rules->tokens)); + } +} + +void udev_rules_apply_static_dev_perms(struct udev_rules *rules) +{ + struct token *cur; + struct token *rule; + uid_t uid = 0; + gid_t gid = 0; + mode_t mode = 0; + + if (rules->tokens == NULL) + return; + + cur = &rules->tokens[0]; + rule = cur; + for (;;) { + switch (cur->type) { + case TK_RULE: + /* current rule */ + rule = cur; + + /* skip rules without a static_node tag */ + if (!rule->rule.has_static_node) + goto next; + + uid = 0; + gid = 0; + mode = 0; + break; + case TK_A_OWNER_ID: + uid = cur->key.uid; + break; + case TK_A_GROUP_ID: + gid = cur->key.gid; + break; + case TK_A_MODE_ID: + mode = cur->key.mode; + break; + case TK_A_STATIC_NODE: { + char filename[UTIL_PATH_SIZE]; + struct stat stats; + + /* we assure, that the permissions tokens are sorted before the static token */ + if (mode == 0 && uid == 0 && gid == 0) + goto next; + util_strscpyl(filename, sizeof(filename), udev_get_dev_path(rules->udev), "/", + &rules->buf[cur->key.value_off], NULL); + if (stat(filename, &stats) != 0) + goto next; + if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode)) + goto next; + if (mode == 0) { + if (gid > 0) + mode = 0660; + else + mode = 0600; + } + if (mode != (stats.st_mode & 01777)) { + chmod(filename, mode); + info(rules->udev, "chmod '%s' %#o\n", filename, mode); + } + + if ((uid != 0 && uid != stats.st_uid) || (gid != 0 && gid != stats.st_gid)) { + chown(filename, uid, gid); + info(rules->udev, "chown '%s' %u %u\n", filename, uid, gid); + } + + utimensat(AT_FDCWD, filename, NULL, 0); + break; + } + case TK_END: + return; + } + + cur++; + continue; +next: + /* fast-forward to next rule */ + cur = rule + rule->rule.token_count; + continue; + } +} diff --git a/src/udev/udev-watch.c b/src/udev/udev-watch.c new file mode 100644 index 0000000000..228d18fedf --- /dev/null +++ b/src/udev/udev-watch.c @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2004-2010 Kay Sievers + * Copyright (C) 2009 Canonical Ltd. + * Copyright (C) 2009 Scott James Remnant + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +static int inotify_fd = -1; + +/* inotify descriptor, will be shared with rules directory; + * set to cloexec since we need our children to be able to add + * watches for us + */ +int udev_watch_init(struct udev *udev) +{ + inotify_fd = inotify_init1(IN_CLOEXEC); + if (inotify_fd < 0) + err(udev, "inotify_init failed: %m\n"); + return inotify_fd; +} + +/* move any old watches directory out of the way, and then restore + * the watches + */ +void udev_watch_restore(struct udev *udev) +{ + char filename[UTIL_PATH_SIZE], oldname[UTIL_PATH_SIZE]; + + if (inotify_fd < 0) + return; + + util_strscpyl(oldname, sizeof(oldname), udev_get_run_path(udev), "/watch.old", NULL); + util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/watch", NULL); + if (rename(filename, oldname) == 0) { + DIR *dir; + struct dirent *ent; + + dir = opendir(oldname); + if (dir == NULL) { + err(udev, "unable to open old watches dir '%s', old watches will not be restored: %m", oldname); + return; + } + + for (ent = readdir(dir); ent != NULL; ent = readdir(dir)) { + char device[UTIL_PATH_SIZE]; + char *s; + size_t l; + ssize_t len; + struct udev_device *dev; + + if (ent->d_name[0] == '.') + continue; + + s = device; + l = util_strpcpy(&s, sizeof(device), udev_get_sys_path(udev)); + len = readlinkat(dirfd(dir), ent->d_name, s, l); + if (len <= 0 || len == (ssize_t)l) + goto unlink; + s[len] = '\0'; + + dev = udev_device_new_from_id_filename(udev, s); + if (dev == NULL) + goto unlink; + + info(udev, "restoring old watch on '%s'\n", udev_device_get_devnode(dev)); + udev_watch_begin(udev, dev); + udev_device_unref(dev); +unlink: + unlinkat(dirfd(dir), ent->d_name, 0); + } + + closedir(dir); + rmdir(oldname); + + } else if (errno != ENOENT) { + err(udev, "unable to move watches dir '%s', old watches will not be restored: %m", filename); + } +} + +void udev_watch_begin(struct udev *udev, struct udev_device *dev) +{ + char filename[UTIL_PATH_SIZE]; + int wd; + + if (inotify_fd < 0) + return; + + info(udev, "adding watch on '%s'\n", udev_device_get_devnode(dev)); + wd = inotify_add_watch(inotify_fd, udev_device_get_devnode(dev), IN_CLOSE_WRITE); + if (wd < 0) { + err(udev, "inotify_add_watch(%d, %s, %o) failed: %m\n", + inotify_fd, udev_device_get_devnode(dev), IN_CLOSE_WRITE); + return; + } + + snprintf(filename, sizeof(filename), "%s/watch/%d", udev_get_run_path(udev), wd); + util_create_path(udev, filename); + unlink(filename); + symlink(udev_device_get_id_filename(dev), filename); + + udev_device_set_watch_handle(dev, wd); +} + +void udev_watch_end(struct udev *udev, struct udev_device *dev) +{ + int wd; + char filename[UTIL_PATH_SIZE]; + + if (inotify_fd < 0) + return; + + wd = udev_device_get_watch_handle(dev); + if (wd < 0) + return; + + info(udev, "removing watch on '%s'\n", udev_device_get_devnode(dev)); + inotify_rm_watch(inotify_fd, wd); + + snprintf(filename, sizeof(filename), "%s/watch/%d", udev_get_run_path(udev), wd); + unlink(filename); + + udev_device_set_watch_handle(dev, -1); +} + +struct udev_device *udev_watch_lookup(struct udev *udev, int wd) +{ + char filename[UTIL_PATH_SIZE]; + char majmin[UTIL_PATH_SIZE]; + char *s; + size_t l; + ssize_t len; + + if (inotify_fd < 0 || wd < 0) + return NULL; + + snprintf(filename, sizeof(filename), "%s/watch/%d", udev_get_run_path(udev), wd); + s = majmin; + l = util_strpcpy(&s, sizeof(majmin), udev_get_sys_path(udev)); + len = readlink(filename, s, l); + if (len <= 0 || (size_t)len == l) + return NULL; + s[len] = '\0'; + + return udev_device_new_from_id_filename(udev, s); +} diff --git a/src/udev/udev.conf b/src/udev/udev.conf new file mode 100644 index 0000000000..f39253eb67 --- /dev/null +++ b/src/udev/udev.conf @@ -0,0 +1,3 @@ +# see udev(7) for details + +#udev_log="info" diff --git a/src/udev/udev.h b/src/udev/udev.h new file mode 100644 index 0000000000..bc051c9b65 --- /dev/null +++ b/src/udev/udev.h @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2003 Greg Kroah-Hartman + * Copyright (C) 2003-2010 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _UDEV_H_ +#define _UDEV_H_ + +#include +#include +#include + +#include "libudev.h" +#include "libudev-private.h" + +struct udev_event { + struct udev *udev; + struct udev_device *dev; + struct udev_device *dev_parent; + struct udev_device *dev_db; + char *name; + char *program_result; + mode_t mode; + uid_t uid; + gid_t gid; + struct udev_list run_list; + int exec_delay; + unsigned long long birth_usec; + unsigned long long timeout_usec; + int fd_signal; + unsigned int builtin_run; + unsigned int builtin_ret; + bool sigterm; + bool inotify_watch; + bool inotify_watch_final; + bool group_final; + bool owner_final; + bool mode_set; + bool mode_final; + bool name_final; + bool devlink_final; + bool run_final; +}; + +struct udev_watch { + struct udev_list_node node; + int handle; + char *name; +}; + +/* udev-rules.c */ +struct udev_rules; +struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names); +struct udev_rules *udev_rules_unref(struct udev_rules *rules); +int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event, const sigset_t *sigmask); +void udev_rules_apply_static_dev_perms(struct udev_rules *rules); + +/* udev-event.c */ +struct udev_event *udev_event_new(struct udev_device *dev); +void udev_event_unref(struct udev_event *event); +size_t udev_event_apply_format(struct udev_event *event, const char *src, char *dest, size_t size); +int udev_event_apply_subsys_kernel(struct udev_event *event, const char *string, + char *result, size_t maxsize, int read_value); +int udev_event_spawn(struct udev_event *event, + const char *cmd, char **envp, const sigset_t *sigmask, + char *result, size_t ressize); +int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules, const sigset_t *sigset); +int udev_event_execute_run(struct udev_event *event, const sigset_t *sigset); +int udev_build_argv(struct udev *udev, char *cmd, int *argc, char *argv[]); + +/* udev-watch.c */ +int udev_watch_init(struct udev *udev); +void udev_watch_restore(struct udev *udev); +void udev_watch_begin(struct udev *udev, struct udev_device *dev); +void udev_watch_end(struct udev *udev, struct udev_device *dev); +struct udev_device *udev_watch_lookup(struct udev *udev, int wd); + +/* udev-node.c */ +void udev_node_add(struct udev_device *dev, mode_t mode, uid_t uid, gid_t gid); +void udev_node_remove(struct udev_device *dev); +void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old); + +/* udev-ctrl.c */ +struct udev_ctrl; +struct udev_ctrl *udev_ctrl_new(struct udev *udev); +struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd); +int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl); +struct udev_ctrl *udev_ctrl_ref(struct udev_ctrl *uctrl); +struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl); +int udev_ctrl_cleanup(struct udev_ctrl *uctrl); +struct udev *udev_ctrl_get_udev(struct udev_ctrl *uctrl); +int udev_ctrl_get_fd(struct udev_ctrl *uctrl); +int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, int timeout); +int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, int timeout); +int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, int timeout); +int udev_ctrl_send_reload(struct udev_ctrl *uctrl, int timeout); +int udev_ctrl_send_ping(struct udev_ctrl *uctrl, int timeout); +int udev_ctrl_send_exit(struct udev_ctrl *uctrl, int timeout); +int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, int timeout); +int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, int timeout); +struct udev_ctrl_connection; +struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl); +struct udev_ctrl_connection *udev_ctrl_connection_ref(struct udev_ctrl_connection *conn); +struct udev_ctrl_connection *udev_ctrl_connection_unref(struct udev_ctrl_connection *conn); +struct udev_ctrl_msg; +struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn); +struct udev_ctrl_msg *udev_ctrl_msg_ref(struct udev_ctrl_msg *ctrl_msg); +struct udev_ctrl_msg *udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg); +int udev_ctrl_get_set_log_level(struct udev_ctrl_msg *ctrl_msg); +int udev_ctrl_get_stop_exec_queue(struct udev_ctrl_msg *ctrl_msg); +int udev_ctrl_get_start_exec_queue(struct udev_ctrl_msg *ctrl_msg); +int udev_ctrl_get_reload(struct udev_ctrl_msg *ctrl_msg); +int udev_ctrl_get_ping(struct udev_ctrl_msg *ctrl_msg); +int udev_ctrl_get_exit(struct udev_ctrl_msg *ctrl_msg); +const char *udev_ctrl_get_set_env(struct udev_ctrl_msg *ctrl_msg); +int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg); + +/* built-in commands */ +enum udev_builtin_cmd { + UDEV_BUILTIN_BLKID, + UDEV_BUILTIN_FIRMWARE, + UDEV_BUILTIN_INPUT_ID, + UDEV_BUILTIN_KMOD, + UDEV_BUILTIN_PATH_ID, + UDEV_BUILTIN_PCI_DB, + UDEV_BUILTIN_USB_DB, + UDEV_BUILTIN_USB_ID, + UDEV_BUILTIN_MAX +}; +struct udev_builtin { + const char *name; + int (*cmd)(struct udev_device *dev, int argc, char *argv[], bool test); + const char *help; + int (*init)(struct udev *udev); + void (*exit)(struct udev *udev); + bool (*validate)(struct udev *udev); + bool run_once; +}; +extern const struct udev_builtin udev_builtin_blkid; +extern const struct udev_builtin udev_builtin_firmware; +extern const struct udev_builtin udev_builtin_input_id; +extern const struct udev_builtin udev_builtin_kmod; +extern const struct udev_builtin udev_builtin_path_id; +extern const struct udev_builtin udev_builtin_pci_db; +extern const struct udev_builtin udev_builtin_usb_db; +extern const struct udev_builtin udev_builtin_usb_id; +int udev_builtin_init(struct udev *udev); +void udev_builtin_exit(struct udev *udev); +enum udev_builtin_cmd udev_builtin_lookup(const char *command); +const char *udev_builtin_name(enum udev_builtin_cmd cmd); +bool udev_builtin_run_once(enum udev_builtin_cmd cmd); +int udev_builtin_run(struct udev_device *dev, enum udev_builtin_cmd cmd, const char *command, bool test); +void udev_builtin_list(struct udev *udev); +int udev_builtin_add_property(struct udev_device *dev, bool test, const char *key, const char *val); + +/* udev logging */ +void udev_main_log(struct udev *udev, int priority, + const char *file, int line, const char *fn, + const char *format, va_list args); + +/* udevadm commands */ +struct udevadm_cmd { + const char *name; + int (*cmd)(struct udev *udev, int argc, char *argv[]); + const char *help; + int debug; +}; +extern const struct udevadm_cmd udevadm_info; +extern const struct udevadm_cmd udevadm_trigger; +extern const struct udevadm_cmd udevadm_settle; +extern const struct udevadm_cmd udevadm_control; +extern const struct udevadm_cmd udevadm_monitor; +extern const struct udevadm_cmd udevadm_test; +extern const struct udevadm_cmd udevadm_test_builtin; +#endif diff --git a/src/udev/udev.pc.in b/src/udev/udev.pc.in new file mode 100644 index 0000000000..0b04c02ef6 --- /dev/null +++ b/src/udev/udev.pc.in @@ -0,0 +1,5 @@ +Name: udev +Description: udev +Version: @VERSION@ + +udevdir=@pkglibexecdir@ diff --git a/src/udev/udevadm-control.c b/src/udev/udevadm-control.c new file mode 100644 index 0000000000..cafa214944 --- /dev/null +++ b/src/udev/udevadm-control.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2005-2011 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +static void print_help(void) +{ + printf("Usage: udevadm control COMMAND\n" + " --exit instruct the daemon to cleanup and exit\n" + " --log-priority= set the udev log level for the daemon\n" + " --stop-exec-queue do not execute events, queue only\n" + " --start-exec-queue execute events, flush queue\n" + " --reload reload rules and databases\n" + " --property== set a global property for all events\n" + " --children-max= maximum number of children\n" + " --timeout= maximum time to block for a reply\n" + " --help print this help text\n\n"); +} + +static int adm_control(struct udev *udev, int argc, char *argv[]) +{ + struct udev_ctrl *uctrl = NULL; + int timeout = 60; + int rc = 1; + + static const struct option options[] = { + { "exit", no_argument, NULL, 'e' }, + { "log-priority", required_argument, NULL, 'l' }, + { "stop-exec-queue", no_argument, NULL, 's' }, + { "start-exec-queue", no_argument, NULL, 'S' }, + { "reload", no_argument, NULL, 'R' }, + { "reload-rules", no_argument, NULL, 'R' }, + { "property", required_argument, NULL, 'p' }, + { "env", required_argument, NULL, 'p' }, + { "children-max", required_argument, NULL, 'm' }, + { "timeout", required_argument, NULL, 't' }, + { "help", no_argument, NULL, 'h' }, + {} + }; + + if (getuid() != 0) { + fprintf(stderr, "root privileges required\n"); + return 1; + } + + uctrl = udev_ctrl_new(udev); + if (uctrl == NULL) + return 2; + + for (;;) { + int option; + + option = getopt_long(argc, argv, "el:sSRp:m:h", options, NULL); + if (option == -1) + break; + + switch (option) { + case 'e': + if (udev_ctrl_send_exit(uctrl, timeout) < 0) + rc = 2; + else + rc = 0; + break; + case 'l': { + int i; + + i = util_log_priority(optarg); + if (i < 0) { + fprintf(stderr, "invalid number '%s'\n", optarg); + goto out; + } + if (udev_ctrl_send_set_log_level(uctrl, util_log_priority(optarg), timeout) < 0) + rc = 2; + else + rc = 0; + break; + } + case 's': + if (udev_ctrl_send_stop_exec_queue(uctrl, timeout) < 0) + rc = 2; + else + rc = 0; + break; + case 'S': + if (udev_ctrl_send_start_exec_queue(uctrl, timeout) < 0) + rc = 2; + else + rc = 0; + break; + case 'R': + if (udev_ctrl_send_reload(uctrl, timeout) < 0) + rc = 2; + else + rc = 0; + break; + case 'p': + if (strchr(optarg, '=') == NULL) { + fprintf(stderr, "expect = instead of '%s'\n", optarg); + goto out; + } + if (udev_ctrl_send_set_env(uctrl, optarg, timeout) < 0) + rc = 2; + else + rc = 0; + break; + case 'm': { + char *endp; + int i; + + i = strtoul(optarg, &endp, 0); + if (endp[0] != '\0' || i < 1) { + fprintf(stderr, "invalid number '%s'\n", optarg); + goto out; + } + if (udev_ctrl_send_set_children_max(uctrl, i, timeout) < 0) + rc = 2; + else + rc = 0; + break; + } + case 't': { + int seconds; + + seconds = atoi(optarg); + if (seconds >= 0) + timeout = seconds; + else + fprintf(stderr, "invalid timeout value\n"); + break; + } + case 'h': + print_help(); + rc = 0; + break; + } + } + + if (argv[optind] != NULL) + fprintf(stderr, "unknown option\n"); + else if (optind == 1) + fprintf(stderr, "missing option\n"); +out: + udev_ctrl_unref(uctrl); + return rc; +} + +const struct udevadm_cmd udevadm_control = { + .name = "control", + .cmd = adm_control, + .help = "control the udev daemon", +}; diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c new file mode 100644 index 0000000000..ee9b59fea8 --- /dev/null +++ b/src/udev/udevadm-info.c @@ -0,0 +1,568 @@ +/* + * Copyright (C) 2004-2009 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +static bool skip_attribute(const char *name) +{ + static const char const *skip[] = { + "uevent", + "dev", + "modalias", + "resource", + "driver", + "subsystem", + "module", + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(skip); i++) + if (strcmp(name, skip[i]) == 0) + return true; + return false; +} + +static void print_all_attributes(struct udev_device *device, const char *key) +{ + struct udev *udev = udev_device_get_udev(device); + struct udev_list_entry *sysattr; + + udev_list_entry_foreach(sysattr, udev_device_get_sysattr_list_entry(device)) { + const char *name; + const char *value; + size_t len; + + name = udev_list_entry_get_name(sysattr); + if (skip_attribute(name)) + continue; + + value = udev_device_get_sysattr_value(device, name); + if (value == NULL) + continue; + dbg(udev, "attr '%s'='%s'\n", name, value); + + /* skip any values that look like a path */ + if (value[0] == '/') + continue; + + /* skip nonprintable attributes */ + len = strlen(value); + while (len > 0 && isprint(value[len-1])) + len--; + if (len > 0) { + dbg(udev, "attribute value of '%s' non-printable, skip\n", name); + continue; + } + + printf(" %s{%s}==\"%s\"\n", key, name, value); + } + printf("\n"); +} + +static int print_device_chain(struct udev_device *device) +{ + struct udev_device *device_parent; + const char *str; + + printf("\n" + "Udevadm info starts with the device specified by the devpath and then\n" + "walks up the chain of parent devices. It prints for every device\n" + "found, all possible attributes in the udev rules key format.\n" + "A rule to match, can be composed by the attributes of the device\n" + "and the attributes from one single parent device.\n" + "\n"); + + printf(" looking at device '%s':\n", udev_device_get_devpath(device)); + printf(" KERNEL==\"%s\"\n", udev_device_get_sysname(device)); + str = udev_device_get_subsystem(device); + if (str == NULL) + str = ""; + printf(" SUBSYSTEM==\"%s\"\n", str); + str = udev_device_get_driver(device); + if (str == NULL) + str = ""; + printf(" DRIVER==\"%s\"\n", str); + print_all_attributes(device, "ATTR"); + + device_parent = device; + do { + device_parent = udev_device_get_parent(device_parent); + if (device_parent == NULL) + break; + printf(" looking at parent device '%s':\n", udev_device_get_devpath(device_parent)); + printf(" KERNELS==\"%s\"\n", udev_device_get_sysname(device_parent)); + str = udev_device_get_subsystem(device_parent); + if (str == NULL) + str = ""; + printf(" SUBSYSTEMS==\"%s\"\n", str); + str = udev_device_get_driver(device_parent); + if (str == NULL) + str = ""; + printf(" DRIVERS==\"%s\"\n", str); + print_all_attributes(device_parent, "ATTRS"); + } while (device_parent != NULL); + + return 0; +} + +static void print_record(struct udev_device *device) +{ + size_t len; + const char *str; + int i; + struct udev_list_entry *list_entry; + + printf("P: %s\n", udev_device_get_devpath(device)); + + len = strlen(udev_get_dev_path(udev_device_get_udev(device))); + str = udev_device_get_devnode(device); + if (str != NULL) + printf("N: %s\n", &str[len+1]); + + i = udev_device_get_devlink_priority(device); + if (i != 0) + printf("L: %i\n", i); + + udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device)) { + len = strlen(udev_get_dev_path(udev_device_get_udev(device))); + printf("S: %s\n", &udev_list_entry_get_name(list_entry)[len+1]); + } + + udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device)) + printf("E: %s=%s\n", + udev_list_entry_get_name(list_entry), + udev_list_entry_get_value(list_entry)); + printf("\n"); +} + +static int stat_device(const char *name, bool export, const char *prefix) +{ + struct stat statbuf; + + if (stat(name, &statbuf) != 0) + return -1; + + if (export) { + if (prefix == NULL) + prefix = "INFO_"; + printf("%sMAJOR=%d\n" + "%sMINOR=%d\n", + prefix, major(statbuf.st_dev), + prefix, minor(statbuf.st_dev)); + } else + printf("%d:%d\n", major(statbuf.st_dev), minor(statbuf.st_dev)); + return 0; +} + +static int export_devices(struct udev *udev) +{ + struct udev_enumerate *udev_enumerate; + struct udev_list_entry *list_entry; + + udev_enumerate = udev_enumerate_new(udev); + if (udev_enumerate == NULL) + return -1; + udev_enumerate_scan_devices(udev_enumerate); + udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) { + struct udev_device *device; + + device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry)); + if (device != NULL) { + print_record(device); + udev_device_unref(device); + } + } + udev_enumerate_unref(udev_enumerate); + return 0; +} + +static void cleanup_dir(DIR *dir, mode_t mask, int depth) +{ + struct dirent *dent; + + if (depth <= 0) + return; + + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + struct stat stats; + + if (dent->d_name[0] == '.') + continue; + if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0) + continue; + if ((stats.st_mode & mask) != 0) + continue; + if (S_ISDIR(stats.st_mode)) { + DIR *dir2; + + dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)); + if (dir2 != NULL) { + cleanup_dir(dir2, mask, depth-1); + closedir(dir2); + } + unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR); + } else { + unlinkat(dirfd(dir), dent->d_name, 0); + } + } +} + +static void cleanup_db(struct udev *udev) +{ + char filename[UTIL_PATH_SIZE]; + DIR *dir; + + util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/queue.bin", NULL); + unlink(filename); + + util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/data", NULL); + dir = opendir(filename); + if (dir != NULL) { + cleanup_dir(dir, S_ISVTX, 1); + closedir(dir); + } + + util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/links", NULL); + dir = opendir(filename); + if (dir != NULL) { + cleanup_dir(dir, 0, 2); + closedir(dir); + } + + util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/tags", NULL); + dir = opendir(filename); + if (dir != NULL) { + cleanup_dir(dir, 0, 2); + closedir(dir); + } + + util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/watch", NULL); + dir = opendir(filename); + if (dir != NULL) { + cleanup_dir(dir, 0, 1); + closedir(dir); + } + + util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/firmware-missing", NULL); + dir = opendir(filename); + if (dir != NULL) { + cleanup_dir(dir, 0, 1); + closedir(dir); + } +} + +static int uinfo(struct udev *udev, int argc, char *argv[]) +{ + struct udev_device *device = NULL; + bool root = 0; + bool export = 0; + const char *export_prefix = NULL; + char path[UTIL_PATH_SIZE]; + char name[UTIL_PATH_SIZE]; + struct udev_list_entry *list_entry; + int rc = 0; + + static const struct option options[] = { + { "name", required_argument, NULL, 'n' }, + { "path", required_argument, NULL, 'p' }, + { "query", required_argument, NULL, 'q' }, + { "attribute-walk", no_argument, NULL, 'a' }, + { "cleanup-db", no_argument, NULL, 'c' }, + { "export-db", no_argument, NULL, 'e' }, + { "root", no_argument, NULL, 'r' }, + { "run", no_argument, NULL, 'R' }, + { "device-id-of-file", required_argument, NULL, 'd' }, + { "export", no_argument, NULL, 'x' }, + { "export-prefix", required_argument, NULL, 'P' }, + { "version", no_argument, NULL, 'V' }, + { "help", no_argument, NULL, 'h' }, + {} + }; + + enum action_type { + ACTION_NONE, + ACTION_QUERY, + ACTION_ATTRIBUTE_WALK, + ACTION_ROOT, + ACTION_DEVICE_ID_FILE, + } action = ACTION_NONE; + + enum query_type { + QUERY_NONE, + QUERY_NAME, + QUERY_PATH, + QUERY_SYMLINK, + QUERY_PROPERTY, + QUERY_ALL, + } query = QUERY_NONE; + + for (;;) { + int option; + struct stat statbuf; + + option = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL); + if (option == -1) + break; + + dbg(udev, "option '%c'\n", option); + switch (option) { + case 'n': + if (device != NULL) { + fprintf(stderr, "device already specified\n"); + rc = 2; + goto exit; + } + /* remove /dev if given */ + if (strncmp(optarg, udev_get_dev_path(udev), strlen(udev_get_dev_path(udev))) != 0) + util_strscpyl(name, sizeof(name), udev_get_dev_path(udev), "/", optarg, NULL); + else + util_strscpy(name, sizeof(name), optarg); + util_remove_trailing_chars(name, '/'); + if (stat(name, &statbuf) < 0) { + fprintf(stderr, "device node not found\n"); + rc = 2; + goto exit; + } else { + char type; + + if (S_ISBLK(statbuf.st_mode)) { + type = 'b'; + } else if (S_ISCHR(statbuf.st_mode)) { + type = 'c'; + } else { + fprintf(stderr, "device node has wrong file type\n"); + rc = 2; + goto exit; + } + device = udev_device_new_from_devnum(udev, type, statbuf.st_rdev); + if (device == NULL) { + fprintf(stderr, "device node not found\n"); + rc = 2; + goto exit; + } + } + break; + case 'p': + if (device != NULL) { + fprintf(stderr, "device already specified\n"); + rc = 2; + goto exit; + } + /* add sys dir if needed */ + if (strncmp(optarg, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) != 0) + util_strscpyl(path, sizeof(path), udev_get_sys_path(udev), optarg, NULL); + else + util_strscpy(path, sizeof(path), optarg); + util_remove_trailing_chars(path, '/'); + device = udev_device_new_from_syspath(udev, path); + if (device == NULL) { + fprintf(stderr, "device path not found\n"); + rc = 2; + goto exit; + } + break; + case 'q': + action = ACTION_QUERY; + if (strcmp(optarg, "property") == 0 || strcmp(optarg, "env") == 0) { + query = QUERY_PROPERTY; + } else if (strcmp(optarg, "name") == 0) { + query = QUERY_NAME; + } else if (strcmp(optarg, "symlink") == 0) { + query = QUERY_SYMLINK; + } else if (strcmp(optarg, "path") == 0) { + query = QUERY_PATH; + } else if (strcmp(optarg, "all") == 0) { + query = QUERY_ALL; + } else { + fprintf(stderr, "unknown query type\n"); + rc = 3; + goto exit; + } + break; + case 'r': + if (action == ACTION_NONE) + action = ACTION_ROOT; + root = true; + break; + case 'R': + printf("%s\n", udev_get_run_path(udev)); + goto exit; + case 'd': + action = ACTION_DEVICE_ID_FILE; + util_strscpy(name, sizeof(name), optarg); + break; + case 'a': + action = ACTION_ATTRIBUTE_WALK; + break; + case 'e': + export_devices(udev); + goto exit; + case 'c': + cleanup_db(udev); + goto exit; + case 'x': + export = true; + break; + case 'P': + export_prefix = optarg; + break; + case 'V': + printf("%s\n", VERSION); + goto exit; + case 'h': + printf("Usage: udevadm info OPTIONS\n" + " --query= query device information:\n" + " name name of device node\n" + " symlink pointing to node\n" + " path sys device path\n" + " property the device properties\n" + " all all values\n" + " --path= sys device path used for query or attribute walk\n" + " --name= node or symlink name used for query or attribute walk\n" + " --root prepend dev directory to path names\n" + " --attribute-walk print all key matches while walking along the chain\n" + " of parent devices\n" + " --device-id-of-file= print major:minor of device containing this file\n" + " --export export key/value pairs\n" + " --export-prefix export the key name with a prefix\n" + " --export-db export the content of the udev database\n" + " --cleanup-db cleanup the udev database\n" + " --help\n\n"); + goto exit; + default: + rc = 1; + goto exit; + } + } + + switch (action) { + case ACTION_QUERY: + if (device == NULL) { + fprintf(stderr, "query needs a valid device specified by --path= or --name=\n"); + rc = 4; + goto exit; + } + + switch(query) { + case QUERY_NAME: { + const char *node = udev_device_get_devnode(device); + + if (node == NULL) { + fprintf(stderr, "no device node found\n"); + rc = 5; + goto exit; + } + + if (root) { + printf("%s\n", udev_device_get_devnode(device)); + } else { + size_t len = strlen(udev_get_dev_path(udev)); + + printf("%s\n", &udev_device_get_devnode(device)[len+1]); + } + break; + } + case QUERY_SYMLINK: + list_entry = udev_device_get_devlinks_list_entry(device); + while (list_entry != NULL) { + if (root) { + printf("%s", udev_list_entry_get_name(list_entry)); + } else { + size_t len; + + len = strlen(udev_get_dev_path(udev_device_get_udev(device))); + printf("%s", &udev_list_entry_get_name(list_entry)[len+1]); + } + list_entry = udev_list_entry_get_next(list_entry); + if (list_entry != NULL) + printf(" "); + } + printf("\n"); + break; + case QUERY_PATH: + printf("%s\n", udev_device_get_devpath(device)); + goto exit; + case QUERY_PROPERTY: + list_entry = udev_device_get_properties_list_entry(device); + while (list_entry != NULL) { + if (export) { + const char *prefix = export_prefix; + + if (prefix == NULL) + prefix = ""; + printf("%s%s='%s'\n", prefix, + udev_list_entry_get_name(list_entry), + udev_list_entry_get_value(list_entry)); + } else { + printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry)); + } + list_entry = udev_list_entry_get_next(list_entry); + } + break; + case QUERY_ALL: + print_record(device); + break; + default: + fprintf(stderr, "unknown query type\n"); + break; + } + break; + case ACTION_ATTRIBUTE_WALK: + if (device == NULL) { + fprintf(stderr, "query needs a valid device specified by --path= or --name=\n"); + rc = 4; + goto exit; + } + print_device_chain(device); + break; + case ACTION_DEVICE_ID_FILE: + if (stat_device(name, export, export_prefix) != 0) + rc = 1; + break; + case ACTION_ROOT: + printf("%s\n", udev_get_dev_path(udev)); + break; + default: + fprintf(stderr, "missing option\n"); + rc = 1; + break; + } + +exit: + udev_device_unref(device); + return rc; +} + +const struct udevadm_cmd udevadm_info = { + .name = "info", + .cmd = uinfo, + .help = "query sysfs or the udev database", +}; diff --git a/src/udev/udevadm-monitor.c b/src/udev/udevadm-monitor.c new file mode 100644 index 0000000000..5997dd8e18 --- /dev/null +++ b/src/udev/udevadm-monitor.c @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2004-2010 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +static bool udev_exit; + +static void sig_handler(int signum) +{ + if (signum == SIGINT || signum == SIGTERM) + udev_exit = true; +} + +static void print_device(struct udev_device *device, const char *source, int prop) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + printf("%-6s[%llu.%06u] %-8s %s (%s)\n", + source, + (unsigned long long) ts.tv_sec, (unsigned int) ts.tv_nsec/1000, + udev_device_get_action(device), + udev_device_get_devpath(device), + udev_device_get_subsystem(device)); + if (prop) { + struct udev_list_entry *list_entry; + + udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device)) + printf("%s=%s\n", + udev_list_entry_get_name(list_entry), + udev_list_entry_get_value(list_entry)); + printf("\n"); + } +} + +static int adm_monitor(struct udev *udev, int argc, char *argv[]) +{ + struct sigaction act; + sigset_t mask; + int option; + bool prop = false; + bool print_kernel = false; + bool print_udev = false; + struct udev_list subsystem_match_list; + struct udev_list tag_match_list; + struct udev_monitor *udev_monitor = NULL; + struct udev_monitor *kernel_monitor = NULL; + int fd_ep = -1; + int fd_kernel = -1, fd_udev = -1; + struct epoll_event ep_kernel, ep_udev; + int rc = 0; + + static const struct option options[] = { + { "property", no_argument, NULL, 'p' }, + { "environment", no_argument, NULL, 'e' }, + { "kernel", no_argument, NULL, 'k' }, + { "udev", no_argument, NULL, 'u' }, + { "subsystem-match", required_argument, NULL, 's' }, + { "tag-match", required_argument, NULL, 't' }, + { "help", no_argument, NULL, 'h' }, + {} + }; + + udev_list_init(udev, &subsystem_match_list, true); + udev_list_init(udev, &tag_match_list, true); + + for (;;) { + option = getopt_long(argc, argv, "pekus:t:h", options, NULL); + if (option == -1) + break; + + switch (option) { + case 'p': + case 'e': + prop = true; + break; + case 'k': + print_kernel = true; + break; + case 'u': + print_udev = true; + break; + case 's': + { + char subsys[UTIL_NAME_SIZE]; + char *devtype; + + util_strscpy(subsys, sizeof(subsys), optarg); + devtype = strchr(subsys, '/'); + if (devtype != NULL) { + devtype[0] = '\0'; + devtype++; + } + udev_list_entry_add(&subsystem_match_list, subsys, devtype); + break; + } + case 't': + udev_list_entry_add(&tag_match_list, optarg, NULL); + break; + case 'h': + printf("Usage: udevadm monitor [--property] [--kernel] [--udev] [--help]\n" + " --property print the event properties\n" + " --kernel print kernel uevents\n" + " --udev print udev events\n" + " --subsystem-match= filter events by subsystem\n" + " --tag-match= filter events by tag\n" + " --help\n\n"); + goto out; + default: + rc = 1; + goto out; + } + } + + if (!print_kernel && !print_udev) { + print_kernel = true; + print_udev = true; + } + + /* set signal handlers */ + memset(&act, 0x00, sizeof(struct sigaction)); + act.sa_handler = sig_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + sigaction(SIGINT, &act, NULL); + sigaction(SIGTERM, &act, NULL); + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + sigprocmask(SIG_UNBLOCK, &mask, NULL); + + fd_ep = epoll_create1(EPOLL_CLOEXEC); + if (fd_ep < 0) { + err(udev, "error creating epoll fd: %m\n"); + goto out; + } + + printf("monitor will print the received events for:\n"); + if (print_udev) { + struct udev_list_entry *entry; + + udev_monitor = udev_monitor_new_from_netlink(udev, "udev"); + if (udev_monitor == NULL) { + fprintf(stderr, "error: unable to create netlink socket\n"); + rc = 1; + goto out; + } + udev_monitor_set_receive_buffer_size(udev_monitor, 128*1024*1024); + fd_udev = udev_monitor_get_fd(udev_monitor); + + udev_list_entry_foreach(entry, udev_list_get_entry(&subsystem_match_list)) { + const char *subsys = udev_list_entry_get_name(entry); + const char *devtype = udev_list_entry_get_value(entry); + + if (udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, subsys, devtype) < 0) + fprintf(stderr, "error: unable to apply subsystem filter '%s'\n", subsys); + } + + udev_list_entry_foreach(entry, udev_list_get_entry(&tag_match_list)) { + const char *tag = udev_list_entry_get_name(entry); + + if (udev_monitor_filter_add_match_tag(udev_monitor, tag) < 0) + fprintf(stderr, "error: unable to apply tag filter '%s'\n", tag); + } + + if (udev_monitor_enable_receiving(udev_monitor) < 0) { + fprintf(stderr, "error: unable to subscribe to udev events\n"); + rc = 2; + goto out; + } + + memset(&ep_udev, 0, sizeof(struct epoll_event)); + ep_udev.events = EPOLLIN; + ep_udev.data.fd = fd_udev; + if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_udev, &ep_udev) < 0) { + err(udev, "fail to add fd to epoll: %m\n"); + goto out; + } + + printf("UDEV - the event which udev sends out after rule processing\n"); + } + + if (print_kernel) { + struct udev_list_entry *entry; + + kernel_monitor = udev_monitor_new_from_netlink(udev, "kernel"); + if (kernel_monitor == NULL) { + fprintf(stderr, "error: unable to create netlink socket\n"); + rc = 3; + goto out; + } + udev_monitor_set_receive_buffer_size(kernel_monitor, 128*1024*1024); + fd_kernel = udev_monitor_get_fd(kernel_monitor); + + udev_list_entry_foreach(entry, udev_list_get_entry(&subsystem_match_list)) { + const char *subsys = udev_list_entry_get_name(entry); + + if (udev_monitor_filter_add_match_subsystem_devtype(kernel_monitor, subsys, NULL) < 0) + fprintf(stderr, "error: unable to apply subsystem filter '%s'\n", subsys); + } + + if (udev_monitor_enable_receiving(kernel_monitor) < 0) { + fprintf(stderr, "error: unable to subscribe to kernel events\n"); + rc = 4; + goto out; + } + + memset(&ep_kernel, 0, sizeof(struct epoll_event)); + ep_kernel.events = EPOLLIN; + ep_kernel.data.fd = fd_kernel; + if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_kernel, &ep_kernel) < 0) { + err(udev, "fail to add fd to epoll: %m\n"); + goto out; + } + + printf("KERNEL - the kernel uevent\n"); + } + printf("\n"); + + while (!udev_exit) { + int fdcount; + struct epoll_event ev[4]; + int i; + + fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), -1); + if (fdcount < 0) { + if (errno != EINTR) + fprintf(stderr, "error receiving uevent message: %m\n"); + continue; + } + + for (i = 0; i < fdcount; i++) { + if (ev[i].data.fd == fd_kernel && ev[i].events & EPOLLIN) { + struct udev_device *device; + + device = udev_monitor_receive_device(kernel_monitor); + if (device == NULL) + continue; + print_device(device, "KERNEL", prop); + udev_device_unref(device); + } else if (ev[i].data.fd == fd_udev && ev[i].events & EPOLLIN) { + struct udev_device *device; + + device = udev_monitor_receive_device(udev_monitor); + if (device == NULL) + continue; + print_device(device, "UDEV", prop); + udev_device_unref(device); + } + } + } +out: + if (fd_ep >= 0) + close(fd_ep); + udev_monitor_unref(udev_monitor); + udev_monitor_unref(kernel_monitor); + udev_list_cleanup(&subsystem_match_list); + udev_list_cleanup(&tag_match_list); + return rc; +} + +const struct udevadm_cmd udevadm_monitor = { + .name = "monitor", + .cmd = adm_monitor, + .help = "listen to kernel and udev events", +}; diff --git a/src/udev/udevadm-settle.c b/src/udev/udevadm-settle.c new file mode 100644 index 0000000000..b168defd90 --- /dev/null +++ b/src/udev/udevadm-settle.c @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2006-2009 Kay Sievers + * Copyright (C) 2009 Canonical Ltd. + * Copyright (C) 2009 Scott James Remnant + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +static int adm_settle(struct udev *udev, int argc, char *argv[]) +{ + static const struct option options[] = { + { "seq-start", required_argument, NULL, 's' }, + { "seq-end", required_argument, NULL, 'e' }, + { "timeout", required_argument, NULL, 't' }, + { "exit-if-exists", required_argument, NULL, 'E' }, + { "quiet", no_argument, NULL, 'q' }, + { "help", no_argument, NULL, 'h' }, + {} + }; + unsigned long long start_usec = now_usec(); + unsigned long long start = 0; + unsigned long long end = 0; + int quiet = 0; + const char *exists = NULL; + unsigned int timeout = 120; + struct pollfd pfd[1]; + struct udev_queue *udev_queue = NULL; + int rc = EXIT_FAILURE; + + dbg(udev, "version %s\n", VERSION); + + for (;;) { + int option; + int seconds; + + option = getopt_long(argc, argv, "s:e:t:E:qh", options, NULL); + if (option == -1) + break; + + switch (option) { + case 's': + start = strtoull(optarg, NULL, 0); + break; + case 'e': + end = strtoull(optarg, NULL, 0); + break; + case 't': + seconds = atoi(optarg); + if (seconds >= 0) + timeout = seconds; + else + fprintf(stderr, "invalid timeout value\n"); + dbg(udev, "timeout=%i\n", timeout); + break; + case 'q': + quiet = 1; + break; + case 'E': + exists = optarg; + break; + case 'h': + printf("Usage: udevadm settle OPTIONS\n" + " --timeout= maximum time to wait for events\n" + " --seq-start= first seqnum to wait for\n" + " --seq-end= last seqnum to wait for\n" + " --exit-if-exists= stop waiting if file exists\n" + " --quiet do not print list after timeout\n" + " --help\n\n"); + exit(EXIT_SUCCESS); + default: + exit(EXIT_FAILURE); + } + } + + udev_queue = udev_queue_new(udev); + if (udev_queue == NULL) + exit(2); + + if (start > 0) { + unsigned long long kernel_seq; + + kernel_seq = udev_queue_get_kernel_seqnum(udev_queue); + + /* unless specified, the last event is the current kernel seqnum */ + if (end == 0) + end = udev_queue_get_kernel_seqnum(udev_queue); + + if (start > end) { + err(udev, "seq-start larger than seq-end, ignoring\n"); + start = 0; + end = 0; + } + + if (start > kernel_seq || end > kernel_seq) { + err(udev, "seq-start or seq-end larger than current kernel value, ignoring\n"); + start = 0; + end = 0; + } + info(udev, "start=%llu end=%llu current=%llu\n", start, end, kernel_seq); + } else { + if (end > 0) { + err(udev, "seq-end needs seq-start parameter, ignoring\n"); + end = 0; + } + } + + /* guarantee that the udev daemon isn't pre-processing */ + if (getuid() == 0) { + struct udev_ctrl *uctrl; + + uctrl = udev_ctrl_new(udev); + if (uctrl != NULL) { + if (udev_ctrl_send_ping(uctrl, timeout) < 0) { + info(udev, "no connection to daemon\n"); + udev_ctrl_unref(uctrl); + rc = EXIT_SUCCESS; + goto out; + } + udev_ctrl_unref(uctrl); + } + } + + pfd[0].events = POLLIN; + pfd[0].fd = inotify_init1(IN_CLOEXEC); + if (pfd[0].fd < 0) { + err(udev, "inotify_init failed: %m\n"); + } else { + if (inotify_add_watch(pfd[0].fd, udev_get_run_path(udev), IN_MOVED_TO) < 0) { + err(udev, "watching '%s' failed\n", udev_get_run_path(udev)); + close(pfd[0].fd); + pfd[0].fd = -1; + } + } + + for (;;) { + struct stat statbuf; + + if (exists != NULL && stat(exists, &statbuf) == 0) { + rc = EXIT_SUCCESS; + break; + } + + if (start > 0) { + /* if asked for, wait for a specific sequence of events */ + if (udev_queue_get_seqnum_sequence_is_finished(udev_queue, start, end) == 1) { + rc = EXIT_SUCCESS; + break; + } + } else { + /* exit if queue is empty */ + if (udev_queue_get_queue_is_empty(udev_queue)) { + rc = EXIT_SUCCESS; + break; + } + } + + if (pfd[0].fd >= 0) { + int delay; + + if (exists != NULL || start > 0) + delay = 100; + else + delay = 1000; + /* wake up after delay, or immediately after the queue is rebuilt */ + if (poll(pfd, 1, delay) > 0 && pfd[0].revents & POLLIN) { + char buf[sizeof(struct inotify_event) + PATH_MAX]; + + read(pfd[0].fd, buf, sizeof(buf)); + } + } else { + sleep(1); + } + + if (timeout > 0) { + unsigned long long age_usec; + + age_usec = now_usec() - start_usec; + if (age_usec / (1000 * 1000) >= timeout) { + struct udev_list_entry *list_entry; + + if (!quiet && udev_queue_get_queued_list_entry(udev_queue) != NULL) { + info(udev, "timeout waiting for udev queue\n"); + printf("\nudevadm settle - timeout of %i seconds reached, the event queue contains:\n", timeout); + udev_list_entry_foreach(list_entry, udev_queue_get_queued_list_entry(udev_queue)) + printf(" %s (%s)\n", + udev_list_entry_get_name(list_entry), + udev_list_entry_get_value(list_entry)); + } + + break; + } + } + } +out: + if (pfd[0].fd >= 0) + close(pfd[0].fd); + udev_queue_unref(udev_queue); + return rc; +} + +const struct udevadm_cmd udevadm_settle = { + .name = "settle", + .cmd = adm_settle, + .help = "wait for the event queue to finish", +}; diff --git a/src/udev/udevadm-test-builtin.c b/src/udev/udevadm-test-builtin.c new file mode 100644 index 0000000000..3a49f7ce9c --- /dev/null +++ b/src/udev/udevadm-test-builtin.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2011 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +static void help(struct udev *udev) +{ + fprintf(stderr, "\n"); + fprintf(stderr, "Usage: udevadm builtin [--help] \n"); + udev_builtin_list(udev); + fprintf(stderr, "\n"); +} + +static int adm_builtin(struct udev *udev, int argc, char *argv[]) +{ + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + {} + }; + char *command = NULL; + char *syspath = NULL; + char filename[UTIL_PATH_SIZE]; + struct udev_device *dev = NULL; + enum udev_builtin_cmd cmd; + int rc = EXIT_SUCCESS; + + dbg(udev, "version %s\n", VERSION); + + for (;;) { + int option; + + option = getopt_long(argc, argv, "h", options, NULL); + if (option == -1) + break; + + switch (option) { + case 'h': + help(udev); + goto out; + } + } + + command = argv[optind++]; + if (command == NULL) { + fprintf(stderr, "command missing\n"); + help(udev); + rc = 2; + goto out; + } + + syspath = argv[optind++]; + if (syspath == NULL) { + fprintf(stderr, "syspath missing\n\n"); + rc = 3; + goto out; + } + + udev_builtin_init(udev); + + cmd = udev_builtin_lookup(command); + if (cmd >= UDEV_BUILTIN_MAX) { + fprintf(stderr, "unknown command '%s'\n", command); + help(udev); + rc = 5; + goto out; + } + + /* add /sys if needed */ + if (strncmp(syspath, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) != 0) + util_strscpyl(filename, sizeof(filename), udev_get_sys_path(udev), syspath, NULL); + else + util_strscpy(filename, sizeof(filename), syspath); + util_remove_trailing_chars(filename, '/'); + + dev = udev_device_new_from_syspath(udev, filename); + if (dev == NULL) { + fprintf(stderr, "unable to open device '%s'\n\n", filename); + rc = 4; + goto out; + } + + if (udev_builtin_run(dev, cmd, command, true) < 0) { + fprintf(stderr, "error executing '%s'\n\n", command); + rc = 6; + } +out: + udev_device_unref(dev); + udev_builtin_exit(udev); + return rc; +} + +const struct udevadm_cmd udevadm_test_builtin = { + .name = "test-builtin", + .cmd = adm_builtin, + .help = "test a built-in command", + .debug = true, +}; diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c new file mode 100644 index 0000000000..6275cff899 --- /dev/null +++ b/src/udev/udevadm-test.c @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2003-2004 Greg Kroah-Hartman + * Copyright (C) 2004-2008 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +static int adm_test(struct udev *udev, int argc, char *argv[]) +{ + int resolve_names = 1; + char filename[UTIL_PATH_SIZE]; + const char *action = "add"; + const char *syspath = NULL; + struct udev_event *event = NULL; + struct udev_device *dev = NULL; + struct udev_rules *rules = NULL; + struct udev_list_entry *entry; + sigset_t mask, sigmask_orig; + int err; + int rc = 0; + + static const struct option options[] = { + { "action", required_argument, NULL, 'a' }, + { "resolve-names", required_argument, NULL, 'N' }, + { "help", no_argument, NULL, 'h' }, + {} + }; + + info(udev, "version %s\n", VERSION); + + for (;;) { + int option; + + option = getopt_long(argc, argv, "a:s:N:fh", options, NULL); + if (option == -1) + break; + + dbg(udev, "option '%c'\n", option); + switch (option) { + case 'a': + action = optarg; + break; + case 'N': + if (strcmp (optarg, "early") == 0) { + resolve_names = 1; + } else if (strcmp (optarg, "late") == 0) { + resolve_names = 0; + } else if (strcmp (optarg, "never") == 0) { + resolve_names = -1; + } else { + fprintf(stderr, "resolve-names must be early, late or never\n"); + err(udev, "resolve-names must be early, late or never\n"); + exit(EXIT_FAILURE); + } + break; + case 'h': + printf("Usage: udevadm test OPTIONS \n" + " --action= set action string\n" + " --help\n\n"); + exit(EXIT_SUCCESS); + default: + exit(EXIT_FAILURE); + } + } + syspath = argv[optind]; + + if (syspath == NULL) { + fprintf(stderr, "syspath parameter missing\n"); + rc = 2; + goto out; + } + + printf("This program is for debugging only, it does not run any program,\n" + "specified by a RUN key. It may show incorrect results, because\n" + "some values may be different, or not available at a simulation run.\n" + "\n"); + + sigprocmask(SIG_SETMASK, NULL, &sigmask_orig); + + udev_builtin_init(udev); + + rules = udev_rules_new(udev, resolve_names); + if (rules == NULL) { + fprintf(stderr, "error reading rules\n"); + rc = 3; + goto out; + } + + /* add /sys if needed */ + if (strncmp(syspath, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) != 0) + util_strscpyl(filename, sizeof(filename), udev_get_sys_path(udev), syspath, NULL); + else + util_strscpy(filename, sizeof(filename), syspath); + util_remove_trailing_chars(filename, '/'); + + dev = udev_device_new_from_syspath(udev, filename); + if (dev == NULL) { + fprintf(stderr, "unable to open device '%s'\n", filename); + rc = 4; + goto out; + } + + /* skip reading of db, but read kernel parameters */ + udev_device_set_info_loaded(dev); + udev_device_read_uevent_file(dev); + + udev_device_set_action(dev, action); + event = udev_event_new(dev); + + sigfillset(&mask); + sigprocmask(SIG_SETMASK, &mask, &sigmask_orig); + event->fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); + if (event->fd_signal < 0) { + fprintf(stderr, "error creating signalfd\n"); + rc = 5; + goto out; + } + + err = udev_event_execute_rules(event, rules, &sigmask_orig); + + udev_list_entry_foreach(entry, udev_device_get_properties_list_entry(dev)) + printf("%s=%s\n", udev_list_entry_get_name(entry), udev_list_entry_get_value(entry)); + + if (err == 0) { + udev_list_entry_foreach(entry, udev_list_get_entry(&event->run_list)) { + char program[UTIL_PATH_SIZE]; + + udev_event_apply_format(event, udev_list_entry_get_name(entry), program, sizeof(program)); + printf("run: '%s'\n", program); + } + } +out: + if (event != NULL && event->fd_signal >= 0) + close(event->fd_signal); + udev_event_unref(event); + udev_device_unref(dev); + udev_rules_unref(rules); + udev_builtin_exit(udev); + return rc; +} + +const struct udevadm_cmd udevadm_test = { + .name = "test", + .cmd = adm_test, + .help = "test an event run", + .debug = true, +}; diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c new file mode 100644 index 0000000000..3cce23dfb2 --- /dev/null +++ b/src/udev/udevadm-trigger.c @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2008-2009 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +static int verbose; +static int dry_run; + +static void exec_list(struct udev_enumerate *udev_enumerate, const char *action) +{ + struct udev *udev = udev_enumerate_get_udev(udev_enumerate); + struct udev_list_entry *entry; + + udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(udev_enumerate)) { + char filename[UTIL_PATH_SIZE]; + int fd; + + if (verbose) + printf("%s\n", udev_list_entry_get_name(entry)); + if (dry_run) + continue; + util_strscpyl(filename, sizeof(filename), udev_list_entry_get_name(entry), "/uevent", NULL); + fd = open(filename, O_WRONLY); + if (fd < 0) { + dbg(udev, "error on opening %s: %m\n", filename); + continue; + } + if (write(fd, action, strlen(action)) < 0) + info(udev, "error writing '%s' to '%s': %m\n", action, filename); + close(fd); + } +} + +static const char *keyval(const char *str, const char **val, char *buf, size_t size) +{ + char *pos; + + util_strscpy(buf, size,str); + pos = strchr(buf, '='); + if (pos != NULL) { + pos[0] = 0; + pos++; + } + *val = pos; + return buf; +} + +static int adm_trigger(struct udev *udev, int argc, char *argv[]) +{ + static const struct option options[] = { + { "verbose", no_argument, NULL, 'v' }, + { "dry-run", no_argument, NULL, 'n' }, + { "type", required_argument, NULL, 't' }, + { "action", required_argument, NULL, 'c' }, + { "subsystem-match", required_argument, NULL, 's' }, + { "subsystem-nomatch", required_argument, NULL, 'S' }, + { "attr-match", required_argument, NULL, 'a' }, + { "attr-nomatch", required_argument, NULL, 'A' }, + { "property-match", required_argument, NULL, 'p' }, + { "tag-match", required_argument, NULL, 'g' }, + { "sysname-match", required_argument, NULL, 'y' }, + { "parent-match", required_argument, NULL, 'b' }, + { "help", no_argument, NULL, 'h' }, + {} + }; + enum { + TYPE_DEVICES, + TYPE_SUBSYSTEMS, + } device_type = TYPE_DEVICES; + const char *action = "change"; + struct udev_enumerate *udev_enumerate; + int rc = 0; + + dbg(udev, "version %s\n", VERSION); + udev_enumerate = udev_enumerate_new(udev); + if (udev_enumerate == NULL) { + rc = 1; + goto exit; + } + + for (;;) { + int option; + const char *key; + const char *val; + char buf[UTIL_PATH_SIZE]; + + option = getopt_long(argc, argv, "vng:o:t:hc:p:s:S:a:A:y:b:", options, NULL); + if (option == -1) + break; + + switch (option) { + case 'v': + verbose = 1; + break; + case 'n': + dry_run = 1; + break; + case 't': + if (strcmp(optarg, "devices") == 0) { + device_type = TYPE_DEVICES; + } else if (strcmp(optarg, "subsystems") == 0) { + device_type = TYPE_SUBSYSTEMS; + } else { + err(udev, "unknown type --type=%s\n", optarg); + rc = 2; + goto exit; + } + break; + case 'c': + action = optarg; + break; + case 's': + udev_enumerate_add_match_subsystem(udev_enumerate, optarg); + break; + case 'S': + udev_enumerate_add_nomatch_subsystem(udev_enumerate, optarg); + break; + case 'a': + key = keyval(optarg, &val, buf, sizeof(buf)); + udev_enumerate_add_match_sysattr(udev_enumerate, key, val); + break; + case 'A': + key = keyval(optarg, &val, buf, sizeof(buf)); + udev_enumerate_add_nomatch_sysattr(udev_enumerate, key, val); + break; + case 'p': + key = keyval(optarg, &val, buf, sizeof(buf)); + udev_enumerate_add_match_property(udev_enumerate, key, val); + break; + case 'g': + udev_enumerate_add_match_tag(udev_enumerate, optarg); + break; + case 'y': + udev_enumerate_add_match_sysname(udev_enumerate, optarg); + break; + case 'b': { + char path[UTIL_PATH_SIZE]; + struct udev_device *dev; + + /* add sys dir if needed */ + if (strncmp(optarg, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) != 0) + util_strscpyl(path, sizeof(path), udev_get_sys_path(udev), optarg, NULL); + else + util_strscpy(path, sizeof(path), optarg); + util_remove_trailing_chars(path, '/'); + dev = udev_device_new_from_syspath(udev, path); + if (dev == NULL) { + err(udev, "unable to open the device '%s'\n", optarg); + rc = 2; + goto exit; + } + udev_enumerate_add_match_parent(udev_enumerate, dev); + /* drop reference immediately, enumerate pins the device as long as needed */ + udev_device_unref(dev); + break; + } + case 'h': + printf("Usage: udevadm trigger OPTIONS\n" + " --verbose print the list of devices while running\n" + " --dry-run do not actually trigger the events\n" + " --type= type of events to trigger\n" + " devices sys devices (default)\n" + " subsystems sys subsystems and drivers\n" + " --action= event action value, default is \"change\"\n" + " --subsystem-match= trigger devices from a matching subsystem\n" + " --subsystem-nomatch= exclude devices from a matching subsystem\n" + " --attr-match=]> trigger devices with a matching attribute\n" + " --attr-nomatch=]> exclude devices with a matching attribute\n" + " --property-match== trigger devices with a matching property\n" + " --tag-match== trigger devices with a matching property\n" + " --sysname-match= trigger devices with a matching name\n" + " --parent-match= trigger devices with that parent device\n" + " --help\n\n"); + goto exit; + default: + rc = 1; + goto exit; + } + } + + switch (device_type) { + case TYPE_SUBSYSTEMS: + udev_enumerate_scan_subsystems(udev_enumerate); + exec_list(udev_enumerate, action); + goto exit; + case TYPE_DEVICES: + udev_enumerate_scan_devices(udev_enumerate); + exec_list(udev_enumerate, action); + goto exit; + default: + goto exit; + } +exit: + udev_enumerate_unref(udev_enumerate); + return rc; +} + +const struct udevadm_cmd udevadm_trigger = { + .name = "trigger", + .cmd = adm_trigger, + .help = "request events from the kernel", +}; diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c new file mode 100644 index 0000000000..224ece0bb7 --- /dev/null +++ b/src/udev/udevadm.c @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2007-2009 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +static bool debug; + +void udev_main_log(struct udev *udev, int priority, + const char *file, int line, const char *fn, + const char *format, va_list args) +{ + if (debug) { + fprintf(stderr, "%s: ", fn); + vfprintf(stderr, format, args); + } else { + va_list args2; + + va_copy(args2, args); + vfprintf(stderr, format, args2); + va_end(args2); + vsyslog(priority, format, args); + } +} + +static int adm_version(struct udev *udev, int argc, char *argv[]) +{ + printf("%s\n", VERSION); + return 0; +} +static const struct udevadm_cmd udevadm_version = { + .name = "version", + .cmd = adm_version, +}; + +static int adm_help(struct udev *udev, int argc, char *argv[]); +static const struct udevadm_cmd udevadm_help = { + .name = "help", + .cmd = adm_help, +}; + +static const struct udevadm_cmd *udevadm_cmds[] = { + &udevadm_info, + &udevadm_trigger, + &udevadm_settle, + &udevadm_control, + &udevadm_monitor, + &udevadm_test, + &udevadm_test_builtin, + &udevadm_version, + &udevadm_help, +}; + +static int adm_help(struct udev *udev, int argc, char *argv[]) +{ + unsigned int i; + + fprintf(stderr, "Usage: udevadm [--help] [--version] [--debug] COMMAND [COMMAND OPTIONS]\n"); + for (i = 0; i < ARRAY_SIZE(udevadm_cmds); i++) + if (udevadm_cmds[i]->help != NULL) + printf(" %-12s %s\n", udevadm_cmds[i]->name, udevadm_cmds[i]->help); + fprintf(stderr, "\n"); + return 0; +} + +static int run_command(struct udev *udev, const struct udevadm_cmd *cmd, int argc, char *argv[]) +{ + if (cmd->debug) { + debug = true; + if (udev_get_log_priority(udev) < LOG_INFO) + udev_set_log_priority(udev, LOG_INFO); + } + info(udev, "calling: %s\n", cmd->name); + return cmd->cmd(udev, argc, argv); +} + +int main(int argc, char *argv[]) +{ + struct udev *udev; + static const struct option options[] = { + { "debug", no_argument, NULL, 'd' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + {} + }; + const char *command; + unsigned int i; + int rc = 1; + + udev = udev_new(); + if (udev == NULL) + goto out; + + udev_log_init("udevadm"); + udev_set_log_fn(udev, udev_main_log); + udev_selinux_init(udev); + + for (;;) { + int option; + + option = getopt_long(argc, argv, "+dhV", options, NULL); + if (option == -1) + break; + + switch (option) { + case 'd': + debug = true; + if (udev_get_log_priority(udev) < LOG_INFO) + udev_set_log_priority(udev, LOG_INFO); + break; + case 'h': + rc = adm_help(udev, argc, argv); + goto out; + case 'V': + rc = adm_version(udev, argc, argv); + goto out; + default: + goto out; + } + } + command = argv[optind]; + + info(udev, "runtime dir '%s'\n", udev_get_run_path(udev)); + + if (command != NULL) + for (i = 0; i < ARRAY_SIZE(udevadm_cmds); i++) { + if (strcmp(udevadm_cmds[i]->name, command) == 0) { + argc -= optind; + argv += optind; + optind = 0; + rc = run_command(udev, udevadm_cmds[i], argc, argv); + goto out; + } + } + + fprintf(stderr, "missing or unknown command\n\n"); + adm_help(udev, argc, argv); + rc = 2; +out: + udev_selinux_exit(udev); + udev_unref(udev); + udev_log_close(); + return rc; +} diff --git a/src/udev/udevd.c b/src/udev/udevd.c new file mode 100644 index 0000000000..694e758777 --- /dev/null +++ b/src/udev/udevd.c @@ -0,0 +1,1746 @@ +/* + * Copyright (C) 2004-2011 Kay Sievers + * Copyright (C) 2004 Chris Friesen + * Copyright (C) 2009 Canonical Ltd. + * Copyright (C) 2009 Scott James Remnant + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" +#include "sd-daemon.h" + +static bool debug; + +void udev_main_log(struct udev *udev, int priority, + const char *file, int line, const char *fn, + const char *format, va_list args) +{ + if (debug) { + char buf[1024]; + struct timespec ts; + + vsnprintf(buf, sizeof(buf), format, args); + clock_gettime(CLOCK_MONOTONIC, &ts); + fprintf(stderr, "[%llu.%06u] [%u] %s: %s", + (unsigned long long) ts.tv_sec, (unsigned int) ts.tv_nsec/1000, + (int) getpid(), fn, buf); + } else { + vsyslog(priority, format, args); + } +} + +static struct udev_rules *rules; +static struct udev_queue_export *udev_queue_export; +static struct udev_ctrl *udev_ctrl; +static struct udev_monitor *monitor; +static int worker_watch[2] = { -1, -1 }; +static int fd_signal = -1; +static int fd_ep = -1; +static int fd_inotify = -1; +static bool stop_exec_queue; +static bool reload; +static int children; +static int children_max; +static int exec_delay; +static sigset_t sigmask_orig; +static UDEV_LIST(event_list); +static UDEV_LIST(worker_list); +static bool udev_exit; + +enum event_state { + EVENT_UNDEF, + EVENT_QUEUED, + EVENT_RUNNING, +}; + +struct event { + struct udev_list_node node; + struct udev *udev; + struct udev_device *dev; + enum event_state state; + int exitcode; + unsigned long long int delaying_seqnum; + unsigned long long int seqnum; + const char *devpath; + size_t devpath_len; + const char *devpath_old; + dev_t devnum; + bool is_block; + int ifindex; +}; + +static struct event *node_to_event(struct udev_list_node *node) +{ + char *event; + + event = (char *)node; + event -= offsetof(struct event, node); + return (struct event *)event; +} + +static void event_queue_cleanup(struct udev *udev, enum event_state type); + +enum worker_state { + WORKER_UNDEF, + WORKER_RUNNING, + WORKER_IDLE, + WORKER_KILLED, +}; + +struct worker { + struct udev_list_node node; + struct udev *udev; + int refcount; + pid_t pid; + struct udev_monitor *monitor; + enum worker_state state; + struct event *event; + unsigned long long event_start_usec; +}; + +/* passed from worker to main process */ +struct worker_message { + pid_t pid; + int exitcode; +}; + +static struct worker *node_to_worker(struct udev_list_node *node) +{ + char *worker; + + worker = (char *)node; + worker -= offsetof(struct worker, node); + return (struct worker *)worker; +} + +static void event_queue_delete(struct event *event, bool export) +{ + udev_list_node_remove(&event->node); + + if (export) { + udev_queue_export_device_finished(udev_queue_export, event->dev); + info(event->udev, "seq %llu done with %i\n", udev_device_get_seqnum(event->dev), event->exitcode); + } + udev_device_unref(event->dev); + free(event); +} + +static struct worker *worker_ref(struct worker *worker) +{ + worker->refcount++; + return worker; +} + +static void worker_cleanup(struct worker *worker) +{ + udev_list_node_remove(&worker->node); + udev_monitor_unref(worker->monitor); + children--; + free(worker); +} + +static void worker_unref(struct worker *worker) +{ + worker->refcount--; + if (worker->refcount > 0) + return; + info(worker->udev, "worker [%u] cleaned up\n", worker->pid); + worker_cleanup(worker); +} + +static void worker_list_cleanup(struct udev *udev) +{ + struct udev_list_node *loop, *tmp; + + udev_list_node_foreach_safe(loop, tmp, &worker_list) { + struct worker *worker = node_to_worker(loop); + + worker_cleanup(worker); + } +} + +static void worker_new(struct event *event) +{ + struct udev *udev = event->udev; + struct worker *worker; + struct udev_monitor *worker_monitor; + pid_t pid; + + /* listen for new events */ + worker_monitor = udev_monitor_new_from_netlink(udev, NULL); + if (worker_monitor == NULL) + return; + /* allow the main daemon netlink address to send devices to the worker */ + udev_monitor_allow_unicast_sender(worker_monitor, monitor); + udev_monitor_enable_receiving(worker_monitor); + + worker = calloc(1, sizeof(struct worker)); + if (worker == NULL) { + udev_monitor_unref(worker_monitor); + return; + } + /* worker + event reference */ + worker->refcount = 2; + worker->udev = udev; + + pid = fork(); + switch (pid) { + case 0: { + struct udev_device *dev = NULL; + int fd_monitor; + struct epoll_event ep_signal, ep_monitor; + sigset_t mask; + int rc = EXIT_SUCCESS; + + /* take initial device from queue */ + dev = event->dev; + event->dev = NULL; + + free(worker); + worker_list_cleanup(udev); + event_queue_cleanup(udev, EVENT_UNDEF); + udev_queue_export_unref(udev_queue_export); + udev_monitor_unref(monitor); + udev_ctrl_unref(udev_ctrl); + close(fd_signal); + close(fd_ep); + close(worker_watch[READ_END]); + + sigfillset(&mask); + fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); + if (fd_signal < 0) { + err(udev, "error creating signalfd %m\n"); + rc = 2; + goto out; + } + + fd_ep = epoll_create1(EPOLL_CLOEXEC); + if (fd_ep < 0) { + err(udev, "error creating epoll fd: %m\n"); + rc = 3; + goto out; + } + + memset(&ep_signal, 0, sizeof(struct epoll_event)); + ep_signal.events = EPOLLIN; + ep_signal.data.fd = fd_signal; + + fd_monitor = udev_monitor_get_fd(worker_monitor); + memset(&ep_monitor, 0, sizeof(struct epoll_event)); + ep_monitor.events = EPOLLIN; + ep_monitor.data.fd = fd_monitor; + + if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 || + epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_monitor, &ep_monitor) < 0) { + err(udev, "fail to add fds to epoll: %m\n"); + rc = 4; + goto out; + } + + /* request TERM signal if parent exits */ + prctl(PR_SET_PDEATHSIG, SIGTERM); + + for (;;) { + struct udev_event *udev_event; + struct worker_message msg; + int err; + + info(udev, "seq %llu running\n", udev_device_get_seqnum(dev)); + udev_event = udev_event_new(dev); + if (udev_event == NULL) { + rc = 5; + goto out; + } + + /* needed for SIGCHLD/SIGTERM in spawn() */ + udev_event->fd_signal = fd_signal; + + if (exec_delay > 0) + udev_event->exec_delay = exec_delay; + + /* apply rules, create node, symlinks */ + err = udev_event_execute_rules(udev_event, rules, &sigmask_orig); + + if (err == 0) + udev_event_execute_run(udev_event, &sigmask_orig); + + /* apply/restore inotify watch */ + if (err == 0 && udev_event->inotify_watch) { + udev_watch_begin(udev, dev); + udev_device_update_db(dev); + } + + /* send processed event back to libudev listeners */ + udev_monitor_send_device(worker_monitor, NULL, dev); + + /* send udevd the result of the event execution */ + memset(&msg, 0, sizeof(struct worker_message)); + if (err != 0) + msg.exitcode = err; + msg.pid = getpid(); + send(worker_watch[WRITE_END], &msg, sizeof(struct worker_message), 0); + + info(udev, "seq %llu processed with %i\n", udev_device_get_seqnum(dev), err); + + udev_device_unref(dev); + dev = NULL; + + if (udev_event->sigterm) { + udev_event_unref(udev_event); + goto out; + } + + udev_event_unref(udev_event); + + /* wait for more device messages from main udevd, or term signal */ + while (dev == NULL) { + struct epoll_event ev[4]; + int fdcount; + int i; + + fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), -1); + if (fdcount < 0) { + if (errno == EINTR) + continue; + err = -errno; + err(udev, "failed to poll: %m\n"); + goto out; + } + + for (i = 0; i < fdcount; i++) { + if (ev[i].data.fd == fd_monitor && ev[i].events & EPOLLIN) { + dev = udev_monitor_receive_device(worker_monitor); + break; + } else if (ev[i].data.fd == fd_signal && ev[i].events & EPOLLIN) { + struct signalfd_siginfo fdsi; + ssize_t size; + + size = read(fd_signal, &fdsi, sizeof(struct signalfd_siginfo)); + if (size != sizeof(struct signalfd_siginfo)) + continue; + switch (fdsi.ssi_signo) { + case SIGTERM: + goto out; + } + } + } + } + } +out: + udev_device_unref(dev); + if (fd_signal >= 0) + close(fd_signal); + if (fd_ep >= 0) + close(fd_ep); + close(fd_inotify); + close(worker_watch[WRITE_END]); + udev_rules_unref(rules); + udev_builtin_exit(udev); + udev_monitor_unref(worker_monitor); + udev_unref(udev); + udev_log_close(); + exit(rc); + } + case -1: + udev_monitor_unref(worker_monitor); + event->state = EVENT_QUEUED; + free(worker); + err(udev, "fork of child failed: %m\n"); + break; + default: + /* close monitor, but keep address around */ + udev_monitor_disconnect(worker_monitor); + worker->monitor = worker_monitor; + worker->pid = pid; + worker->state = WORKER_RUNNING; + worker->event_start_usec = now_usec(); + worker->event = event; + event->state = EVENT_RUNNING; + udev_list_node_append(&worker->node, &worker_list); + children++; + info(udev, "seq %llu forked new worker [%u]\n", udev_device_get_seqnum(event->dev), pid); + break; + } +} + +static void event_run(struct event *event) +{ + struct udev_list_node *loop; + + udev_list_node_foreach(loop, &worker_list) { + struct worker *worker = node_to_worker(loop); + ssize_t count; + + if (worker->state != WORKER_IDLE) + continue; + + count = udev_monitor_send_device(monitor, worker->monitor, event->dev); + if (count < 0) { + err(event->udev, "worker [%u] did not accept message %zi (%m), kill it\n", worker->pid, count); + kill(worker->pid, SIGKILL); + worker->state = WORKER_KILLED; + continue; + } + worker_ref(worker); + worker->event = event; + worker->state = WORKER_RUNNING; + worker->event_start_usec = now_usec(); + event->state = EVENT_RUNNING; + return; + } + + if (children >= children_max) { + if (children_max > 1) + info(event->udev, "maximum number (%i) of children reached\n", children); + return; + } + + /* start new worker and pass initial device */ + worker_new(event); +} + +static int event_queue_insert(struct udev_device *dev) +{ + struct event *event; + + event = calloc(1, sizeof(struct event)); + if (event == NULL) + return -1; + + event->udev = udev_device_get_udev(dev); + event->dev = dev; + event->seqnum = udev_device_get_seqnum(dev); + event->devpath = udev_device_get_devpath(dev); + event->devpath_len = strlen(event->devpath); + event->devpath_old = udev_device_get_devpath_old(dev); + event->devnum = udev_device_get_devnum(dev); + event->is_block = (strcmp("block", udev_device_get_subsystem(dev)) == 0); + event->ifindex = udev_device_get_ifindex(dev); + + udev_queue_export_device_queued(udev_queue_export, dev); + info(event->udev, "seq %llu queued, '%s' '%s'\n", udev_device_get_seqnum(dev), + udev_device_get_action(dev), udev_device_get_subsystem(dev)); + + event->state = EVENT_QUEUED; + udev_list_node_append(&event->node, &event_list); + return 0; +} + +static void worker_kill(struct udev *udev, int retain) +{ + struct udev_list_node *loop; + int max; + + if (children <= retain) + return; + + max = children - retain; + + udev_list_node_foreach(loop, &worker_list) { + struct worker *worker = node_to_worker(loop); + + if (max-- <= 0) + break; + + if (worker->state == WORKER_KILLED) + continue; + + worker->state = WORKER_KILLED; + kill(worker->pid, SIGTERM); + } +} + +/* lookup event for identical, parent, child device */ +static bool is_devpath_busy(struct event *event) +{ + struct udev_list_node *loop; + size_t common; + + /* check if queue contains events we depend on */ + udev_list_node_foreach(loop, &event_list) { + struct event *loop_event = node_to_event(loop); + + /* we already found a later event, earlier can not block us, no need to check again */ + if (loop_event->seqnum < event->delaying_seqnum) + continue; + + /* event we checked earlier still exists, no need to check again */ + if (loop_event->seqnum == event->delaying_seqnum) + return true; + + /* found ourself, no later event can block us */ + if (loop_event->seqnum >= event->seqnum) + break; + + /* check major/minor */ + if (major(event->devnum) != 0 && event->devnum == loop_event->devnum && event->is_block == loop_event->is_block) + return true; + + /* check network device ifindex */ + if (event->ifindex != 0 && event->ifindex == loop_event->ifindex) + return true; + + /* check our old name */ + if (event->devpath_old != NULL && strcmp(loop_event->devpath, event->devpath_old) == 0) { + event->delaying_seqnum = loop_event->seqnum; + return true; + } + + /* compare devpath */ + common = MIN(loop_event->devpath_len, event->devpath_len); + + /* one devpath is contained in the other? */ + if (memcmp(loop_event->devpath, event->devpath, common) != 0) + continue; + + /* identical device event found */ + if (loop_event->devpath_len == event->devpath_len) { + /* devices names might have changed/swapped in the meantime */ + if (major(event->devnum) != 0 && (event->devnum != loop_event->devnum || event->is_block != loop_event->is_block)) + continue; + if (event->ifindex != 0 && event->ifindex != loop_event->ifindex) + continue; + event->delaying_seqnum = loop_event->seqnum; + return true; + } + + /* parent device event found */ + if (event->devpath[common] == '/') { + event->delaying_seqnum = loop_event->seqnum; + return true; + } + + /* child device event found */ + if (loop_event->devpath[common] == '/') { + event->delaying_seqnum = loop_event->seqnum; + return true; + } + + /* no matching device */ + continue; + } + + return false; +} + +static void event_queue_start(struct udev *udev) +{ + struct udev_list_node *loop; + + udev_list_node_foreach(loop, &event_list) { + struct event *event = node_to_event(loop); + + if (event->state != EVENT_QUEUED) + continue; + + /* do not start event if parent or child event is still running */ + if (is_devpath_busy(event)) { + dbg(udev, "delay seq %llu (%s)\n", event->seqnum, event->devpath); + continue; + } + + event_run(event); + } +} + +static void event_queue_cleanup(struct udev *udev, enum event_state match_type) +{ + struct udev_list_node *loop, *tmp; + + udev_list_node_foreach_safe(loop, tmp, &event_list) { + struct event *event = node_to_event(loop); + + if (match_type != EVENT_UNDEF && match_type != event->state) + continue; + + event_queue_delete(event, false); + } +} + +static void worker_returned(int fd_worker) +{ + for (;;) { + struct worker_message msg; + ssize_t size; + struct udev_list_node *loop; + + size = recv(fd_worker, &msg, sizeof(struct worker_message), MSG_DONTWAIT); + if (size != sizeof(struct worker_message)) + break; + + /* lookup worker who sent the signal */ + udev_list_node_foreach(loop, &worker_list) { + struct worker *worker = node_to_worker(loop); + + if (worker->pid != msg.pid) + continue; + + /* worker returned */ + if (worker->event) { + worker->event->exitcode = msg.exitcode; + event_queue_delete(worker->event, true); + worker->event = NULL; + } + if (worker->state != WORKER_KILLED) + worker->state = WORKER_IDLE; + worker_unref(worker); + break; + } + } +} + +/* receive the udevd message from userspace */ +static struct udev_ctrl_connection *handle_ctrl_msg(struct udev_ctrl *uctrl) +{ + struct udev *udev = udev_ctrl_get_udev(uctrl); + struct udev_ctrl_connection *ctrl_conn; + struct udev_ctrl_msg *ctrl_msg = NULL; + const char *str; + int i; + + ctrl_conn = udev_ctrl_get_connection(uctrl); + if (ctrl_conn == NULL) + goto out; + + ctrl_msg = udev_ctrl_receive_msg(ctrl_conn); + if (ctrl_msg == NULL) + goto out; + + i = udev_ctrl_get_set_log_level(ctrl_msg); + if (i >= 0) { + info(udev, "udevd message (SET_LOG_PRIORITY) received, log_priority=%i\n", i); + udev_set_log_priority(udev, i); + worker_kill(udev, 0); + } + + if (udev_ctrl_get_stop_exec_queue(ctrl_msg) > 0) { + info(udev, "udevd message (STOP_EXEC_QUEUE) received\n"); + stop_exec_queue = true; + } + + if (udev_ctrl_get_start_exec_queue(ctrl_msg) > 0) { + info(udev, "udevd message (START_EXEC_QUEUE) received\n"); + stop_exec_queue = false; + } + + if (udev_ctrl_get_reload(ctrl_msg) > 0) { + info(udev, "udevd message (RELOAD) received\n"); + reload = true; + } + + str = udev_ctrl_get_set_env(ctrl_msg); + if (str != NULL) { + char *key; + + key = strdup(str); + if (key != NULL) { + char *val; + + val = strchr(key, '='); + if (val != NULL) { + val[0] = '\0'; + val = &val[1]; + if (val[0] == '\0') { + info(udev, "udevd message (ENV) received, unset '%s'\n", key); + udev_add_property(udev, key, NULL); + } else { + info(udev, "udevd message (ENV) received, set '%s=%s'\n", key, val); + udev_add_property(udev, key, val); + } + } else { + err(udev, "wrong key format '%s'\n", key); + } + free(key); + } + worker_kill(udev, 0); + } + + i = udev_ctrl_get_set_children_max(ctrl_msg); + if (i >= 0) { + info(udev, "udevd message (SET_MAX_CHILDREN) received, children_max=%i\n", i); + children_max = i; + } + + if (udev_ctrl_get_ping(ctrl_msg) > 0) + info(udev, "udevd message (SYNC) received\n"); + + if (udev_ctrl_get_exit(ctrl_msg) > 0) { + info(udev, "udevd message (EXIT) received\n"); + udev_exit = true; + /* keep reference to block the client until we exit */ + udev_ctrl_connection_ref(ctrl_conn); + } +out: + udev_ctrl_msg_unref(ctrl_msg); + return udev_ctrl_connection_unref(ctrl_conn); +} + +/* read inotify messages */ +static int handle_inotify(struct udev *udev) +{ + int nbytes, pos; + char *buf; + struct inotify_event *ev; + + if ((ioctl(fd_inotify, FIONREAD, &nbytes) < 0) || (nbytes <= 0)) + return 0; + + buf = malloc(nbytes); + if (buf == NULL) { + err(udev, "error getting buffer for inotify\n"); + return -1; + } + + nbytes = read(fd_inotify, buf, nbytes); + + for (pos = 0; pos < nbytes; pos += sizeof(struct inotify_event) + ev->len) { + struct udev_device *dev; + + ev = (struct inotify_event *)(buf + pos); + dev = udev_watch_lookup(udev, ev->wd); + if (dev != NULL) { + info(udev, "inotify event: %x for %s\n", ev->mask, udev_device_get_devnode(dev)); + if (ev->mask & IN_CLOSE_WRITE) { + char filename[UTIL_PATH_SIZE]; + int fd; + + info(udev, "device %s closed, synthesising 'change'\n", udev_device_get_devnode(dev)); + util_strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL); + fd = open(filename, O_WRONLY); + if (fd >= 0) { + if (write(fd, "change", 6) < 0) + info(udev, "error writing uevent: %m\n"); + close(fd); + } + } + if (ev->mask & IN_IGNORED) + udev_watch_end(udev, dev); + + udev_device_unref(dev); + } + + } + + free(buf); + return 0; +} + +static void handle_signal(struct udev *udev, int signo) +{ + switch (signo) { + case SIGINT: + case SIGTERM: + udev_exit = true; + break; + case SIGCHLD: + for (;;) { + pid_t pid; + int status; + struct udev_list_node *loop, *tmp; + + pid = waitpid(-1, &status, WNOHANG); + if (pid <= 0) + break; + + udev_list_node_foreach_safe(loop, tmp, &worker_list) { + struct worker *worker = node_to_worker(loop); + + if (worker->pid != pid) + continue; + info(udev, "worker [%u] exit\n", pid); + + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) != 0) + err(udev, "worker [%u] exit with return code %i\n", pid, WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) { + err(udev, "worker [%u] terminated by signal %i (%s)\n", + pid, WTERMSIG(status), strsignal(WTERMSIG(status))); + } else if (WIFSTOPPED(status)) { + err(udev, "worker [%u] stopped\n", pid); + } else if (WIFCONTINUED(status)) { + err(udev, "worker [%u] continued\n", pid); + } else { + err(udev, "worker [%u] exit with status 0x%04x\n", pid, status); + } + + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + if (worker->event) { + err(udev, "worker [%u] failed while handling '%s'\n", + pid, worker->event->devpath); + worker->event->exitcode = -32; + event_queue_delete(worker->event, true); + /* drop reference taken for state 'running' */ + worker_unref(worker); + } + } + worker_unref(worker); + break; + } + } + break; + case SIGHUP: + reload = true; + break; + } +} + +static void static_dev_create_from_modules(struct udev *udev) +{ + struct utsname kernel; + char modules[UTIL_PATH_SIZE]; + char buf[4096]; + FILE *f; + + uname(&kernel); + util_strscpyl(modules, sizeof(modules), "/lib/modules/", kernel.release, "/modules.devname", NULL); + f = fopen(modules, "r"); + if (f == NULL) + return; + + while (fgets(buf, sizeof(buf), f) != NULL) { + char *s; + const char *modname; + const char *devname; + const char *devno; + int maj, min; + char type; + mode_t mode; + char filename[UTIL_PATH_SIZE]; + + if (buf[0] == '#') + continue; + + modname = buf; + s = strchr(modname, ' '); + if (s == NULL) + continue; + s[0] = '\0'; + + devname = &s[1]; + s = strchr(devname, ' '); + if (s == NULL) + continue; + s[0] = '\0'; + + devno = &s[1]; + s = strchr(devno, ' '); + if (s == NULL) + s = strchr(devno, '\n'); + if (s != NULL) + s[0] = '\0'; + if (sscanf(devno, "%c%u:%u", &type, &maj, &min) != 3) + continue; + + if (type == 'c') + mode = S_IFCHR; + else if (type == 'b') + mode = S_IFBLK; + else + continue; + + util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/", devname, NULL); + util_create_path_selinux(udev, filename); + udev_selinux_setfscreatecon(udev, filename, mode); + info(udev, "mknod '%s' %c%u:%u\n", filename, type, maj, min); + if (mknod(filename, mode, makedev(maj, min)) < 0 && errno == EEXIST) + utimensat(AT_FDCWD, filename, NULL, 0); + udev_selinux_resetfscreatecon(udev); + } + + fclose(f); +} + +static int copy_dev_dir(struct udev *udev, DIR *dir_from, DIR *dir_to, int maxdepth) +{ + struct dirent *dent; + + for (dent = readdir(dir_from); dent != NULL; dent = readdir(dir_from)) { + struct stat stats; + + if (dent->d_name[0] == '.') + continue; + if (fstatat(dirfd(dir_from), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0) + continue; + + if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) { + udev_selinux_setfscreateconat(udev, dirfd(dir_to), dent->d_name, stats.st_mode & 0777); + if (mknodat(dirfd(dir_to), dent->d_name, stats.st_mode, stats.st_rdev) == 0) { + fchmodat(dirfd(dir_to), dent->d_name, stats.st_mode & 0777, 0); + fchownat(dirfd(dir_to), dent->d_name, stats.st_uid, stats.st_gid, 0); + } else { + utimensat(dirfd(dir_to), dent->d_name, NULL, 0); + } + udev_selinux_resetfscreatecon(udev); + } else if (S_ISLNK(stats.st_mode)) { + char target[UTIL_PATH_SIZE]; + ssize_t len; + + len = readlinkat(dirfd(dir_from), dent->d_name, target, sizeof(target)); + if (len <= 0 || len == (ssize_t)sizeof(target)) + continue; + target[len] = '\0'; + udev_selinux_setfscreateconat(udev, dirfd(dir_to), dent->d_name, S_IFLNK); + if (symlinkat(target, dirfd(dir_to), dent->d_name) < 0 && errno == EEXIST) + utimensat(dirfd(dir_to), dent->d_name, NULL, AT_SYMLINK_NOFOLLOW); + udev_selinux_resetfscreatecon(udev); + } else if (S_ISDIR(stats.st_mode)) { + DIR *dir2_from, *dir2_to; + + if (maxdepth == 0) + continue; + + udev_selinux_setfscreateconat(udev, dirfd(dir_to), dent->d_name, S_IFDIR|0755); + mkdirat(dirfd(dir_to), dent->d_name, 0755); + udev_selinux_resetfscreatecon(udev); + + dir2_to = fdopendir(openat(dirfd(dir_to), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)); + if (dir2_to == NULL) + continue; + + dir2_from = fdopendir(openat(dirfd(dir_from), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)); + if (dir2_from == NULL) { + closedir(dir2_to); + continue; + } + + copy_dev_dir(udev, dir2_from, dir2_to, maxdepth-1); + + closedir(dir2_to); + closedir(dir2_from); + } + } + + return 0; +} + +static void static_dev_create_links(struct udev *udev, DIR *dir) +{ + struct stdlinks { + const char *link; + const char *target; + }; + static const struct stdlinks stdlinks[] = { + { "core", "/proc/kcore" }, + { "fd", "/proc/self/fd" }, + { "stdin", "/proc/self/fd/0" }, + { "stdout", "/proc/self/fd/1" }, + { "stderr", "/proc/self/fd/2" }, + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(stdlinks); i++) { + struct stat sb; + + if (stat(stdlinks[i].target, &sb) == 0) { + udev_selinux_setfscreateconat(udev, dirfd(dir), stdlinks[i].link, S_IFLNK); + if (symlinkat(stdlinks[i].target, dirfd(dir), stdlinks[i].link) < 0 && errno == EEXIST) + utimensat(dirfd(dir), stdlinks[i].link, NULL, AT_SYMLINK_NOFOLLOW); + udev_selinux_resetfscreatecon(udev); + } + } +} + +static void static_dev_create_from_devices(struct udev *udev, DIR *dir) +{ + DIR *dir_from; + + dir_from = opendir(UDEVLIBEXECDIR "/devices"); + if (dir_from == NULL) + return; + copy_dev_dir(udev, dir_from, dir, 8); + closedir(dir_from); +} + +static void static_dev_create(struct udev *udev) +{ + DIR *dir; + + dir = opendir(udev_get_dev_path(udev)); + if (dir == NULL) + return; + + static_dev_create_links(udev, dir); + static_dev_create_from_devices(udev, dir); + + closedir(dir); +} + +static int mem_size_mb(void) +{ + FILE *f; + char buf[4096]; + long int memsize = -1; + + f = fopen("/proc/meminfo", "r"); + if (f == NULL) + return -1; + + while (fgets(buf, sizeof(buf), f) != NULL) { + long int value; + + if (sscanf(buf, "MemTotal: %ld kB", &value) == 1) { + memsize = value / 1024; + break; + } + } + + fclose(f); + return memsize; +} + +static int convert_db(struct udev *udev) +{ + char filename[UTIL_PATH_SIZE]; + FILE *f; + struct udev_enumerate *udev_enumerate; + struct udev_list_entry *list_entry; + + /* current database */ + util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/data", NULL); + if (access(filename, F_OK) >= 0) + return 0; + + /* make sure we do not get here again */ + util_create_path(udev, filename); + mkdir(filename, 0755); + + /* old database */ + util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/db", NULL); + if (access(filename, F_OK) < 0) + return 0; + + f = fopen("/dev/kmsg", "w"); + if (f != NULL) { + fprintf(f, "<30>udevd[%u]: converting old udev database\n", getpid()); + fclose(f); + } + + udev_enumerate = udev_enumerate_new(udev); + if (udev_enumerate == NULL) + return -1; + udev_enumerate_scan_devices(udev_enumerate); + udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) { + struct udev_device *device; + + device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry)); + if (device == NULL) + continue; + + /* try to find the old database for devices without a current one */ + if (udev_device_read_db(device, NULL) < 0) { + bool have_db; + const char *id; + struct stat stats; + char devpath[UTIL_PATH_SIZE]; + char from[UTIL_PATH_SIZE]; + + have_db = false; + + /* find database in old location */ + id = udev_device_get_id_filename(device); + util_strscpyl(from, sizeof(from), udev_get_dev_path(udev), "/.udev/db/", id, NULL); + if (lstat(from, &stats) == 0) { + if (!have_db) { + udev_device_read_db(device, from); + have_db = true; + } + unlink(from); + } + + /* find old database with $subsys:$sysname name */ + util_strscpyl(from, sizeof(from), udev_get_dev_path(udev), + "/.udev/db/", udev_device_get_subsystem(device), ":", + udev_device_get_sysname(device), NULL); + if (lstat(from, &stats) == 0) { + if (!have_db) { + udev_device_read_db(device, from); + have_db = true; + } + unlink(from); + } + + /* find old database with the encoded devpath name */ + util_path_encode(udev_device_get_devpath(device), devpath, sizeof(devpath)); + util_strscpyl(from, sizeof(from), udev_get_dev_path(udev), "/.udev/db/", devpath, NULL); + if (lstat(from, &stats) == 0) { + if (!have_db) { + udev_device_read_db(device, from); + have_db = true; + } + unlink(from); + } + + /* write out new database */ + if (have_db) + udev_device_update_db(device); + } + udev_device_unref(device); + } + udev_enumerate_unref(udev_enumerate); + return 0; +} + +static int systemd_fds(struct udev *udev, int *rctrl, int *rnetlink) +{ + int ctrl = -1, netlink = -1; + int fd, n; + + n = sd_listen_fds(true); + if (n <= 0) + return -1; + + for (fd = SD_LISTEN_FDS_START; fd < n + SD_LISTEN_FDS_START; fd++) { + if (sd_is_socket(fd, AF_LOCAL, SOCK_SEQPACKET, -1)) { + if (ctrl >= 0) + return -1; + ctrl = fd; + continue; + } + + if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1)) { + if (netlink >= 0) + return -1; + netlink = fd; + continue; + } + + return -1; + } + + if (ctrl < 0 || netlink < 0) + return -1; + + info(udev, "ctrl=%i netlink=%i\n", ctrl, netlink); + *rctrl = ctrl; + *rnetlink = netlink; + return 0; +} + +static bool check_rules_timestamp(struct udev *udev) +{ + char **p; + unsigned long long *stamp_usec; + int i, n; + bool changed = false; + + n = udev_get_rules_path(udev, &p, &stamp_usec); + for (i = 0; i < n; i++) { + struct stat stats; + + if (stat(p[i], &stats) < 0) + continue; + + if (stamp_usec[i] == ts_usec(&stats.st_mtim)) + continue; + + /* first check */ + if (stamp_usec[i] != 0) { + info(udev, "reload - timestamp of '%s' changed\n", p[i]); + changed = true; + } + + /* update timestamp */ + stamp_usec[i] = ts_usec(&stats.st_mtim); + } + + return changed; +} + +int main(int argc, char *argv[]) +{ + struct udev *udev; + FILE *f; + sigset_t mask; + int daemonize = false; + int resolve_names = 1; + static const struct option options[] = { + { "daemon", no_argument, NULL, 'd' }, + { "debug", no_argument, NULL, 'D' }, + { "children-max", required_argument, NULL, 'c' }, + { "exec-delay", required_argument, NULL, 'e' }, + { "resolve-names", required_argument, NULL, 'N' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + {} + }; + int fd_ctrl = -1; + int fd_netlink = -1; + int fd_worker = -1; + struct epoll_event ep_ctrl, ep_inotify, ep_signal, ep_netlink, ep_worker; + struct udev_ctrl_connection *ctrl_conn = NULL; + char **s; + int rc = 1; + + udev = udev_new(); + if (udev == NULL) + goto exit; + + udev_log_init("udevd"); + udev_set_log_fn(udev, udev_main_log); + info(udev, "version %s\n", VERSION); + udev_selinux_init(udev); + + for (;;) { + int option; + + option = getopt_long(argc, argv, "c:deDtN:hV", options, NULL); + if (option == -1) + break; + + switch (option) { + case 'd': + daemonize = true; + break; + case 'c': + children_max = strtoul(optarg, NULL, 0); + break; + case 'e': + exec_delay = strtoul(optarg, NULL, 0); + break; + case 'D': + debug = true; + if (udev_get_log_priority(udev) < LOG_INFO) + udev_set_log_priority(udev, LOG_INFO); + break; + case 'N': + if (strcmp (optarg, "early") == 0) { + resolve_names = 1; + } else if (strcmp (optarg, "late") == 0) { + resolve_names = 0; + } else if (strcmp (optarg, "never") == 0) { + resolve_names = -1; + } else { + fprintf(stderr, "resolve-names must be early, late or never\n"); + err(udev, "resolve-names must be early, late or never\n"); + goto exit; + } + break; + case 'h': + printf("Usage: udevd OPTIONS\n" + " --daemon\n" + " --debug\n" + " --children-max=\n" + " --exec-delay=\n" + " --resolve-names=early|late|never\n" + " --version\n" + " --help\n" + "\n"); + goto exit; + case 'V': + printf("%s\n", VERSION); + goto exit; + default: + goto exit; + } + } + + /* + * read the kernel commandline, in case we need to get into debug mode + * udev.log-priority= syslog priority + * udev.children-max= events are fully serialized if set to 1 + * + */ + f = fopen("/proc/cmdline", "r"); + if (f != NULL) { + char cmdline[4096]; + + if (fgets(cmdline, sizeof(cmdline), f) != NULL) { + char *pos; + + pos = strstr(cmdline, "udev.log-priority="); + if (pos != NULL) { + pos += strlen("udev.log-priority="); + udev_set_log_priority(udev, util_log_priority(pos)); + } + + pos = strstr(cmdline, "udev.children-max="); + if (pos != NULL) { + pos += strlen("udev.children-max="); + children_max = strtoul(pos, NULL, 0); + } + + pos = strstr(cmdline, "udev.exec-delay="); + if (pos != NULL) { + pos += strlen("udev.exec-delay="); + exec_delay = strtoul(pos, NULL, 0); + } + } + fclose(f); + } + + if (getuid() != 0) { + fprintf(stderr, "root privileges required\n"); + err(udev, "root privileges required\n"); + goto exit; + } + + /* set umask before creating any file/directory */ + chdir("/"); + umask(022); + + /* /run/udev */ + mkdir(udev_get_run_path(udev), 0755); + + /* create standard links, copy static nodes, create nodes from modules */ + static_dev_create(udev); + static_dev_create_from_modules(udev); + + /* before opening new files, make sure std{in,out,err} fds are in a sane state */ + if (daemonize) { + int fd; + + fd = open("/dev/null", O_RDWR); + if (fd >= 0) { + if (write(STDOUT_FILENO, 0, 0) < 0) + dup2(fd, STDOUT_FILENO); + if (write(STDERR_FILENO, 0, 0) < 0) + dup2(fd, STDERR_FILENO); + if (fd > STDERR_FILENO) + close(fd); + } else { + fprintf(stderr, "cannot open /dev/null\n"); + err(udev, "cannot open /dev/null\n"); + } + } + + if (systemd_fds(udev, &fd_ctrl, &fd_netlink) >= 0) { + /* get control and netlink socket from from systemd */ + udev_ctrl = udev_ctrl_new_from_fd(udev, fd_ctrl); + if (udev_ctrl == NULL) { + err(udev, "error taking over udev control socket"); + rc = 1; + goto exit; + } + + monitor = udev_monitor_new_from_netlink_fd(udev, "kernel", fd_netlink); + if (monitor == NULL) { + err(udev, "error taking over netlink socket\n"); + rc = 3; + goto exit; + } + } else { + /* open control and netlink socket */ + udev_ctrl = udev_ctrl_new(udev); + if (udev_ctrl == NULL) { + fprintf(stderr, "error initializing udev control socket"); + err(udev, "error initializing udev control socket"); + rc = 1; + goto exit; + } + fd_ctrl = udev_ctrl_get_fd(udev_ctrl); + + monitor = udev_monitor_new_from_netlink(udev, "kernel"); + if (monitor == NULL) { + fprintf(stderr, "error initializing netlink socket\n"); + err(udev, "error initializing netlink socket\n"); + rc = 3; + goto exit; + } + fd_netlink = udev_monitor_get_fd(monitor); + } + + if (udev_monitor_enable_receiving(monitor) < 0) { + fprintf(stderr, "error binding netlink socket\n"); + err(udev, "error binding netlink socket\n"); + rc = 3; + goto exit; + } + + if (udev_ctrl_enable_receiving(udev_ctrl) < 0) { + fprintf(stderr, "error binding udev control socket\n"); + err(udev, "error binding udev control socket\n"); + rc = 1; + goto exit; + } + + udev_monitor_set_receive_buffer_size(monitor, 128*1024*1024); + + /* create queue file before signalling 'ready', to make sure we block 'settle' */ + udev_queue_export = udev_queue_export_new(udev); + if (udev_queue_export == NULL) { + err(udev, "error creating queue file\n"); + goto exit; + } + + if (daemonize) { + pid_t pid; + int fd; + + pid = fork(); + switch (pid) { + case 0: + break; + case -1: + err(udev, "fork of daemon failed: %m\n"); + rc = 4; + goto exit; + default: + rc = EXIT_SUCCESS; + goto exit_daemonize; + } + + setsid(); + + fd = open("/proc/self/oom_score_adj", O_RDWR); + if (fd < 0) { + /* Fallback to old interface */ + fd = open("/proc/self/oom_adj", O_RDWR); + if (fd < 0) { + err(udev, "error disabling OOM: %m\n"); + } else { + /* OOM_DISABLE == -17 */ + write(fd, "-17", 3); + close(fd); + } + } else { + write(fd, "-1000", 5); + close(fd); + } + } else { + sd_notify(1, "READY=1"); + } + + f = fopen("/dev/kmsg", "w"); + if (f != NULL) { + fprintf(f, "<30>udevd[%u]: starting version " VERSION "\n", getpid()); + fclose(f); + } + + if (!debug) { + int fd; + + fd = open("/dev/null", O_RDWR); + if (fd >= 0) { + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + close(fd); + } + } + + fd_inotify = udev_watch_init(udev); + if (fd_inotify < 0) { + fprintf(stderr, "error initializing inotify\n"); + err(udev, "error initializing inotify\n"); + rc = 4; + goto exit; + } + udev_watch_restore(udev); + + /* block and listen to all signals on signalfd */ + sigfillset(&mask); + sigprocmask(SIG_SETMASK, &mask, &sigmask_orig); + fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); + if (fd_signal < 0) { + fprintf(stderr, "error creating signalfd\n"); + err(udev, "error creating signalfd\n"); + rc = 5; + goto exit; + } + + /* unnamed socket from workers to the main daemon */ + if (socketpair(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0, worker_watch) < 0) { + fprintf(stderr, "error creating socketpair\n"); + err(udev, "error creating socketpair\n"); + rc = 6; + goto exit; + } + fd_worker = worker_watch[READ_END]; + + udev_builtin_init(udev); + + rules = udev_rules_new(udev, resolve_names); + if (rules == NULL) { + err(udev, "error reading rules\n"); + goto exit; + } + + memset(&ep_ctrl, 0, sizeof(struct epoll_event)); + ep_ctrl.events = EPOLLIN; + ep_ctrl.data.fd = fd_ctrl; + + memset(&ep_inotify, 0, sizeof(struct epoll_event)); + ep_inotify.events = EPOLLIN; + ep_inotify.data.fd = fd_inotify; + + memset(&ep_signal, 0, sizeof(struct epoll_event)); + ep_signal.events = EPOLLIN; + ep_signal.data.fd = fd_signal; + + memset(&ep_netlink, 0, sizeof(struct epoll_event)); + ep_netlink.events = EPOLLIN; + ep_netlink.data.fd = fd_netlink; + + memset(&ep_worker, 0, sizeof(struct epoll_event)); + ep_worker.events = EPOLLIN; + ep_worker.data.fd = fd_worker; + + fd_ep = epoll_create1(EPOLL_CLOEXEC); + if (fd_ep < 0) { + err(udev, "error creating epoll fd: %m\n"); + goto exit; + } + if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_ctrl, &ep_ctrl) < 0 || + epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_inotify, &ep_inotify) < 0 || + epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 || + epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_netlink, &ep_netlink) < 0 || + epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_worker, &ep_worker) < 0) { + err(udev, "fail to add fds to epoll: %m\n"); + goto exit; + } + + /* if needed, convert old database from earlier udev version */ + convert_db(udev); + + if (children_max <= 0) { + int memsize = mem_size_mb(); + + /* set value depending on the amount of RAM */ + if (memsize > 0) + children_max = 128 + (memsize / 8); + else + children_max = 128; + } + info(udev, "set children_max to %u\n", children_max); + + udev_rules_apply_static_dev_perms(rules); + + udev_list_node_init(&event_list); + udev_list_node_init(&worker_list); + + for (;;) { + static unsigned long long last_usec; + struct epoll_event ev[8]; + int fdcount; + int timeout; + bool is_worker, is_signal, is_inotify, is_netlink, is_ctrl; + int i; + + if (udev_exit) { + /* close sources of new events and discard buffered events */ + if (fd_ctrl >= 0) { + epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_ctrl, NULL); + fd_ctrl = -1; + } + if (monitor != NULL) { + epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_netlink, NULL); + udev_monitor_unref(monitor); + monitor = NULL; + } + if (fd_inotify >= 0) { + epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_inotify, NULL); + close(fd_inotify); + fd_inotify = -1; + } + + /* discard queued events and kill workers */ + event_queue_cleanup(udev, EVENT_QUEUED); + worker_kill(udev, 0); + + /* exit after all has cleaned up */ + if (udev_list_node_is_empty(&event_list) && udev_list_node_is_empty(&worker_list)) + break; + + /* timeout at exit for workers to finish */ + timeout = 30 * 1000; + } else if (udev_list_node_is_empty(&event_list) && children <= 2) { + /* we are idle */ + timeout = -1; + } else { + /* kill idle or hanging workers */ + timeout = 3 * 1000; + } + fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), timeout); + if (fdcount < 0) + continue; + + if (fdcount == 0) { + struct udev_list_node *loop; + + /* timeout */ + if (udev_exit) { + err(udev, "timeout, giving up waiting for workers to finish\n"); + break; + } + + /* kill idle workers */ + if (udev_list_node_is_empty(&event_list)) { + info(udev, "cleanup idle workers\n"); + worker_kill(udev, 2); + } + + /* check for hanging events */ + udev_list_node_foreach(loop, &worker_list) { + struct worker *worker = node_to_worker(loop); + + if (worker->state != WORKER_RUNNING) + continue; + + if ((now_usec() - worker->event_start_usec) > 30 * 1000 * 1000) { + err(udev, "worker [%u] timeout, kill it\n", worker->pid, + worker->event ? worker->event->devpath : ""); + kill(worker->pid, SIGKILL); + worker->state = WORKER_KILLED; + /* drop reference taken for state 'running' */ + worker_unref(worker); + if (worker->event) { + err(udev, "seq %llu '%s' killed\n", + udev_device_get_seqnum(worker->event->dev), worker->event->devpath); + worker->event->exitcode = -64; + event_queue_delete(worker->event, true); + worker->event = NULL; + } + } + } + + } + + is_worker = is_signal = is_inotify = is_netlink = is_ctrl = false; + for (i = 0; i < fdcount; i++) { + if (ev[i].data.fd == fd_worker && ev[i].events & EPOLLIN) + is_worker = true; + else if (ev[i].data.fd == fd_netlink && ev[i].events & EPOLLIN) + is_netlink = true; + else if (ev[i].data.fd == fd_signal && ev[i].events & EPOLLIN) + is_signal = true; + else if (ev[i].data.fd == fd_inotify && ev[i].events & EPOLLIN) + is_inotify = true; + else if (ev[i].data.fd == fd_ctrl && ev[i].events & EPOLLIN) + is_ctrl = true; + } + + /* check for changed config, every 3 seconds at most */ + if ((now_usec() - last_usec) > 3 * 1000 * 1000) { + if (check_rules_timestamp(udev)) + reload = true; + if (udev_builtin_validate(udev)) + reload = true; + + last_usec = now_usec(); + } + + /* reload requested, HUP signal received, rules changed, builtin changed */ + if (reload) { + worker_kill(udev, 0); + rules = udev_rules_unref(rules); + udev_builtin_exit(udev); + reload = 0; + } + + /* event has finished */ + if (is_worker) + worker_returned(fd_worker); + + if (is_netlink) { + struct udev_device *dev; + + dev = udev_monitor_receive_device(monitor); + if (dev != NULL) { + udev_device_set_usec_initialized(dev, now_usec()); + if (event_queue_insert(dev) < 0) + udev_device_unref(dev); + } + } + + /* start new events */ + if (!udev_list_node_is_empty(&event_list) && !udev_exit && !stop_exec_queue) { + if (rules == NULL) + rules = udev_rules_new(udev, resolve_names); + if (rules != NULL) + event_queue_start(udev); + } + + if (is_signal) { + struct signalfd_siginfo fdsi; + ssize_t size; + + size = read(fd_signal, &fdsi, sizeof(struct signalfd_siginfo)); + if (size == sizeof(struct signalfd_siginfo)) + handle_signal(udev, fdsi.ssi_signo); + } + + /* we are shutting down, the events below are not handled anymore */ + if (udev_exit) + continue; + + /* device node watch */ + if (is_inotify) + handle_inotify(udev); + + /* + * This needs to be after the inotify handling, to make sure, + * that the ping is send back after the possibly generated + * "change" events by the inotify device node watch. + * + * A single time we may receive a client connection which we need to + * keep open to block the client. It will be closed right before we + * exit. + */ + if (is_ctrl) + ctrl_conn = handle_ctrl_msg(udev_ctrl); + } + + rc = EXIT_SUCCESS; +exit: + udev_queue_export_cleanup(udev_queue_export); + udev_ctrl_cleanup(udev_ctrl); +exit_daemonize: + if (fd_ep >= 0) + close(fd_ep); + worker_list_cleanup(udev); + event_queue_cleanup(udev, EVENT_UNDEF); + udev_rules_unref(rules); + udev_builtin_exit(udev); + if (fd_signal >= 0) + close(fd_signal); + if (worker_watch[READ_END] >= 0) + close(worker_watch[READ_END]); + if (worker_watch[WRITE_END] >= 0) + close(worker_watch[WRITE_END]); + udev_monitor_unref(monitor); + udev_queue_export_unref(udev_queue_export); + udev_ctrl_connection_unref(ctrl_conn); + udev_ctrl_unref(udev_ctrl); + udev_selinux_exit(udev); + udev_unref(udev); + udev_log_close(); + return rc; +} diff --git a/src/udev/v4l_id/60-persistent-v4l.rules b/src/udev/v4l_id/60-persistent-v4l.rules new file mode 100644 index 0000000000..93c5ee8c27 --- /dev/null +++ b/src/udev/v4l_id/60-persistent-v4l.rules @@ -0,0 +1,20 @@ +# do not edit this file, it will be overwritten on update + +ACTION=="remove", GOTO="persistent_v4l_end" +SUBSYSTEM!="video4linux", GOTO="persistent_v4l_end" +ENV{MAJOR}=="", GOTO="persistent_v4l_end" + +IMPORT{program}="v4l_id $devnode" + +SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" +KERNEL=="video*", ENV{ID_SERIAL}=="?*", SYMLINK+="v4l/by-id/$env{ID_BUS}-$env{ID_SERIAL}-video-index$attr{index}" + +# check for valid "index" number +TEST!="index", GOTO="persistent_v4l_end" +ATTR{index}!="?*", GOTO="persistent_v4l_end" + +IMPORT{builtin}="path_id" +ENV{ID_PATH}=="?*", KERNEL=="video*|vbi*", SYMLINK+="v4l/by-path/$env{ID_PATH}-video-index$attr{index}" +ENV{ID_PATH}=="?*", KERNEL=="audio*", SYMLINK+="v4l/by-path/$env{ID_PATH}-audio-index$attr{index}" + +LABEL="persistent_v4l_end" diff --git a/src/udev/v4l_id/v4l_id.c b/src/udev/v4l_id/v4l_id.c new file mode 100644 index 0000000000..a2a80b5f43 --- /dev/null +++ b/src/udev/v4l_id/v4l_id.c @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2009 Kay Sievers + * Copyright (c) 2009 Filippo Argiolas + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details: + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main (int argc, char *argv[]) +{ + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + {} + }; + int fd; + char *device; + struct v4l2_capability v2cap; + + while (1) { + int option; + + option = getopt_long(argc, argv, "h", options, NULL); + if (option == -1) + break; + + switch (option) { + case 'h': + printf("Usage: v4l_id [--help] \n\n"); + return 0; + default: + return 1; + } + } + device = argv[optind]; + + if (device == NULL) + return 2; + fd = open (device, O_RDONLY); + if (fd < 0) + return 3; + + if (ioctl (fd, VIDIOC_QUERYCAP, &v2cap) == 0) { + printf("ID_V4L_VERSION=2\n"); + printf("ID_V4L_PRODUCT=%s\n", v2cap.card); + printf("ID_V4L_CAPABILITIES=:"); + if ((v2cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) > 0) + printf("capture:"); + if ((v2cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) > 0) + printf("video_output:"); + if ((v2cap.capabilities & V4L2_CAP_VIDEO_OVERLAY) > 0) + printf("video_overlay:"); + if ((v2cap.capabilities & V4L2_CAP_AUDIO) > 0) + printf("audio:"); + if ((v2cap.capabilities & V4L2_CAP_TUNER) > 0) + printf("tuner:"); + if ((v2cap.capabilities & V4L2_CAP_RADIO) > 0) + printf("radio:"); + printf("\n"); + } + + close (fd); + return 0; +} diff --git a/units/.gitignore b/units/.gitignore index 94412d52e7..f3b3cef133 100644 --- a/units/.gitignore +++ b/units/.gitignore @@ -40,3 +40,6 @@ systemd-update-utmp-runlevel.service systemd-update-utmp-shutdown.service test-env-replace systemd-binfmt.service +/udev-settle.service +/udev-trigger.service +/udev.service diff --git a/units/udev-control.socket b/units/udev-control.socket new file mode 100644 index 0000000000..f80f774427 --- /dev/null +++ b/units/udev-control.socket @@ -0,0 +1,10 @@ +[Unit] +Description=udev Control Socket +DefaultDependencies=no +ConditionCapability=CAP_MKNOD + +[Socket] +Service=udev.service +ListenSequentialPacket=/run/udev/control +SocketMode=0600 +PassCredentials=yes diff --git a/units/udev-kernel.socket b/units/udev-kernel.socket new file mode 100644 index 0000000000..23fa9d5e11 --- /dev/null +++ b/units/udev-kernel.socket @@ -0,0 +1,10 @@ +[Unit] +Description=udev Kernel Socket +DefaultDependencies=no +ConditionCapability=CAP_MKNOD + +[Socket] +Service=udev.service +ReceiveBuffer=134217728 +ListenNetlink=kobject-uevent 1 +PassCredentials=yes diff --git a/units/udev-settle.service.in b/units/udev-settle.service.in new file mode 100644 index 0000000000..b0a4964f76 --- /dev/null +++ b/units/udev-settle.service.in @@ -0,0 +1,25 @@ +# This service is usually not enabled by default. If enabled, it +# acts as a barrier for basic.target -- so all later services will +# wait for udev completely finishing its coldplug run. +# +# If needed, to work around broken or non-hotplug-aware services, +# it might be enabled unconditionally, or pulled-in on-demand by +# the services that assume a fully populated /dev at startup. It +# should not be used or pulled-in ever on systems without such +# legacy services running. + +[Unit] +Description=udev Wait for Complete Device Initialization +DefaultDependencies=no +Wants=udev.service +After=udev-trigger.service +Before=basic.target + +[Service] +Type=oneshot +TimeoutSec=180 +RemainAfterExit=yes +ExecStart=@bindir@/udevadm settle + +[Install] +WantedBy=basic.target diff --git a/units/udev-trigger.service.in b/units/udev-trigger.service.in new file mode 100644 index 0000000000..cd81945c88 --- /dev/null +++ b/units/udev-trigger.service.in @@ -0,0 +1,10 @@ +[Unit] +Description=udev Coldplug all Devices +Wants=udev.service +After=udev-kernel.socket udev-control.socket +DefaultDependencies=no + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@bindir@/udevadm trigger --type=subsystems --action=add ; @bindir@/udevadm trigger --type=devices --action=add diff --git a/units/udev.service.in b/units/udev.service.in new file mode 100644 index 0000000000..c27eb1baf5 --- /dev/null +++ b/units/udev.service.in @@ -0,0 +1,14 @@ +[Unit] +Description=udev Kernel Device Manager +Wants=udev-control.socket udev-kernel.socket +After=udev-control.socket udev-kernel.socket +Before=basic.target +DefaultDependencies=no +ConditionCapability=CAP_MKNOD + +[Service] +Type=notify +OOMScoreAdjust=-1000 +Sockets=udev-control.socket udev-kernel.socket +Restart=on-failure +ExecStart=@pkglibexecdir@/udevd -- cgit v1.2.3-54-g00ecf From e96d6be763014be75d480fde503d0b77f41194a0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 5 Apr 2012 22:08:10 +0200 Subject: systemd: add hardware watchdog support This adds minimal hardware watchdog support to PID 1. The idea is that PID 1 supervises and watchdogs system services, while the hardware watchdog is used to supervise PID 1. This adds two hardware watchdog configuration options, for the runtime watchdog and for a shutdown watchdog. The former is active during normal operation, the latter only at reboots to ensure that if a clean reboot times out we reboot nonetheless. If the runtime watchdog is enabled PID 1 will automatically wake up at half the configured interval and write to the watchdog daemon. By default we enable the shutdown watchdog, but leave the runtime watchdog disabled in order not to break independent hardware watchdog daemons people might be using. This is only the most basic hookup. If necessary we can later on hook up the watchdog ping more closely with services deemed crucial. --- .gitignore | 1 + Makefile.am | 17 +++++- TODO | 2 - man/systemd.conf.xml | 45 ++++++++++++++ src/dbus-manager.c | 19 ++++-- src/main.c | 52 +++++++++++++--- src/manager.c | 25 ++++++-- src/manager.h | 3 + src/shutdown.c | 8 ++- src/system.conf | 2 + src/test-watchdog.c | 51 ++++++++++++++++ src/watchdog.c | 166 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/watchdog.h | 31 ++++++++++ 13 files changed, 400 insertions(+), 22 deletions(-) create mode 100644 src/test-watchdog.c create mode 100644 src/watchdog.c create mode 100644 src/watchdog.h (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 16a6f583b5..af68896a76 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/test-watchdog /test-journal-send /systemd-multi-seat-x /systemd-cgtop diff --git a/Makefile.am b/Makefile.am index 9d05c7c829..5a8d22d613 100644 --- a/Makefile.am +++ b/Makefile.am @@ -229,7 +229,8 @@ noinst_PROGRAMS = \ test-cgroup \ test-env-replace \ test-strv \ - test-install + test-install \ + test-watchdog dist_pkgsysconf_DATA = \ src/system.conf \ @@ -612,6 +613,8 @@ libsystemd_core_la_SOURCES = \ src/tcpwrap.h \ src/cgroup-attr.c \ src/cgroup-attr.h \ + src/watchdog.c \ + src/watchdog.h \ src/sd-daemon.c \ src/sd-id128.c \ src/macro.h \ @@ -820,6 +823,14 @@ test_install_CFLAGS = \ test_install_LDADD = \ libsystemd-basic.la +test_watchdog_SOURCES = \ + src/test-watchdog.c \ + src/watchdog.c \ + src/watchdog.h + +test_watchdog_LDADD = \ + libsystemd-basic.la + systemd_initctl_SOURCES = \ src/initctl.c \ src/dbus-common.c @@ -862,7 +873,9 @@ systemd_shutdownd_LDADD = \ systemd_shutdown_SOURCES = \ src/mount-setup.c \ src/umount.c \ - src/shutdown.c + src/shutdown.c \ + src/watchdog.c \ + src/watchdog.h systemd_shutdown_LDADD = \ libsystemd-basic.la \ diff --git a/TODO b/TODO index b1207ba363..bee3b2b5a9 100644 --- a/TODO +++ b/TODO @@ -85,8 +85,6 @@ Features: * There's currently no way to cancel fsck (used to be possible via C-c or c on the console) -* hook up /dev/watchdog with main event loop for embedded, server uses - * when dumping cgroup contents, include main/control PID of a service, explicitly * keep an eye on https://bugzilla.gnome.org/show_bug.cgi?id=670100 diff --git a/man/systemd.conf.xml b/man/systemd.conf.xml index ba144da8cf..c7890287b5 100644 --- a/man/systemd.conf.xml +++ b/man/systemd.conf.xml @@ -149,6 +149,51 @@ controllers in separate hierarchies.
+ + + RuntimeWatchdogSec= + ShutdownWatchdogSec= + + Configure the hardware + watchdog at runtime and at + reboot. Takes a timeout value in + seconds (or in other time units if + suffixed with ms, + min, + h, + d, + w). If + RuntimeWatchdogSec= + is set to a non-zero value the + watchdog hardware + (/dev/watchdog) + will be programmed to automatically + reboot the system if it is not + contacted within the specified timeout + interval. The system manager will + ensure to contact it at least once in + half the specified timeout + interval. This feature requires a + hardware watchdog device to be + present, as it is commonly the case in + embedded and server systems. Not all + hardware watchdogs allow configuration + of the reboot timeout, in which case + the closest available timeout is + picked. ShutdownWatchdogSec= + may be used to configure the hardware + watchdog when the system is asked to + reboot. It works as a safety net to + ensure that the reboot takes place + even if a clean reboot attempt times + out. By default + RuntimeWatchdogSec= + defaults to 0 (off), and + ShutdownWatchdogSec= + to 10min. These settings have no + effect if a hardware watchdog is not + available. + diff --git a/src/dbus-manager.c b/src/dbus-manager.c index 6d272cb321..0a6e55d87d 100644 --- a/src/dbus-manager.c +++ b/src/dbus-manager.c @@ -240,7 +240,9 @@ " \n" \ " \n" \ " \n" \ - " \n" + " \n" \ + " \n" \ + " \n" #ifdef HAVE_SYSV_COMPAT #define BUS_MANAGER_INTERFACE_PROPERTIES_SYSV \ @@ -491,7 +493,10 @@ static int bus_manager_send_unit_files_changed(Manager *m) { return r; } -static const char systemd_property_string[] = PACKAGE_STRING "\0" DISTRIBUTION "\0" SYSTEMD_FEATURES; +static const char systemd_property_string[] = + PACKAGE_STRING "\0" + DISTRIBUTION "\0" + SYSTEMD_FEATURES; static const BusProperty bus_systemd_properties[] = { { "Version", bus_property_append_string, "s", 0 }, @@ -502,7 +507,7 @@ static const BusProperty bus_systemd_properties[] = { static const BusProperty bus_manager_properties[] = { { "RunningAs", bus_manager_append_running_as, "s", offsetof(Manager, running_as) }, - { "Tainted", bus_manager_append_tainted, "s", 0 }, + { "Tainted", bus_manager_append_tainted, "s", 0 }, { "InitRDTimestamp", bus_property_append_uint64, "t", offsetof(Manager, initrd_timestamp.realtime) }, { "InitRDTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, initrd_timestamp.monotonic) }, { "StartupTimestamp", bus_property_append_uint64, "t", offsetof(Manager, startup_timestamp.realtime) }, @@ -511,11 +516,11 @@ static const BusProperty bus_manager_properties[] = { { "FinishTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, finish_timestamp.monotonic) }, { "LogLevel", bus_manager_append_log_level, "s", 0, 0, bus_manager_set_log_level }, { "LogTarget", bus_manager_append_log_target, "s", 0, 0, bus_manager_set_log_target }, - { "NNames", bus_manager_append_n_names, "u", 0 }, - { "NJobs", bus_manager_append_n_jobs, "u", 0 }, + { "NNames", bus_manager_append_n_names, "u", 0 }, + { "NJobs", bus_manager_append_n_jobs, "u", 0 }, { "NInstalledJobs",bus_property_append_uint32, "u", offsetof(Manager, n_installed_jobs) }, { "NFailedJobs", bus_property_append_uint32, "u", offsetof(Manager, n_failed_jobs) }, - { "Progress", bus_manager_append_progress, "d", 0 }, + { "Progress", bus_manager_append_progress, "d", 0 }, { "Environment", bus_property_append_strv, "as", offsetof(Manager, environment), true }, { "ConfirmSpawn", bus_property_append_bool, "b", offsetof(Manager, confirm_spawn) }, { "ShowStatus", bus_property_append_bool, "b", offsetof(Manager, show_status) }, @@ -527,6 +532,8 @@ static const BusProperty bus_manager_properties[] = { { "DefaultControllers", bus_property_append_strv, "as", offsetof(Manager, default_controllers), true }, { "DefaultStandardOutput", bus_manager_append_exec_output, "s", offsetof(Manager, default_std_output) }, { "DefaultStandardError", bus_manager_append_exec_output, "s", offsetof(Manager, default_std_error) }, + { "RuntimeWatchdogUSec", bus_property_append_usec, "t", offsetof(Manager, runtime_watchdog), }, + { "ShutdownWatchdogUSec", bus_property_append_usec, "t", offsetof(Manager, shutdown_watchdog), }, #ifdef HAVE_SYSV_COMPAT { "SysVConsole", bus_property_append_bool, "b", offsetof(Manager, sysv_console) }, { "SysVInitPath", bus_property_append_strv, "as", offsetof(Manager, lookup_paths.sysvinit_path), true }, diff --git a/src/main.c b/src/main.c index 7ae88414a9..40be2b2033 100644 --- a/src/main.c +++ b/src/main.c @@ -54,6 +54,7 @@ #include "strv.h" #include "def.h" #include "virt.h" +#include "watchdog.h" static enum { ACTION_RUN, @@ -80,6 +81,8 @@ static char **arg_default_controllers = NULL; static char ***arg_join_controllers = NULL; static ExecOutput arg_default_std_output = EXEC_OUTPUT_JOURNAL; static ExecOutput arg_default_std_error = EXEC_OUTPUT_INHERIT; +static usec_t arg_runtime_watchdog = 0; +static usec_t arg_shutdown_watchdog = 10 * USEC_PER_MINUTE; static FILE* serialization = NULL; @@ -660,6 +663,8 @@ static int parse_config_file(void) { { "Manager", "DefaultStandardOutput", config_parse_output, 0, &arg_default_std_output }, { "Manager", "DefaultStandardError", config_parse_output, 0, &arg_default_std_error }, { "Manager", "JoinControllers", config_parse_join_controllers, 0, &arg_join_controllers }, + { "Manager", "RuntimeWatchdogSec", config_parse_usec, 0, &arg_runtime_watchdog }, + { "Manager", "ShutdownWatchdogSec", config_parse_usec, 0, &arg_shutdown_watchdog }, { NULL, NULL, NULL, 0, NULL } }; @@ -1163,6 +1168,7 @@ int main(int argc, char *argv[]) { bool is_reexec = false; int j; bool loaded_policy = false; + bool arm_reboot_watchdog = false; #ifdef HAVE_SYSV_COMPAT if (getpid() != 1 && strstr(program_invocation_short_name, "init")) { @@ -1391,7 +1397,11 @@ int main(int argc, char *argv[]) { test_cgroups(); } - if ((r = manager_new(arg_running_as, &m)) < 0) { + if (arg_running_as == MANAGER_SYSTEM && arg_runtime_watchdog > 0) + watchdog_set_timeout(&arg_runtime_watchdog); + + r = manager_new(arg_running_as, &m); + if (r < 0) { log_error("Failed to allocate manager object: %s", strerror(-r)); goto finish; } @@ -1404,6 +1414,8 @@ int main(int argc, char *argv[]) { m->swap_auto = arg_swap_auto; m->default_std_output = arg_default_std_output; m->default_std_error = arg_default_std_error; + m->runtime_watchdog = arg_runtime_watchdog; + m->shutdown_watchdog = arg_shutdown_watchdog; if (dual_timestamp_is_set(&initrd_timestamp)) m->initrd_timestamp = initrd_timestamp; @@ -1415,7 +1427,8 @@ int main(int argc, char *argv[]) { before_startup = now(CLOCK_MONOTONIC); - if ((r = manager_startup(m, serialization, fds)) < 0) + r = manager_startup(m, serialization, fds); + if (r < 0) log_error("Failed to fully start up daemon: %s", strerror(-r)); if (fds) { @@ -1438,7 +1451,8 @@ int main(int argc, char *argv[]) { log_debug("Activating default unit: %s", arg_default_unit); - if ((r = manager_load_unit(m, arg_default_unit, NULL, &error, &target)) < 0) { + r = manager_load_unit(m, arg_default_unit, NULL, &error, &target); + if (r < 0) { log_error("Failed to load default target: %s", bus_error(&error, r)); dbus_error_free(&error); } else if (target->load_state == UNIT_ERROR) @@ -1449,7 +1463,8 @@ int main(int argc, char *argv[]) { if (!target || target->load_state != UNIT_LOADED) { log_info("Trying to load rescue target..."); - if ((r = manager_load_unit(m, SPECIAL_RESCUE_TARGET, NULL, &error, &target)) < 0) { + r = manager_load_unit(m, SPECIAL_RESCUE_TARGET, NULL, &error, &target); + if (r < 0) { log_error("Failed to load rescue target: %s", bus_error(&error, r)); dbus_error_free(&error); goto finish; @@ -1491,7 +1506,8 @@ int main(int argc, char *argv[]) { } for (;;) { - if ((r = manager_loop(m)) < 0) { + r = manager_loop(m); + if (r < 0) { log_error("Failed to run mainloop: %s", strerror(-r)); goto finish; } @@ -1505,7 +1521,8 @@ int main(int argc, char *argv[]) { case MANAGER_RELOAD: log_info("Reloading."); - if ((r = manager_reload(m)) < 0) + r = manager_reload(m); + if (r < 0) log_error("Failed to reload: %s", strerror(-r)); break; @@ -1529,6 +1546,7 @@ int main(int argc, char *argv[]) { }; assert_se(shutdown_verb = table[m->exit_code]); + arm_reboot_watchdog = m->exit_code == MANAGER_REBOOT; log_notice("Shutting down."); goto finish; @@ -1615,13 +1633,33 @@ finish: fdset_free(fds); if (shutdown_verb) { + char e[32]; + const char * command_line[] = { SYSTEMD_SHUTDOWN_BINARY_PATH, shutdown_verb, NULL }; + const char * env_block[] = { + NULL, + NULL + }; - execv(SYSTEMD_SHUTDOWN_BINARY_PATH, (char **) command_line); + if (arm_reboot_watchdog && arg_shutdown_watchdog > 0) { + /* If we reboot let's set the shutdown + * watchdog and tell the shutdown binary to + * repeatedly ping it */ + watchdog_set_timeout(&arg_shutdown_watchdog); + watchdog_close(false); + + /* Tell the binary how often to ping */ + snprintf(e, sizeof(e), "WATCHDOG_USEC=%llu", (unsigned long long) arg_shutdown_watchdog); + char_array_0(e); + env_block[0] = e; + } else + watchdog_close(true); + + execve(SYSTEMD_SHUTDOWN_BINARY_PATH, (char **) command_line, (char**) env_block); log_error("Failed to execute shutdown binary, freezing: %m"); } diff --git a/src/manager.c b/src/manager.c index 74bd740747..be47766a73 100644 --- a/src/manager.c +++ b/src/manager.c @@ -61,6 +61,7 @@ #include "bus-errors.h" #include "exit-status.h" #include "virt.h" +#include "watchdog.h" /* As soon as 16 units are in our GC queue, make sure to run a gc sweep */ #define GC_QUEUE_ENTRIES_MAX 16 @@ -2433,6 +2434,7 @@ static int process_event(Manager *m, struct epoll_event *ev) { int manager_loop(Manager *m) { int r; + int wait_msec = -1; RATELIMIT_DEFINE(rl, 1*USEC_PER_SEC, 50000); @@ -2447,17 +2449,29 @@ int manager_loop(Manager *m) { /* There might still be some zombies hanging around from * before we were exec()'ed. Leat's reap them */ - if ((r = manager_dispatch_sigchld(m)) < 0) + r = manager_dispatch_sigchld(m); + if (r < 0) return r; + /* Sleep for half the watchdog time */ + if (m->runtime_watchdog > 0 && m->running_as == MANAGER_SYSTEM) { + wait_msec = (int) (m->runtime_watchdog / 2 / USEC_PER_MSEC); + if (wait_msec <= 0) + wait_msec = 1; + } + while (m->exit_code == MANAGER_RUNNING) { struct epoll_event event; int n; + if (wait_msec >= 0) + watchdog_ping(); + if (!ratelimit_test(&rl)) { /* Yay, something is going seriously wrong, pause a little */ log_warning("Looping too fast. Throttling execution a little."); sleep(1); + continue; } if (manager_dispatch_load_queue(m) > 0) @@ -2481,17 +2495,20 @@ int manager_loop(Manager *m) { if (swap_dispatch_reload(m) > 0) continue; - if ((n = epoll_wait(m->epoll_fd, &event, 1, -1)) < 0) { + n = epoll_wait(m->epoll_fd, &event, 1, wait_msec); + if (n < 0) { if (errno == EINTR) continue; return -errno; - } + } else if (n == 0) + continue; assert(n == 1); - if ((r = process_event(m, &event)) < 0) + r = process_event(m, &event); + if (r < 0) return r; } diff --git a/src/manager.h b/src/manager.h index a9d08f0a25..9ecc926cbf 100644 --- a/src/manager.h +++ b/src/manager.h @@ -144,6 +144,9 @@ struct Manager { char **environment; char **default_controllers; + usec_t runtime_watchdog; + usec_t shutdown_watchdog; + dual_timestamp initrd_timestamp; dual_timestamp startup_timestamp; dual_timestamp finish_timestamp; diff --git a/src/shutdown.c b/src/shutdown.c index d157e0fbfe..9f65b1dab0 100644 --- a/src/shutdown.c +++ b/src/shutdown.c @@ -42,6 +42,7 @@ #include "umount.h" #include "util.h" #include "virt.h" +#include "watchdog.h" #define TIMEOUT_USEC (5 * USEC_PER_SEC) #define FINALIZE_ATTEMPTS 50 @@ -306,7 +307,7 @@ int main(int argc, char *argv[]) { int cmd, r; unsigned retries; bool need_umount = true, need_swapoff = true, need_loop_detach = true, need_dm_detach = true; - bool killed_everbody = false, in_container; + bool killed_everbody = false, in_container, use_watchdog = false; log_parse_environment(); log_set_target(LOG_TARGET_CONSOLE); /* syslog will die if not gone yet */ @@ -342,6 +343,8 @@ int main(int argc, char *argv[]) { goto error; } + use_watchdog = !!getenv("WATCHDOG_USEC"); + /* lock us into memory */ if (mlockall(MCL_CURRENT|MCL_FUTURE) != 0) log_warning("Cannot lock process memory: %m"); @@ -359,6 +362,9 @@ int main(int argc, char *argv[]) { for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) { bool changed = false; + if (use_watchdog) + watchdog_ping(); + if (need_umount) { log_info("Unmounting file systems."); r = umount_all(&changed); diff --git a/src/system.conf b/src/system.conf index 33d09bccea..36eac07788 100644 --- a/src/system.conf +++ b/src/system.conf @@ -24,3 +24,5 @@ #DefaultStandardOutput=journal #DefaultStandardError=inherit #JoinControllers=cpu,cpuacct +#RuntimeWatchdog=0 +#ShutdownWatchdog=10min diff --git a/src/test-watchdog.c b/src/test-watchdog.c new file mode 100644 index 0000000000..d53fddbb9d --- /dev/null +++ b/src/test-watchdog.c @@ -0,0 +1,51 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "watchdog.h" +#include "log.h" + +int main(int argc, char *argv[]) { + usec_t t = 10 * USEC_PER_SEC; + unsigned i; + int r; + + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + + r = watchdog_set_timeout(&t); + if (r < 0) + log_warning("Failed to open watchdog: %s", strerror(-r)); + + for (i = 0; i < 5; i++) { + log_info("Pinging..."); + r = watchdog_ping(); + if (r < 0) + log_warning("Failed to ping watchdog: %s", strerror(-r)); + + usleep(t/2); + } + + watchdog_close(true); + return 0; +} diff --git a/src/watchdog.c b/src/watchdog.c new file mode 100644 index 0000000000..9625e151af --- /dev/null +++ b/src/watchdog.c @@ -0,0 +1,166 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "watchdog.h" +#include "log.h" + +static int watchdog_fd = -1; +static usec_t watchdog_timeout = (usec_t) -1; + +static int update_timeout(void) { + int r; + + if (watchdog_fd < 0) + return 0; + + if (watchdog_timeout == (usec_t) -1) + return 0; + else if (watchdog_timeout == 0) { + int flags; + + flags = WDIOS_DISABLECARD; + r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags); + if (r < 0) { + log_warning("Failed to disable hardware watchdog: %m"); + return -errno; + } + } else { + int sec, flags; + char buf[FORMAT_TIMESPAN_MAX]; + + sec = (int) ((watchdog_timeout + USEC_PER_SEC - 1) / USEC_PER_SEC); + r = ioctl(watchdog_fd, WDIOC_SETTIMEOUT, &sec); + if (r < 0) { + log_warning("Failed to set timeout to %is: %m", sec); + return -errno; + } + + watchdog_timeout = (usec_t) sec * USEC_PER_SEC; + log_info("Set hardware watchdog to %s.", format_timespan(buf, sizeof(buf), watchdog_timeout)); + + flags = WDIOS_ENABLECARD; + r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags); + if (r < 0) { + log_warning("Failed to enable hardware watchdog: %m"); + return -errno; + } + + r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0); + if (r < 0) { + log_warning("Failed to ping hardware watchdog: %m"); + return -errno; + } + } + + return 0; +} + +static int open_watchdog(void) { + struct watchdog_info ident; + + if (watchdog_fd >= 0) + return 0; + + watchdog_fd = open("/dev/watchdog", O_WRONLY|O_CLOEXEC); + if (watchdog_fd < 0) + return -errno; + + if (ioctl(watchdog_fd, WDIOC_GETSUPPORT, &ident) >= 0) + log_info("Hardware watchdog '%s', version %x", + ident.identity, + ident.firmware_version); + + return update_timeout(); +} + +int watchdog_set_timeout(usec_t *usec) { + + watchdog_timeout = *usec; + + /* If we didn't open the watchdog yet and didn't get any + * explicit timeout value set, don't do anything */ + if (watchdog_fd < 0 && watchdog_timeout == (usec_t) -1) + return 0; + + if (watchdog_fd < 0) + return open_watchdog(); + else + return update_timeout(); + + *usec = watchdog_timeout; +} + +int watchdog_ping(void) { + int r; + + if (watchdog_fd < 0) { + r = open_watchdog(); + if (r < 0) + return r; + } + + r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0); + if (r < 0) { + log_warning("Failed to ping hardware watchdog: %m"); + return -errno; + } + + return 0; +} + +void watchdog_close(bool disarm) { + int r; + + if (watchdog_fd < 0) + return; + + if (disarm) { + int flags; + + /* Explicitly disarm it */ + flags = WDIOS_DISABLECARD; + r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags); + if (r < 0) + log_warning("Failed to disable hardware watchdog: %m"); + + /* To be sure, use magic close logic, too */ + for (;;) { + static const char v = 'V'; + + if (write(watchdog_fd, &v, 1) > 0) + break; + + if (errno != EINTR) { + log_error("Failed to disarm watchdog timer: %m"); + break; + } + } + } + + close_nointr_nofail(watchdog_fd); + watchdog_fd = -1; +} diff --git a/src/watchdog.h b/src/watchdog.h new file mode 100644 index 0000000000..5ba70798ae --- /dev/null +++ b/src/watchdog.h @@ -0,0 +1,31 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foowatchdoghfoo +#define foowatchdoghfoo + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include "util.h" + +int watchdog_set_timeout(usec_t *usec); +int watchdog_ping(void); +void watchdog_close(bool disarm); + +#endif -- cgit v1.2.3-54-g00ecf From 04a9d3a00a9290cbf254d0b0ba2ce4521be632ae Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Tue, 10 Apr 2012 16:41:52 +0200 Subject: udev: fix gcc warnings showing up after adding $(AM_CFLAGS) --- .gitignore | 2 + Makefile.am | 125 ++++++++++++++++++++------------------- src/udev/udev-builtin-blkid.c | 1 - src/udev/udev-builtin-input_id.c | 5 +- src/udev/udev-builtin-usb_id.c | 7 +-- src/udev/udev-ctrl.c | 1 - src/udev/udev-event.c | 8 +-- src/udev/udev-node.c | 5 -- src/udev/udev-rules.c | 5 +- src/udev/udevadm-info.c | 1 - src/udev/udevadm-trigger.c | 1 - src/udev/udevd.c | 12 ++-- 12 files changed, 81 insertions(+), 92 deletions(-) (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index af68896a76..ba34a3a076 100644 --- a/.gitignore +++ b/.gitignore @@ -119,3 +119,5 @@ libtool /udevadm /udevd /v4l_id +/test-libudev +/test-udev diff --git a/Makefile.am b/Makefile.am index 39299e2feb..68a1a61045 100644 --- a/Makefile.am +++ b/Makefile.am @@ -84,6 +84,7 @@ INSTALL_DATA_HOOKS = DISTCHECK_HOOKS = DISTCLEAN_LOCAL_HOOKS = pkginclude_HEADERS = +noinst_LTLIBRARIES = lib_LTLIBRARIES = include_HEADERS = pkgconfiglib_DATA = @@ -549,12 +550,8 @@ EXTRA_DIST += \ $(MANPAGES_ALIAS) # ------------------------------------------------------------------------------ -noinst_LTLIBRARIES = \ - libsystemd-basic.la \ - libsystemd-capability.la \ - libsystemd-audit.la \ - libsystemd-acl.la \ - libsystemd-core.la +noinst_LTLIBRARIES += \ + libsystemd-basic.la libsystemd_basic_la_SOURCES = \ src/shared/util.c \ @@ -591,6 +588,9 @@ libsystemd_basic_la_LIBADD = \ $(SELINUX_LIBS) # ------------------------------------------------------------------------------ +noinst_LTLIBRARIES += \ + libsystemd-capability.la + libsystemd_capability_la_SOURCES = \ src/shared/capability.c \ src/shared/capability.h @@ -603,6 +603,9 @@ libsystemd_capability_la_LIBADD = \ $(CAP_LIBS) # ------------------------------------------------------------------------------ +noinst_LTLIBRARIES += \ + libsystemd-audit.la + libsystemd_audit_la_SOURCES = \ src/shared/audit.c \ src/shared/audit.h @@ -611,6 +614,9 @@ libsystemd_audit_la_LIBADD = \ libsystemd-capability.la # ------------------------------------------------------------------------------ +noinst_LTLIBRARIES += \ + libsystemd-acl.la + libsystemd_acl_la_SOURCES = \ src/shared/acl.c \ src/shared/acl.h @@ -623,6 +629,9 @@ libsystemd_acl_la_LIBADD = \ $(ACL_LIBS) # ------------------------------------------------------------------------------ +noinst_LTLIBRARIES += \ + libsystemd-core.la + libsystemd_core_la_SOURCES = \ src/unit.c \ src/unit.h \ @@ -1208,9 +1217,6 @@ include_HEADERS += \ lib_LTLIBRARIES += \ libudev.la -noinst_LTLIBRARIES += \ - libudev-private.la - libudev_la_SOURCES =\ src/udev/libudev-private.h \ src/udev/libudev.c \ @@ -1221,24 +1227,14 @@ libudev_la_SOURCES =\ src/udev/libudev-monitor.c \ src/udev/libudev-queue.c +libudev_la_CFLAGS = \ + $(AM_CFLAGS) \ + -fvisibility=hidden + libudev_la_LDFLAGS = \ $(AM_LDFLAGS) \ -version-info $(LIBUDEV_CURRENT):$(LIBUDEV_REVISION):$(LIBUDEV_AGE) -libudev_private_la_SOURCES =\ - $(libudev_la_SOURCES) \ - src/udev/libudev-util-private.c \ - src/udev/libudev-device-private.c \ - src/udev/libudev-queue-private.c - -if HAVE_SELINUX -libudev_private_la_SOURCES +=\ - src/udev/libudev-selinux-private.c -endif - -libudev_private_la_LIBADD = \ - $(SELINUX_LIBS) - pkgconfiglib_DATA += \ src/udev/libudev.pc @@ -1264,6 +1260,28 @@ libudev-uninstall-move-hook: INSTALL_EXEC_HOOKS += libudev-install-move-hook UNINSTALL_EXEC_HOOKS += libudev-uninstall-move-hook +# ------------------------------------------------------------------------------ +noinst_LTLIBRARIES += \ + libudev-private.la + +libudev_private_la_SOURCES =\ + $(libudev_la_SOURCES) \ + src/udev/libudev-util-private.c \ + src/udev/libudev-device-private.c \ + src/udev/libudev-queue-private.c + +libudev_private_la_CFLAGS = \ + $(AM_CFLAGS) \ + -fvisibility=default + +if HAVE_SELINUX +libudev_private_la_SOURCES +=\ + src/udev/libudev-selinux-private.c + +libudev_private_la_LIBADD = \ + $(SELINUX_LIBS) +endif + # ------------------------------------------------------------------------------ udev-confdirs: -mkdir -p $(DESTDIR)$(sysconfdir)/udev/rules.d @@ -1328,7 +1346,10 @@ bin_PROGRAMS += \ udevlibexec_PROGRAMS = \ udevd -udev_common_sources = \ +noinst_LTLIBRARIES += \ + libudev-core.la + +libudev_core_la_SOURCES = \ src/udev/udev.h \ src/udev/udev-event.c \ src/udev/udev-watch.c \ @@ -1342,51 +1363,43 @@ udev_common_sources = \ src/udev/udev-builtin-input_id.c \ src/udev/udev-builtin-kmod.c \ src/udev/udev-builtin-path_id.c \ - src/udev/udev-builtin-usb_id.c \ - src/systemd/sd-daemon.h \ - src/sd-daemon.c + src/udev/udev-builtin-usb_id.c -udev_common_CFLAGS = \ +libudev_core_la_CFLAGS = \ $(AM_CFLAGS) \ $(BLKID_CFLAGS) \ $(KMOD_CFLAGS) -udev_common_LDADD = \ +libudev_core_la_LIBADD = \ libudev-private.la \ + libsystemd-daemon.la \ $(BLKID_LIBS) \ $(KMOD_LIBS) -udev_common_CPPFLAGS = \ +libudev_core_la_CPPFLAGS = \ $(AM_CPPFLAGS) \ -DFIRMWARE_PATH="$(FIRMWARE_PATH)" \ -DUSB_DATABASE=\"$(USB_DATABASE)\" -DPCI_DATABASE=\"$(PCI_DATABASE)\" if HAVE_ACL -udev_common_sources += \ +libudev_core_la_SOURCES += \ src/udev/udev-builtin-uaccess.c \ src/login/logind-acl.c -udev_common_LDADD += \ +libudev_core_la_LIBADD += \ libsystemd-login.la \ libsystemd-acl.la endif udevd_SOURCES = \ - $(udev_common_sources) \ src/udev/udevd.c -udevd_CFLAGS = \ - $(udev_common_CFLAGS) - udevd_LDADD = \ - $(udev_common_LDADD) \ - libsystemd-basic.la - -udevd_CPPFLAGS = \ - $(udev_common_CPPFLAGS) + libudev-core.la \ + libsystemd-basic.la \ + libsystemd-daemon.la udevadm_SOURCES = \ - $(udev_common_sources) \ src/udev/udevadm.c \ src/udev/udevadm-info.c \ src/udev/udevadm-control.c \ @@ -1396,15 +1409,11 @@ udevadm_SOURCES = \ src/udev/udevadm-test.c \ src/udev/udevadm-test-builtin.c -udevadm_CFLAGS = \ - $(udev_common_CFLAGS) - udevadm_LDADD = \ - $(udev_common_LDADD) \ - libsystemd-basic.la - -udevadm_CPPFLAGS = \ - $(udev_common_CPPFLAGS) + libudev-core.la \ + libudev-private.la \ + libsystemd-basic.la \ + libsystemd-daemon.la # ------------------------------------------------------------------------------ TESTS = \ @@ -1422,19 +1431,13 @@ test_libudev_LDADD = \ libudev.la test_udev_SOURCES = \ - $(udev_common_sources) \ src/udev/test-udev.c -test_udev_CFLAGS = \ - $(udev_common_CFLAGS) - test_udev_LDADD = \ - $(udev_common_LDADD) \ + libudev-core.la \ + libudev-private.la \ libsystemd-basic.la -test_udev_CPPFLAGS = \ - $(udev_common_CPPFLAGS) - test_udev_DEPENDENCIES = \ src/udev/test/sys @@ -1467,7 +1470,7 @@ cdrom_id_SOURCES = \ src/udev/cdrom_id/cdrom_id.c cdrom_id_LDADD = \ - libudev-private.la \ + libudev.la \ libsystemd-basic.la udevlibexec_PROGRAMS += \ @@ -1508,7 +1511,7 @@ v4l_id_SOURCES = \ src/udev/v4l_id/v4l_id.c v4l_id_LDADD = \ - libudev-private.la + libudev.la udevlibexec_PROGRAMS += \ v4l_id @@ -1521,7 +1524,7 @@ accelerometer_SOURCES = \ src/udev/accelerometer/accelerometer.c accelerometer_LDADD = \ - libudev-private.la -lm \ + libudev.la -lm \ libsystemd-basic.la udevlibexec_PROGRAMS += \ diff --git a/src/udev/udev-builtin-blkid.c b/src/udev/udev-builtin-blkid.c index 174e20e2fa..c6dee9c3cf 100644 --- a/src/udev/udev-builtin-blkid.c +++ b/src/udev/udev-builtin-blkid.c @@ -115,7 +115,6 @@ static int probe_superblocks(blkid_probe pr) static int builtin_blkid(struct udev_device *dev, int argc, char *argv[], bool test) { - struct udev *udev = udev_device_get_udev(dev); int64_t offset = 0; bool noraid = false; int fd = -1; diff --git a/src/udev/udev-builtin-input_id.c b/src/udev/udev-builtin-input_id.c index ee8294b700..fce10a351a 100644 --- a/src/udev/udev-builtin-input_id.c +++ b/src/udev/udev-builtin-input_id.c @@ -38,6 +38,8 @@ #define LONG(x) ((x)/BITS_PER_LONG) #define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" /* * Read a capability attribute and return bitmask. * @param dev udev_device @@ -49,7 +51,6 @@ static void get_cap_mask(struct udev_device *dev, unsigned long *bitmask, size_t bitmask_size, bool test) { - struct udev *udev = udev_device_get_udev(dev); char text[4096]; unsigned i; char* word; @@ -87,6 +88,7 @@ static void get_cap_mask(struct udev_device *dev, log_debug(text, i * BITS_PER_LONG, bitmask[i]); } } +#pragma GCC diagnostic pop /* pointer devices */ static void test_pointers (struct udev_device *dev, @@ -143,7 +145,6 @@ static void test_key (struct udev_device *dev, const unsigned long* bitmask_key, bool test) { - struct udev *udev = udev_device_get_udev(dev); unsigned i; unsigned long found; unsigned long mask; diff --git a/src/udev/udev-builtin-usb_id.c b/src/udev/udev-builtin-usb_id.c index 6481b35ae7..18e10c4cc8 100644 --- a/src/udev/udev-builtin-usb_id.c +++ b/src/udev/udev-builtin-usb_id.c @@ -33,7 +33,7 @@ static void set_usb_iftype(char *to, int if_class_num, size_t len) { - char *type = "generic"; + const char *type = "generic"; switch (if_class_num) { case 1: @@ -86,7 +86,7 @@ static int set_usb_mass_storage_ifsubtype(char *to, const char *from, size_t len { int type_num = 0; char *eptr; - char *type = "generic"; + const char *type = "generic"; type_num = strtoul(from, &eptr, 0); if (eptr != from) { @@ -119,7 +119,7 @@ static void set_scsi_type(char *to, const char *from, size_t len) { int type_num; char *eptr; - char *type = "generic"; + const char *type = "generic"; type_num = strtoul(from, &eptr, 0); if (eptr != from) { @@ -256,7 +256,6 @@ static int builtin_usb_id(struct udev_device *dev, int argc, char *argv[], bool const char *driver = NULL; char serial[256]; - struct udev *udev = udev_device_get_udev(dev); struct udev_device *dev_interface = NULL; struct udev_device *dev_usb = NULL; const char *if_class, *if_subclass; diff --git a/src/udev/udev-ctrl.c b/src/udev/udev-ctrl.c index dd345f80a2..71c330f6f9 100644 --- a/src/udev/udev-ctrl.c +++ b/src/udev/udev-ctrl.c @@ -337,7 +337,6 @@ int udev_ctrl_send_exit(struct udev_ctrl *uctrl, int timeout) struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) { - struct udev *udev = conn->uctrl->udev; struct udev_ctrl_msg *uctrl_msg; ssize_t size; struct msghdr smsg; diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c index afbfebb3a9..debda61eed 100644 --- a/src/udev/udev-event.c +++ b/src/udev/udev-event.c @@ -86,8 +86,8 @@ size_t udev_event_apply_format(struct udev_event *event, const char *src, char * SUBST_SYS, }; static const struct subst_map { - char *name; - char fmt; + const char *name; + const char fmt; enum subst_type type; } map[] = { { .name = "devnode", .fmt = 'N', .type = SUBST_DEVNODE }, @@ -386,7 +386,6 @@ static int spawn_exec(struct udev_event *event, const char *cmd, char *const argv[], char **envp, const sigset_t *sigmask, int fd_stdout, int fd_stderr) { - struct udev *udev = event->udev; int err; int fd; @@ -433,7 +432,6 @@ static void spawn_read(struct udev_event *event, int fd_stdout, int fd_stderr, char *result, size_t ressize) { - struct udev *udev = event->udev; size_t respos = 0; int fd_ep = -1; struct epoll_event ep_outpipe, ep_errpipe; @@ -553,7 +551,6 @@ out: static int spawn_wait(struct udev_event *event, const char *cmd, pid_t pid) { - struct udev *udev = event->udev; struct pollfd pfd[1]; int err = 0; @@ -939,7 +936,6 @@ int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules, udev_device_unref(event->dev_db); event->dev_db = NULL; } -out: return err; } diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c index 95edd972f2..8e1318872a 100644 --- a/src/udev/udev-node.c +++ b/src/udev/udev-node.c @@ -240,7 +240,6 @@ static void link_update(struct udev_device *dev, const char *slink, bool add) void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old) { - struct udev *udev = udev_device_get_udev(dev); struct udev_list_entry *list_entry; /* update possible left-over symlinks */ @@ -322,7 +321,6 @@ void udev_node_add(struct udev_device *dev, mode_t mode, uid_t uid, gid_t gid) struct udev *udev = udev_device_get_udev(dev); char filename[UTIL_PATH_SIZE]; struct udev_list_entry *list_entry; - int err = 0; log_debug("handling device node '%s', devnum=%s, mode=%#o, uid=%d, gid=%d\n", udev_device_get_devnode(dev), udev_device_get_id_filename(dev), mode, uid, gid); @@ -351,9 +349,6 @@ void udev_node_remove(struct udev_device *dev) { struct udev *udev = udev_device_get_udev(dev); struct udev_list_entry *list_entry; - const char *devnode; - struct stat stats; - struct udev_device *dev_check; char filename[UTIL_PATH_SIZE]; /* remove/update symlinks, remove symlinks from name index */ diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index 401da5d8c5..c371298a69 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -781,7 +781,6 @@ static int import_program_into_properties(struct udev_event *event, const char * static int import_parent_into_properties(struct udev_device *dev, const char *filter) { - struct udev *udev = udev_device_get_udev(dev); struct udev_device *dev_parent; struct udev_list_entry *list_entry; @@ -969,7 +968,7 @@ static int get_key(struct udev *udev, char **line, char **key, enum operation_ty } /* extract possible KEY{attr} */ -static char *get_key_attribute(struct udev *udev, char *str) +static const char *get_key_attribute(struct udev *udev, char *str) { char *pos; char *attr; @@ -1178,7 +1177,7 @@ static int add_rule(struct udev_rules *rules, char *line, const char *filename, unsigned int filename_off, unsigned int lineno) { char *linepos; - char *attr; + const char *attr; struct rule_tmp rule_tmp; memset(&rule_tmp, 0x00, sizeof(struct rule_tmp)); diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c index 20a70cb458..f392818c83 100644 --- a/src/udev/udevadm-info.c +++ b/src/udev/udevadm-info.c @@ -52,7 +52,6 @@ static bool skip_attribute(const char *name) static void print_all_attributes(struct udev_device *device, const char *key) { - struct udev *udev = udev_device_get_udev(device); struct udev_list_entry *sysattr; udev_list_entry_foreach(sysattr, udev_device_get_sysattr_list_entry(device)) { diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c index 36a2864fa7..99ef12cb85 100644 --- a/src/udev/udevadm-trigger.c +++ b/src/udev/udevadm-trigger.c @@ -38,7 +38,6 @@ static int dry_run; static void exec_list(struct udev_enumerate *udev_enumerate, const char *action) { - struct udev *udev = udev_enumerate_get_udev(udev_enumerate); struct udev_list_entry *entry; udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(udev_enumerate)) { diff --git a/src/udev/udevd.c b/src/udev/udevd.c index 35478c19ca..fc33ff0117 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -880,11 +880,6 @@ static void static_dev_create_from_modules(struct udev *udev) static void static_dev_create_links(struct udev *udev) { DIR *dir; - - dir = opendir(udev_get_dev_path(udev)); - if (dir == NULL) - return; - struct stdlinks { const char *link; const char *target; @@ -898,6 +893,10 @@ static void static_dev_create_links(struct udev *udev) }; unsigned int i; + dir = opendir(udev_get_dev_path(udev)); + if (dir == NULL) + return; + for (i = 0; i < ARRAY_SIZE(stdlinks); i++) { struct stat sb; @@ -1115,7 +1114,6 @@ int main(int argc, char *argv[]) int fd_worker = -1; struct epoll_event ep_ctrl, ep_inotify, ep_signal, ep_netlink, ep_worker; struct udev_ctrl_connection *ctrl_conn = NULL; - char **s; int rc = 1; udev = udev_new(); @@ -1525,7 +1523,7 @@ int main(int argc, char *argv[]) continue; if ((now_usec() - worker->event_start_usec) > 30 * 1000 * 1000) { - log_error("worker [%u] timeout, kill it\n", worker->pid, + log_error("worker [%u] %s timeout; kill it\n", worker->pid, worker->event ? worker->event->devpath : ""); kill(worker->pid, SIGKILL); worker->state = WORKER_KILLED; -- cgit v1.2.3-54-g00ecf From b562f5a57d11d356aab26b08481f3befffff0822 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 13 Apr 2012 21:36:37 +0200 Subject: build-sys: add stub makefiles to all subdirs to ease development with emacs --- .gitignore | 138 ++++++++++++++++++------------------ src/ac-power/Makefile | 1 + src/analyze/Makefile | 1 + src/ask-password/Makefile | 1 + src/cgls/Makefile | 1 + src/cgroups-agent/Makefile | 1 + src/cgtop/Makefile | 1 + src/detect-virt/Makefile | 1 + src/fsck/Makefile | 1 + src/getty-generator/Makefile | 1 + src/gudev/Makefile | 1 + src/initctl/Makefile | 1 + src/libsystemd-daemon/Makefile | 1 + src/libsystemd-id128/Makefile | 1 + src/libudev/Makefile | 1 + src/modules-load/Makefile | 1 + src/notify/Makefile | 1 + src/nspawn/Makefile | 1 + src/quotacheck/Makefile | 1 + src/random-seed/Makefile | 1 + src/rc-local-generator/Makefile | 1 + src/remount-api-vfs/Makefile | 1 + src/reply-password/Makefile | 1 + src/shutdownd/Makefile | 1 + src/stdio-bridge/Makefile | 1 + src/sysctl/Makefile | 1 + src/systemctl/Makefile | 1 + src/timestamp/Makefile | 1 + src/tmpfiles/Makefile | 1 + src/tty-ask-password-agent/Makefile | 1 + src/udev/Makefile | 1 + src/update-utmp/Makefile | 1 + 32 files changed, 99 insertions(+), 70 deletions(-) create mode 120000 src/ac-power/Makefile create mode 120000 src/analyze/Makefile create mode 120000 src/ask-password/Makefile create mode 120000 src/cgls/Makefile create mode 120000 src/cgroups-agent/Makefile create mode 120000 src/cgtop/Makefile create mode 120000 src/detect-virt/Makefile create mode 120000 src/fsck/Makefile create mode 120000 src/getty-generator/Makefile create mode 120000 src/gudev/Makefile create mode 120000 src/initctl/Makefile create mode 120000 src/libsystemd-daemon/Makefile create mode 120000 src/libsystemd-id128/Makefile create mode 120000 src/libudev/Makefile create mode 120000 src/modules-load/Makefile create mode 120000 src/notify/Makefile create mode 120000 src/nspawn/Makefile create mode 120000 src/quotacheck/Makefile create mode 120000 src/random-seed/Makefile create mode 120000 src/rc-local-generator/Makefile create mode 120000 src/remount-api-vfs/Makefile create mode 120000 src/reply-password/Makefile create mode 120000 src/shutdownd/Makefile create mode 120000 src/stdio-bridge/Makefile create mode 120000 src/sysctl/Makefile create mode 120000 src/systemctl/Makefile create mode 120000 src/timestamp/Makefile create mode 120000 src/tmpfiles/Makefile create mode 120000 src/tty-ask-password-agent/Makefile create mode 120000 src/udev/Makefile create mode 120000 src/update-utmp/Makefile (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index ba34a3a076..2bfc77c7f8 100644 --- a/.gitignore +++ b/.gitignore @@ -6,73 +6,71 @@ /systemd-cat /systemd-rc-local-generator /libsystemd-id128.pc -journalctl -systemd-journald -test-id128 -test-journal -test-install -org.freedesktop.hostname1.xml -org.freedesktop.locale1.xml -libsystemd-daemon.pc -libsystemd-login.pc -test-login -loginctl -systemd-localed -systemd-timedated -org.freedesktop.timedate1.xml -systemd-logind -systemd-uaccess -systemd-hostnamed -systemd-binfmt -systemd-getty-generator -systemd-nspawn -systemd-stdio-bridge -systemd-machine-id-setup -systemd-detect-virt -systemd-sysctl -test-strv -systemd-ac-power -systemd-timestamp -systemd-cryptsetup -systemd-cryptsetup-generator -systemd-tty-ask-password-agent -systemd-fsck -systemd-quotacheck -systemd-user-sessions -systemd-shutdown -systemd-tmpfiles -systemd-readahead-collect -systemd-readahead-replay -systemd-reply-password -systemd-gnome-ask-password-agent -systemd-ask-password -systemd-kmsg-syslogd -systemd-remount-api-vfs -test-hostname -systemd-modules-load -systemd-vconsole-setup -systemd-shutdownd -systemd-random-seed -systemd-update-utmp -test-env-replace -systemd-cgls -systemd.pc -test-cgroup +/journalctl +/systemd-journald +/test-id128 +/test-journal +/test-install +/org.freedesktop.hostname1.xml +/org.freedesktop.locale1.xml +/libsystemd-daemon.pc +/libsystemd-login.pc +/test-login +/loginctl +/systemd-localed +/systemd-timedated +/org.freedesktop.timedate1.xml +/systemd-logind +/systemd-uaccess +/systemd-hostnamed +/systemd-binfmt +/systemd-getty-generator +/systemd-nspawn +/systemd-stdio-bridge +/systemd-machine-id-setup +/systemd-detect-virt +/systemd-sysctl +/test-strv +/systemd-ac-power +/systemd-timestamp +/systemd-cryptsetup +/systemd-cryptsetup-generator +/systemd-tty-ask-password-agent +/systemd-fsck +/systemd-quotacheck +/systemd-user-sessions +/systemd-shutdown +/systemd-tmpfiles +/systemd-readahead-collect +/systemd-readahead-replay +/systemd-reply-password +/systemd-gnome-ask-password-agent +/systemd-ask-password +/systemd-kmsg-syslogd +/systemd-remount-api-vfs +/test-hostname +/systemd-modules-load +/systemd-vconsole-setup +/systemd-shutdownd +/systemd-random-seed +/systemd-update-utmp +/test-env-replace +/systemd-cgls +/systemd.pc +/test-cgroup .libs/ -systemd-notify -test-daemon -systemd-install -org.freedesktop.systemd1.*.xml -test-ns -test-loopback -systemd-cgroups-agent -systemd-initctl +/systemd-notify +/test-daemon +/org.freedesktop.systemd1.*.xml +/test-ns +/test-loopback +/systemd-cgroups-agent +/systemd-initctl /systemd -test-engine -test-job-type -systemd-stdout-syslog-bridge -systemctl -systemadm +/test-engine +/test-job-type +/systemctl +/systemadm .dirstamp *.1 *.3 @@ -103,11 +101,11 @@ missing stamp-* *.stamp /Makefile -ltmain.sh -*.tar.xz -*.tar.gz -*.tar.bz2 -libtool +/ltmain.sh +/*.tar.xz +/*.tar.gz +/*.tar.bz2 +/libtool /accelerometer /ata_id /cdrom_id diff --git a/src/ac-power/Makefile b/src/ac-power/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/ac-power/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/analyze/Makefile b/src/analyze/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/analyze/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/ask-password/Makefile b/src/ask-password/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/ask-password/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/cgls/Makefile b/src/cgls/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/cgls/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/cgroups-agent/Makefile b/src/cgroups-agent/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/cgroups-agent/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/cgtop/Makefile b/src/cgtop/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/cgtop/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/detect-virt/Makefile b/src/detect-virt/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/detect-virt/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/fsck/Makefile b/src/fsck/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/fsck/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/getty-generator/Makefile b/src/getty-generator/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/getty-generator/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/gudev/Makefile b/src/gudev/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/gudev/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/initctl/Makefile b/src/initctl/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/initctl/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/libsystemd-daemon/Makefile b/src/libsystemd-daemon/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/libsystemd-daemon/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/libsystemd-id128/Makefile b/src/libsystemd-id128/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/libsystemd-id128/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/libudev/Makefile b/src/libudev/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/libudev/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/modules-load/Makefile b/src/modules-load/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/modules-load/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/notify/Makefile b/src/notify/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/notify/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/nspawn/Makefile b/src/nspawn/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/nspawn/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/quotacheck/Makefile b/src/quotacheck/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/quotacheck/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/random-seed/Makefile b/src/random-seed/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/random-seed/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/rc-local-generator/Makefile b/src/rc-local-generator/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/rc-local-generator/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/remount-api-vfs/Makefile b/src/remount-api-vfs/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/remount-api-vfs/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/reply-password/Makefile b/src/reply-password/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/reply-password/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/shutdownd/Makefile b/src/shutdownd/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/shutdownd/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/stdio-bridge/Makefile b/src/stdio-bridge/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/stdio-bridge/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/sysctl/Makefile b/src/sysctl/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/sysctl/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/systemctl/Makefile b/src/systemctl/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/systemctl/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/timestamp/Makefile b/src/timestamp/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/timestamp/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/tmpfiles/Makefile b/src/tmpfiles/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/tmpfiles/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/tty-ask-password-agent/Makefile b/src/tty-ask-password-agent/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/tty-ask-password-agent/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/udev/Makefile b/src/udev/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/udev/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/update-utmp/Makefile b/src/update-utmp/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/update-utmp/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file -- cgit v1.2.3-54-g00ecf From 3b6d9a7c50bd7217a0ca80d39fd30decbb0c935a Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Fri, 13 Apr 2012 21:57:39 +0200 Subject: update .gitignore --- .gitignore | 4 ---- src/core/.gitignore | 1 + src/libsystemd-daemon/.gitignore | 2 +- src/libsystemd-id128/.gitignore | 2 +- src/libudev/.gitignore | 2 +- src/login/.gitignore | 7 ++++--- 6 files changed, 8 insertions(+), 10 deletions(-) create mode 100644 src/core/.gitignore (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 2bfc77c7f8..cf1dff8212 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,6 @@ /systemd-coredump /systemd-cat /systemd-rc-local-generator -/libsystemd-id128.pc /journalctl /systemd-journald /test-id128 @@ -13,8 +12,6 @@ /test-install /org.freedesktop.hostname1.xml /org.freedesktop.locale1.xml -/libsystemd-daemon.pc -/libsystemd-login.pc /test-login /loginctl /systemd-localed @@ -56,7 +53,6 @@ /systemd-update-utmp /test-env-replace /systemd-cgls -/systemd.pc /test-cgroup .libs/ /systemd-notify diff --git a/src/core/.gitignore b/src/core/.gitignore new file mode 100644 index 0000000000..eef2e5971e --- /dev/null +++ b/src/core/.gitignore @@ -0,0 +1 @@ +/systemd.pc diff --git a/src/libsystemd-daemon/.gitignore b/src/libsystemd-daemon/.gitignore index 49ca83d0fe..5184131b28 100644 --- a/src/libsystemd-daemon/.gitignore +++ b/src/libsystemd-daemon/.gitignore @@ -1 +1 @@ -/*.pc +/libsystemd-daemon.pc diff --git a/src/libsystemd-id128/.gitignore b/src/libsystemd-id128/.gitignore index 49ca83d0fe..144adf9bea 100644 --- a/src/libsystemd-id128/.gitignore +++ b/src/libsystemd-id128/.gitignore @@ -1 +1 @@ -/*.pc +/libsystemd-id128.pc diff --git a/src/libudev/.gitignore b/src/libudev/.gitignore index aa0de7ee12..0c8a5d5231 100644 --- a/src/libudev/.gitignore +++ b/src/libudev/.gitignore @@ -1 +1 @@ -libudev.pc +/libudev.pc diff --git a/src/login/.gitignore b/src/login/.gitignore index 1c0f3995ed..6fb49e5ace 100644 --- a/src/login/.gitignore +++ b/src/login/.gitignore @@ -1,3 +1,4 @@ -logind-gperf.c -org.freedesktop.login1.policy -73-seat-late.rules +/libsystemd-login.pc +/logind-gperf.c +/org.freedesktop.login1.policy +/73-seat-late.rules -- cgit v1.2.3-54-g00ecf From f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 16 Apr 2012 16:47:33 +0200 Subject: logind: add shutdown/suspend/idle inhibition framework --- .gitignore | 1 + Makefile.am | 16 +- TODO | 4 +- src/login/logind-dbus.c | 316 ++++++++++++++++++++++--- src/login/logind-inhibit.c | 365 +++++++++++++++++++++++++++++ src/login/logind-inhibit.h | 76 ++++++ src/login/logind-session.c | 21 +- src/login/logind.c | 114 ++++++++- src/login/logind.h | 8 +- src/login/org.freedesktop.login1.conf | 8 + src/login/org.freedesktop.login1.policy.in | 30 +++ src/login/test-inhibit.c | 137 +++++++++++ src/shared/dbus-common.c | 50 ++++ src/shared/dbus-common.h | 2 + src/shared/polkit.c | 52 +--- src/shared/util.c | 3 +- 16 files changed, 1084 insertions(+), 119 deletions(-) create mode 100644 src/login/logind-inhibit.c create mode 100644 src/login/logind-inhibit.h create mode 100644 src/login/test-inhibit.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index cf1dff8212..af7a017b6d 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ /test-id128 /test-journal /test-install +/test-inhibit /org.freedesktop.hostname1.xml /org.freedesktop.locale1.xml /test-login diff --git a/Makefile.am b/Makefile.am index 1353d97ef2..0d2cb70349 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2577,6 +2577,8 @@ systemd_logind_SOURCES = \ src/login/logind-session.h \ src/login/logind-user.c \ src/login/logind-user.h \ + src/login/logind-inhibit.c \ + src/login/logind-inhibit.h \ src/login/logind-session-dbus.c \ src/login/logind-seat-dbus.c \ src/login/logind-user-dbus.c \ @@ -2638,8 +2640,20 @@ test_login_LDADD = \ libsystemd-login.la \ libsystemd-shared.la +test_inhibit_SOURCES = \ + src/login/test-inhibit.c + +test_inhibit_LDADD = \ + libsystemd-shared.la \ + libsystemd-dbus.la + +test_inhibit_CFLAGS = \ + $(AM_CFLAGS) \ + $(DBUS_CFLAGS) + noinst_PROGRAMS += \ - test-login + test-login \ + test-inhibit libsystemd_login_la_SOURCES = \ src/login/sd-login.c diff --git a/TODO b/TODO index 3586b41e3c..06a2afc54a 100644 --- a/TODO +++ b/TODO @@ -15,7 +15,9 @@ Bugfixes: Features: -* cg_create_and_attach() should fail for non-available controllers +* filter default cgroups in logind too + +* remove empty cgroups from cgls output * udev: remove /sys and /dev configurability diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 6b013b9fb2..d01cf1ae89 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -140,6 +140,15 @@ " \n" \ " \n" \ " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ " \n" \ " \n" \ " \n" \ @@ -174,6 +183,7 @@ " \n" \ " \n" \ " \n" \ + " \n" \ " \n" #define INTROSPECTION_BEGIN \ @@ -224,6 +234,20 @@ static int bus_manager_append_idle_hint_since(DBusMessageIter *i, const char *pr return 0; } +static int bus_manager_append_inhibited(DBusMessageIter *i, const char *property, void *data) { + Manager *m = data; + InhibitWhat w; + const char *p; + + w = manager_inhibit_what(m); + p = inhibit_what_to_string(w); + + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &p)) + return -ENOMEM; + + return 0; +} + static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMessage **_reply) { Session *session = NULL; User *user = NULL; @@ -466,9 +490,9 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess } else { do { free(id); - asprintf(&id, "c%lu", ++m->session_counter); + id = NULL; - if (!id) { + if (asprintf(&id, "c%lu", ++m->session_counter) < 0) { r = -ENOMEM; goto fail; } @@ -602,6 +626,122 @@ fail: return r; } +static int bus_manager_inhibit(Manager *m, DBusConnection *connection, DBusMessage *message, DBusError *error, DBusMessage **_reply) { + Inhibitor *i = NULL; + char *id = NULL; + const char *who, *why, *what; + pid_t pid; + InhibitWhat w; + unsigned long ul; + int r, fifo_fd = -1; + DBusMessage *reply = NULL; + + assert(m); + assert(connection); + assert(message); + assert(error); + assert(_reply); + + if (!dbus_message_get_args( + message, + error, + DBUS_TYPE_STRING, &what, + DBUS_TYPE_STRING, &who, + DBUS_TYPE_STRING, &why, + DBUS_TYPE_INVALID)) { + r = -EIO; + goto fail; + } + + w = inhibit_what_from_string(what); + if (w <= 0) { + r = -EINVAL; + goto fail; + } + + r = verify_polkit(connection, message, "org.freedesktop.login1.inhibit", false, NULL, error); + if (r < 0) + goto fail; + + ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), error); + if (ul == (unsigned long) -1) { + r = -EIO; + goto fail; + } + + pid = bus_get_unix_process_id(connection, dbus_message_get_sender(message), error); + if (pid <= 0) { + r = -EIO; + goto fail; + } + + do { + free(id); + id = NULL; + + if (asprintf(&id, "%lu", ++m->inhibit_counter) < 0) { + r = -ENOMEM; + goto fail; + } + } while (hashmap_get(m->inhibitors, id)); + + r = manager_add_inhibitor(m, id, &i); + free(id); + + if (r < 0) + goto fail; + + i->what = w; + i->pid = pid; + i->uid = (uid_t) ul; + i->why = strdup(why); + i->who = strdup(who); + + if (!i->why || !i->who) { + r = -ENOMEM; + goto fail; + } + + fifo_fd = inhibitor_create_fifo(i); + if (fifo_fd < 0) { + r = fifo_fd; + goto fail; + } + + reply = dbus_message_new_method_return(message); + if (!reply) { + r = -ENOMEM; + goto fail; + } + + if (!dbus_message_append_args( + reply, + DBUS_TYPE_UNIX_FD, &fifo_fd, + DBUS_TYPE_INVALID)) { + r = -ENOMEM; + goto fail; + } + + close_nointr_nofail(fifo_fd); + *_reply = reply; + + inhibitor_start(i); + + return 0; + +fail: + if (i) + inhibitor_free(i); + + if (fifo_fd >= 0) + close_nointr_nofail(fifo_fd); + + if (reply) + dbus_message_unref(reply); + + return r; +} + static int trigger_device(Manager *m, struct udev_device *d) { struct udev_enumerate *e; struct udev_list_entry *first, *item; @@ -780,6 +920,7 @@ static const BusProperty bus_login_manager_properties[] = { { "IdleHint", bus_manager_append_idle_hint, "b", 0 }, { "IdleSinceHint", bus_manager_append_idle_hint_since, "t", 0 }, { "IdleSinceHintMonotonic", bus_manager_append_idle_hint_since, "t", 0 }, + { "Inhibited", bus_manager_append_inhibited, "s", 0 }, { NULL, } }; @@ -1067,6 +1208,56 @@ static DBusHandlerResult manager_message_handler( if (!dbus_message_iter_close_container(&iter, &sub)) goto oom; + } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListInhibitors")) { + Inhibitor *inhibitor; + Iterator i; + DBusMessageIter iter, sub; + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + + dbus_message_iter_init_append(reply, &iter); + + if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sssuu)", &sub)) + goto oom; + + HASHMAP_FOREACH(inhibitor, m->inhibitors, i) { + DBusMessageIter sub2; + dbus_uint32_t uid, pid; + const char *what, *who, *why; + + if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2)) + goto oom; + + what = inhibit_what_to_string(inhibitor->what); + who = strempty(inhibitor->who); + why = strempty(inhibitor->why); + uid = (dbus_uint32_t) inhibitor->uid; + pid = (dbus_uint32_t) inhibitor->pid; + + if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &what) || + !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &who) || + !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &why) || + !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) || + !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &pid)) + goto oom; + + if (!dbus_message_iter_close_container(&sub, &sub2)) + goto oom; + } + + if (!dbus_message_iter_close_container(&iter, &sub)) + goto oom; + + } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "Inhibit")) { + + r = bus_manager_inhibit(m, connection, message, &error, &reply); + + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CreateSession")) { r = bus_manager_create_session(m, message, &reply); @@ -1077,7 +1268,7 @@ static DBusHandlerResult manager_message_handler( * see this fail quickly then be retried later */ if (r < 0) - return bus_send_error_reply(connection, message, &error, r); + return bus_send_error_reply(connection, message, NULL, r); } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ReleaseSession")) { const char *name; @@ -1441,11 +1632,10 @@ static DBusHandlerResult manager_message_handler( } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "PowerOff") || dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "Reboot")) { dbus_bool_t interactive; - bool multiple_sessions; + bool multiple_sessions, inhibit; DBusMessage *forward, *freply; - const char *name; + const char *name, *action; const char *mode = "replace"; - const char *action; if (!dbus_message_get_args( message, @@ -1459,26 +1649,37 @@ static DBusHandlerResult manager_message_handler( return bus_send_error_reply(connection, message, &error, r); multiple_sessions = r > 0; + inhibit = !!(manager_inhibit_what(m) & INHIBIT_SHUTDOWN); - if (streq(dbus_message_get_member(message), "PowerOff")) { - if (multiple_sessions) - action = "org.freedesktop.login1.power-off-multiple-sessions"; - else - action = "org.freedesktop.login1.power-off"; + if (multiple_sessions) { + action = streq(dbus_message_get_member(message), "PowerOff") ? + "org.freedesktop.login1.power-off-multiple-sessions" : + "org.freedesktop.login1.reboot-multiple-sessions"; - name = SPECIAL_POWEROFF_TARGET; - } else { - if (multiple_sessions) - action = "org.freedesktop.login1.reboot-multiple-sessions"; - else - action = "org.freedesktop.login1.reboot"; + r = verify_polkit(connection, message, action, interactive, NULL, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + } + + if (inhibit) { + action = streq(dbus_message_get_member(message), "PowerOff") ? + "org.freedesktop.login1.power-off-ignore-inhibit" : + "org.freedesktop.login1.reboot-ignore-inhibit"; - name = SPECIAL_REBOOT_TARGET; + r = verify_polkit(connection, message, action, interactive, NULL, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); } - r = verify_polkit(connection, message, action, interactive, NULL, &error); - if (r < 0) - return bus_send_error_reply(connection, message, &error, r); + if (!multiple_sessions && !inhibit) { + action = streq(dbus_message_get_member(message), "PowerOff") ? + "org.freedesktop.login1.power-off" : + "org.freedesktop.login1.reboot"; + + r = verify_polkit(connection, message, action, interactive, NULL, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + } forward = dbus_message_new_method_call( "org.freedesktop.systemd1", @@ -1488,6 +1689,9 @@ static DBusHandlerResult manager_message_handler( if (!forward) return bus_send_error_reply(connection, message, NULL, -ENOMEM); + name = streq(dbus_message_get_member(message), "PowerOff") ? + SPECIAL_POWEROFF_TARGET : SPECIAL_REBOOT_TARGET; + if (!dbus_message_append_args(forward, DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &mode, @@ -1511,43 +1715,77 @@ static DBusHandlerResult manager_message_handler( } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanPowerOff") || dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanReboot")) { - bool multiple_sessions, challenge, b; - const char *t, *action; + bool multiple_sessions, challenge, inhibit, b; + const char *action, *result; r = have_multiple_sessions(connection, m, message, &error); if (r < 0) return bus_send_error_reply(connection, message, &error, r); multiple_sessions = r > 0; + inhibit = !!(manager_inhibit_what(m) & INHIBIT_SHUTDOWN); + + if (multiple_sessions) { + action = streq(dbus_message_get_member(message), "CanPowerOff") ? + "org.freedesktop.login1.power-off-multiple-sessions" : + "org.freedesktop.login1.reboot-multiple-sessions"; - if (streq(dbus_message_get_member(message), "CanPowerOff")) { - if (multiple_sessions) - action = "org.freedesktop.login1.power-off-multiple-sessions"; + r = verify_polkit(connection, message, action, false, &challenge, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + if (r > 0) + result = "yes"; + else if (challenge) + result = "challenge"; else - action = "org.freedesktop.login1.power-off"; + result = "no"; + } - } else { - if (multiple_sessions) - action = "org.freedesktop.login1.reboot-multiple-sessions"; + if (inhibit) { + action = streq(dbus_message_get_member(message), "CanPowerOff") ? + "org.freedesktop.login1.power-off-ignore-inhibit" : + "org.freedesktop.login1.reboot-ignore-inhibit"; + + r = verify_polkit(connection, message, action, false, &challenge, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + if (r > 0 && !result) + result = "yes"; + else if (challenge && (!result || streq(result, "yes"))) + result = "challenge"; else - action = "org.freedesktop.login1.reboot"; + result = "no"; } - r = verify_polkit(connection, message, action, false, &challenge, &error); - if (r < 0) - return bus_send_error_reply(connection, message, &error, r); + if (!multiple_sessions && !inhibit) { + /* If neither inhibit nor multiple sessions + * apply then just check the normal policy */ + + action = streq(dbus_message_get_member(message), "CanPowerOff") ? + "org.freedesktop.login1.power-off" : + "org.freedesktop.login1.reboot"; + + r = verify_polkit(connection, message, action, false, &challenge, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + if (r > 0) + result = "yes"; + else if (challenge) + result = "challenge"; + else + result = "no"; + } reply = dbus_message_new_method_return(message); if (!reply) goto oom; - t = r > 0 ? "yes" : - challenge ? "challenge" : - "no"; - b = dbus_message_append_args( reply, - DBUS_TYPE_STRING, &t, + DBUS_TYPE_STRING, &result, DBUS_TYPE_INVALID); if (!b) goto oom; diff --git a/src/login/logind-inhibit.c b/src/login/logind-inhibit.c new file mode 100644 index 0000000000..2f7a758e7c --- /dev/null +++ b/src/login/logind-inhibit.c @@ -0,0 +1,365 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "mkdir.h" + +#include "logind-inhibit.h" + +Inhibitor* inhibitor_new(Manager *m, const char* id) { + Inhibitor *i; + + assert(m); + + i = new0(Inhibitor, 1); + if (!i) + return NULL; + + i->state_file = strappend("/run/systemd/inhibit/", id); + if (!i->state_file) { + free(i); + return NULL; + } + + i->id = file_name_from_path(i->state_file); + + if (hashmap_put(m->inhibitors, i->id, i) < 0) { + free(i->state_file); + free(i); + return NULL; + } + + i->manager = m; + i->fifo_fd = -1; + + return i; +} + +void inhibitor_free(Inhibitor *i) { + assert(i); + + free(i->who); + free(i->why); + + hashmap_remove(i->manager->inhibitors, i->id); + inhibitor_remove_fifo(i); + + if (i->state_file) { + unlink(i->state_file); + free(i->state_file); + } + + free(i); +} + +int inhibitor_save(Inhibitor *i) { + char *temp_path, *cc; + int r; + FILE *f; + + assert(i); + + r = safe_mkdir("/run/systemd/inhibit", 0755, 0, 0); + if (r < 0) + goto finish; + + r = fopen_temporary(i->state_file, &f, &temp_path); + if (r < 0) + goto finish; + + fchmod(fileno(f), 0644); + + fprintf(f, + "# This is private data. Do not parse.\n" + "WHAT=%s\n" + "UID=%lu\n" + "PID=%lu\n", + inhibit_what_to_string(i->what), + (unsigned long) i->uid, + (unsigned long) i->pid); + + if (i->who) { + cc = cescape(i->who); + if (!cc) + r = -ENOMEM; + else { + fprintf(f, "WHO=%s\n", cc); + free(cc); + } + } + + if (i->why) { + cc = cescape(i->why); + if (!cc) + r = -ENOMEM; + else { + fprintf(f, "WHY=%s\n", cc); + free(cc); + } + } + + if (i->fifo_path) + fprintf(f, "FIFO=%s\n", i->fifo_path); + + fflush(f); + + if (ferror(f) || rename(temp_path, i->state_file) < 0) { + r = -errno; + unlink(i->state_file); + unlink(temp_path); + } + + fclose(f); + free(temp_path); + +finish: + if (r < 0) + log_error("Failed to save inhibit data for %s: %s", i->id, strerror(-r)); + + return r; +} + +int inhibitor_start(Inhibitor *i) { + assert(i); + + if (i->started) + return 0; + + log_debug("Inhibitor %s (%s) pid=%lu uid=%lu started.", + strna(i->who), strna(i->why), + (unsigned long) i->pid, (unsigned long) i->uid); + + inhibitor_save(i); + + i->started = true; + + manager_send_changed(i->manager, "Inhibited\0"); + + return 0; +} + +int inhibitor_stop(Inhibitor *i) { + assert(i); + + if (i->started) + log_debug("Inhibitor %s (%s) pid=%lu uid=%lu stopped.", + strna(i->who), strna(i->why), + (unsigned long) i->pid, (unsigned long) i->uid); + + if (i->state_file) + unlink(i->state_file); + + i->started = false; + + manager_send_changed(i->manager, "Inhibited\0"); + + return 0; +} + +int inhibitor_load(Inhibitor *i) { + InhibitWhat w; + int r; + char *cc, + *what = NULL, + *uid = NULL, + *pid = NULL, + *who = NULL, + *why = NULL; + + r = parse_env_file(i->state_file, NEWLINE, + "WHAT", &what, + "UID", &uid, + "PID", &pid, + "WHO", &who, + "WHY", &why, + "FIFO", &i->fifo_path, + NULL); + if (r < 0) + goto finish; + + w = inhibit_what_from_string(what); + if (w >= 0) + i->what = w; + + parse_uid(uid, &i->uid); + parse_pid(pid, &i->pid); + + if (who) { + cc = cunescape(who); + if (!cc) { + r = -ENOMEM; + goto finish; + } + + free(i->who); + i->who = cc; + } + + if (why) { + cc = cunescape(why); + if (!cc) { + r = -ENOMEM; + goto finish; + } + + free(i->why); + i->why = cc; + } + + if (i->fifo_path) { + int fd; + + fd = inhibitor_create_fifo(i); + if (fd >= 0) + close_nointr_nofail(fd); + } + +finish: + free(what); + free(uid); + free(pid); + free(who); + free(why); + + return r; +} + +int inhibitor_create_fifo(Inhibitor *i) { + int r; + + assert(i); + + /* Create FIFO */ + if (!i->fifo_path) { + r = safe_mkdir("/run/systemd/inhibit", 0755, 0, 0); + if (r < 0) + return r; + + if (asprintf(&i->fifo_path, "/run/systemd/inhibit/%s.ref", i->id) < 0) + return -ENOMEM; + + if (mkfifo(i->fifo_path, 0600) < 0 && errno != EEXIST) + return -errno; + } + + /* Open reading side */ + if (i->fifo_fd < 0) { + struct epoll_event ev; + + i->fifo_fd = open(i->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY); + if (i->fifo_fd < 0) + return -errno; + + r = hashmap_put(i->manager->inhibitor_fds, INT_TO_PTR(i->fifo_fd + 1), i); + if (r < 0) + return r; + + zero(ev); + ev.events = 0; + ev.data.u32 = FD_FIFO_BASE + i->fifo_fd; + + if (epoll_ctl(i->manager->epoll_fd, EPOLL_CTL_ADD, i->fifo_fd, &ev) < 0) + return -errno; + } + + /* Open writing side */ + r = open(i->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY); + if (r < 0) + return -errno; + + return r; +} + +void inhibitor_remove_fifo(Inhibitor *i) { + assert(i); + + if (i->fifo_fd >= 0) { + assert_se(hashmap_remove(i->manager->inhibitor_fds, INT_TO_PTR(i->fifo_fd + 1)) == i); + assert_se(epoll_ctl(i->manager->epoll_fd, EPOLL_CTL_DEL, i->fifo_fd, NULL) == 0); + close_nointr_nofail(i->fifo_fd); + i->fifo_fd = -1; + } + + if (i->fifo_path) { + unlink(i->fifo_path); + free(i->fifo_path); + i->fifo_path = NULL; + } +} + +InhibitWhat manager_inhibit_what(Manager *m) { + Inhibitor *i; + Iterator j; + InhibitWhat what = 0; + + assert(m); + + HASHMAP_FOREACH(i, m->inhibitor_fds, j) + what |= i->what; + + return what; +} + +const char *inhibit_what_to_string(InhibitWhat w) { + + static const char* const table[_INHIBIT_WHAT_MAX] = { + [0] = "", + [INHIBIT_SHUTDOWN] = "shutdown", + [INHIBIT_SUSPEND] = "suspend", + [INHIBIT_IDLE] = "idle", + [INHIBIT_SHUTDOWN|INHIBIT_SUSPEND] = "shutdown:suspend", + [INHIBIT_SHUTDOWN|INHIBIT_IDLE] = "shutdown:idle", + [INHIBIT_SHUTDOWN|INHIBIT_SUSPEND|INHIBIT_IDLE] = "shutdown:suspend:idle", + [INHIBIT_SUSPEND|INHIBIT_IDLE] = "suspend:idle" + }; + + if (w < 0 || w >= _INHIBIT_WHAT_MAX) + return NULL; + + return table[w]; +} + +InhibitWhat inhibit_what_from_string(const char *s) { + InhibitWhat what = 0; + char *w, *state; + size_t l; + + FOREACH_WORD_SEPARATOR(w, l, s, ":", state) { + if (l == 8 && strncmp(w, "shutdown", l) == 0) + what |= INHIBIT_SHUTDOWN; + else if (l == 7 && strncmp(w, "suspend", l) == 0) + what |= INHIBIT_SUSPEND; + else if (l == 4 && strncmp(w, "idle", l) == 0) + what |= INHIBIT_IDLE; + else + return _INHIBIT_WHAT_INVALID; + } + + return what; + +} diff --git a/src/login/logind-inhibit.h b/src/login/logind-inhibit.h new file mode 100644 index 0000000000..0670a7f8cd --- /dev/null +++ b/src/login/logind-inhibit.h @@ -0,0 +1,76 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foologindinhibithfoo +#define foologindinhibithfoo + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +typedef struct Inhibitor Inhibitor; + +#include "list.h" +#include "util.h" +#include "logind.h" +#include "logind-seat.h" + +typedef enum InhibitWhat { + INHIBIT_SHUTDOWN = 1, + INHIBIT_SUSPEND = 2, + INHIBIT_IDLE = 4, + _INHIBIT_WHAT_MAX = 8, + _INHIBIT_WHAT_INVALID = -1 +} InhibitWhat; + +struct Inhibitor { + Manager *manager; + + char *id; + char *state_file; + + bool started; + + InhibitWhat what; + char *who; + char *why; + + pid_t pid; + uid_t uid; + + char *fifo_path; + int fifo_fd; +}; + +Inhibitor* inhibitor_new(Manager *m, const char *id); +void inhibitor_free(Inhibitor *i); + +int inhibitor_save(Inhibitor *i); +int inhibitor_load(Inhibitor *i); + +int inhibitor_start(Inhibitor *i); +int inhibitor_stop(Inhibitor *i); + +int inhibitor_create_fifo(Inhibitor *i); +void inhibitor_remove_fifo(Inhibitor *i); + +InhibitWhat manager_inhibit_what(Manager *m); + +const char *inhibit_what_to_string(InhibitWhat k); +InhibitWhat inhibit_what_from_string(const char *s); + +#endif diff --git a/src/login/logind-session.c b/src/login/logind-session.c index cbc4b5cf3c..5490366cc1 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -25,11 +25,11 @@ #include #include -#include "logind-session.h" #include "strv.h" #include "util.h" #include "mkdir.h" #include "cgroup-util.h" +#include "logind-session.h" #define IDLE_THRESHOLD_USEC (5*USEC_PER_MINUTE) @@ -52,7 +52,7 @@ Session* session_new(Manager *m, User *u, const char *id) { s->id = file_name_from_path(s->state_file); if (hashmap_put(m->sessions, s->id, s) < 0) { - free(s->id); + free(s->state_file); free(s); return NULL; } @@ -99,7 +99,6 @@ void session_free(Session *s) { free(s->service); hashmap_remove(s->manager->sessions, s->id); - session_remove_fifo(s); free(s->state_file); @@ -287,14 +286,9 @@ int session_load(Session *s) { } if (leader) { - pid_t pid; - - k = parse_pid(leader, &pid); - if (k >= 0 && pid >= 1) { - s->leader = pid; - - audit_session_from_pid(pid, &s->audit_id); - } + k = parse_pid(leader, &s->leader); + if (k >= 0) + audit_session_from_pid(s->leader, &s->audit_id); } if (type) { @@ -326,7 +320,6 @@ int session_load(Session *s) { close_nointr_nofail(fd); } - finish: free(remote); free(kill_processes); @@ -840,7 +833,7 @@ int session_create_fifo(Session *s) { if (s->fifo_fd < 0) return -errno; - r = hashmap_put(s->manager->fifo_fds, INT_TO_PTR(s->fifo_fd + 1), s); + r = hashmap_put(s->manager->session_fds, INT_TO_PTR(s->fifo_fd + 1), s); if (r < 0) return r; @@ -864,7 +857,7 @@ void session_remove_fifo(Session *s) { assert(s); if (s->fifo_fd >= 0) { - assert_se(hashmap_remove(s->manager->fifo_fds, INT_TO_PTR(s->fifo_fd + 1)) == s); + assert_se(hashmap_remove(s->manager->session_fds, INT_TO_PTR(s->fifo_fd + 1)) == s); assert_se(epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_DEL, s->fifo_fd, NULL) == 0); close_nointr_nofail(s->fifo_fd); s->fifo_fd = -1; diff --git a/src/login/logind.c b/src/login/logind.c index fc08c4bc69..7222e3aaa7 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -55,10 +55,14 @@ Manager *manager_new(void) { m->seats = hashmap_new(string_hash_func, string_compare_func); m->sessions = hashmap_new(string_hash_func, string_compare_func); m->users = hashmap_new(trivial_hash_func, trivial_compare_func); + m->inhibitors = hashmap_new(string_hash_func, string_compare_func); + m->cgroups = hashmap_new(string_hash_func, string_compare_func); - m->fifo_fds = hashmap_new(trivial_hash_func, trivial_compare_func); + m->session_fds = hashmap_new(trivial_hash_func, trivial_compare_func); + m->inhibitor_fds = hashmap_new(trivial_hash_func, trivial_compare_func); - if (!m->devices || !m->seats || !m->sessions || !m->users || !m->cgroups || !m->fifo_fds) { + if (!m->devices || !m->seats || !m->sessions || !m->users || !m->inhibitors || + !m->cgroups || !m->session_fds || !m->inhibitor_fds) { manager_free(m); return NULL; } @@ -89,6 +93,7 @@ void manager_free(Manager *m) { User *u; Device *d; Seat *s; + Inhibitor *i; assert(m); @@ -104,12 +109,18 @@ void manager_free(Manager *m) { while ((s = hashmap_first(m->seats))) seat_free(s); - hashmap_free(m->sessions); - hashmap_free(m->users); + while ((i = hashmap_first(m->inhibitors))) + inhibitor_free(i); + hashmap_free(m->devices); hashmap_free(m->seats); + hashmap_free(m->sessions); + hashmap_free(m->users); + hashmap_free(m->inhibitors); + hashmap_free(m->cgroups); - hashmap_free(m->fifo_fds); + hashmap_free(m->session_fds); + hashmap_free(m->inhibitor_fds); if (m->console_active_fd >= 0) close_nointr_nofail(m->console_active_fd); @@ -268,6 +279,30 @@ int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) { return manager_add_user(m, uid, p->pw_gid, p->pw_name, _user); } +int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor) { + Inhibitor *i; + + assert(m); + assert(id); + + i = hashmap_get(m->inhibitors, id); + if (i) { + if (_inhibitor) + *_inhibitor = i; + + return 0; + } + + i = inhibitor_new(m, id); + if (!i) + return -ENOMEM; + + if (_inhibitor) + *_inhibitor = i; + + return 0; +} + int manager_process_seat_device(Manager *m, struct udev_device *d) { Device *device; int r; @@ -412,10 +447,9 @@ int manager_enumerate_seats(Manager *m) { } static int manager_enumerate_users_from_cgroup(Manager *m) { - int r = 0; + int r = 0, k; char *name; DIR *d; - int k; r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_path, &d); if (r < 0) { @@ -641,6 +675,46 @@ int manager_enumerate_sessions(Manager *m) { return r; } +int manager_enumerate_inhibitors(Manager *m) { + DIR *d; + struct dirent *de; + int r = 0; + + assert(m); + + d = opendir("/run/systemd/inhibit"); + if (!d) { + if (errno == ENOENT) + return 0; + + log_error("Failed to open /run/systemd/inhibit: %m"); + return -errno; + } + + while ((de = readdir(d))) { + int k; + Inhibitor *i; + + if (!dirent_is_file(de)) + continue; + + k = manager_add_inhibitor(m, de->d_name, &i); + if (k < 0) { + log_notice("Couldn't add inhibitor %s: %s", de->d_name, strerror(-k)); + r = k; + continue; + } + + k = inhibitor_load(i); + if (k < 0) + r = k; + } + + closedir(d); + + return r; +} + int manager_dispatch_seat_udev(Manager *m) { struct udev_device *d; int r; @@ -853,15 +927,28 @@ void manager_cgroup_notify_empty(Manager *m, const char *cgroup) { static void manager_pipe_notify_eof(Manager *m, int fd) { Session *s; + Inhibitor *i; assert_se(m); assert_se(fd >= 0); - assert_se(s = hashmap_get(m->fifo_fds, INT_TO_PTR(fd + 1))); - assert(s->fifo_fd == fd); - session_remove_fifo(s); + s = hashmap_get(m->session_fds, INT_TO_PTR(fd + 1)); + if (s) { + assert(s->fifo_fd == fd); + session_remove_fifo(s); + session_stop(s); + return; + } - session_stop(s); + i = hashmap_get(m->inhibitor_fds, INT_TO_PTR(fd + 1)); + if (i) { + assert(i->fifo_fd == fd); + inhibitor_stop(i); + inhibitor_free(i); + return; + } + + assert_not_reached("Got EOF on unknown pipe"); } static int manager_connect_bus(Manager *m) { @@ -1110,6 +1197,7 @@ int manager_startup(Manager *m) { Seat *seat; Session *session; User *user; + Inhibitor *inhibitor; Iterator i; assert(m); @@ -1144,6 +1232,7 @@ int manager_startup(Manager *m) { manager_enumerate_seats(m); manager_enumerate_users(m); manager_enumerate_sessions(m); + manager_enumerate_inhibitors(m); /* Remove stale objects before we start them */ manager_gc(m, false); @@ -1158,6 +1247,9 @@ int manager_startup(Manager *m) { HASHMAP_FOREACH(session, m->sessions, i) session_start(session); + HASHMAP_FOREACH(inhibitor, m->inhibitors, i) + inhibitor_start(inhibitor); + return 0; } diff --git a/src/login/logind.h b/src/login/logind.h index a3080371d0..4e9dcd5fed 100644 --- a/src/login/logind.h +++ b/src/login/logind.h @@ -39,6 +39,7 @@ typedef struct Manager Manager; #include "logind-seat.h" #include "logind-session.h" #include "logind-user.h" +#include "logind-inhibit.h" struct Manager { DBusConnection *bus; @@ -47,6 +48,7 @@ struct Manager { Hashmap *seats; Hashmap *sessions; Hashmap *users; + Hashmap *inhibitors; LIST_HEAD(Seat, seat_gc_queue); LIST_HEAD(Session, session_gc_queue); @@ -74,9 +76,11 @@ struct Manager { bool kill_user_processes; unsigned long session_counter; + unsigned long inhibit_counter; Hashmap *cgroups; - Hashmap *fifo_fds; + Hashmap *session_fds; + Hashmap *inhibitor_fds; }; enum { @@ -96,6 +100,7 @@ int manager_add_session(Manager *m, User *u, const char *id, Session **_session) int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user); int manager_add_user_by_name(Manager *m, const char *name, User **_user); int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user); +int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor); int manager_process_seat_device(Manager *m, struct udev_device *d); int manager_dispatch_seat_udev(Manager *m); @@ -106,6 +111,7 @@ int manager_enumerate_devices(Manager *m); int manager_enumerate_seats(Manager *m); int manager_enumerate_sessions(Manager *m); int manager_enumerate_users(Manager *m); +int manager_enumerate_inhibitors(Manager *m); int manager_startup(Manager *m); int manager_run(Manager *m); diff --git a/src/login/org.freedesktop.login1.conf b/src/login/org.freedesktop.login1.conf index 7ebf9ea99c..0f70b37c04 100644 --- a/src/login/org.freedesktop.login1.conf +++ b/src/login/org.freedesktop.login1.conf @@ -64,6 +64,14 @@ send_interface="org.freedesktop.login1.Manager" send_member="ListSeats"/> + + + + diff --git a/src/login/org.freedesktop.login1.policy.in b/src/login/org.freedesktop.login1.policy.in index fb5c539d50..a2dc4025ce 100644 --- a/src/login/org.freedesktop.login1.policy.in +++ b/src/login/org.freedesktop.login1.policy.in @@ -16,6 +16,16 @@ The systemd Project http://www.freedesktop.org/wiki/Software/systemd + + <_description>Allow applications to inhibit system shutdown and suspend + <_message>Authentication is required to allow an application to inhibit system shutdown or suspend + + auth_admin_keep + yes + yes + + + <_description>Allow non-logged-in users to run programs <_message>Authentication is required to allow a non-logged-in user to run programs @@ -66,6 +76,16 @@ + + <_description>Power off the system when an application asked to inhibit it + <_message>Authentication is required to allow powering off the system while an application asked to inhibit it + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + <_description>Reboot the system <_message>Authentication is required to allow rebooting the system @@ -86,4 +106,14 @@ + + <_description>Reboot the system when an application asked to inhibit it + <_message>Authentication is required to allow rebooting the system while an application asked to inhibit it + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + diff --git a/src/login/test-inhibit.c b/src/login/test-inhibit.c new file mode 100644 index 0000000000..c83e960d3e --- /dev/null +++ b/src/login/test-inhibit.c @@ -0,0 +1,137 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +#include "macro.h" +#include "util.h" +#include "dbus-common.h" + +static int inhibit(DBusConnection *bus, const char *what) { + DBusMessage *m, *reply; + DBusError error; + const char *who = "Test Tool", *reason = "Just because!"; + int fd; + + dbus_error_init(&error); + + m = dbus_message_new_method_call( + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "Inhibit"); + assert(m); + + assert_se(dbus_message_append_args(m, + DBUS_TYPE_STRING, &what, + DBUS_TYPE_STRING, &who, + DBUS_TYPE_STRING, &reason, + DBUS_TYPE_INVALID)); + + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); + assert(reply); + + assert(dbus_message_get_args(reply, &error, + DBUS_TYPE_UNIX_FD, &fd, + DBUS_TYPE_INVALID)); + + dbus_message_unref(m); + dbus_message_unref(reply); + + return fd; +} + +static void print_inhibitors(DBusConnection *bus) { + DBusMessage *m, *reply; + DBusError error; + unsigned n = 0; + DBusMessageIter iter, sub, sub2; + + dbus_error_init(&error); + + m = dbus_message_new_method_call( + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "ListInhibitors"); + assert(m); + + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); + assert(reply); + + assert(dbus_message_iter_init(reply, &iter)); + dbus_message_iter_recurse(&iter, &sub); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + const char *what, *who, *reason; + dbus_uint32_t uid, pid; + + dbus_message_iter_recurse(&sub, &sub2); + + assert_se(bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &what, true) >= 0); + assert_se(bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &who, true) >= 0); + assert_se(bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &reason, true) >= 0); + assert_se(bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) >= 0); + assert_se(bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &pid, false) >= 0); + + printf("what=<%s> who=<%s> reason=<%s> uid=<%lu> pid=<%lu>\n", + what, who, reason, (unsigned long) uid, (unsigned long) pid); + + dbus_message_iter_next(&sub); + + n++; + } + + printf("%u inhibitors\n", n); + + dbus_message_unref(m); + dbus_message_unref(reply); +} + +int main(int argc, char*argv[]) { + DBusConnection *bus; + int fd1, fd2; + + bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, NULL); + assert(bus); + + print_inhibitors(bus); + + fd1 = inhibit(bus, "suspend"); + assert(fd1 >= 0); + print_inhibitors(bus); + + fd2 = inhibit(bus, "idle:shutdown"); + assert(fd2 >= 0); + print_inhibitors(bus); + + close_nointr_nofail(fd1); + sleep(1); + print_inhibitors(bus); + + close_nointr_nofail(fd2); + sleep(1); + print_inhibitors(bus); + + return 0; +} diff --git a/src/shared/dbus-common.c b/src/shared/dbus-common.c index 6d000e1162..e161273cd8 100644 --- a/src/shared/dbus-common.c +++ b/src/shared/dbus-common.c @@ -1155,3 +1155,53 @@ DBusHandlerResult bus_exit_idle_filter(DBusConnection *bus, DBusMessage *m, void return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } + +/* This mimics dbus_bus_get_unix_user() */ +pid_t bus_get_unix_process_id( + DBusConnection *connection, + const char *name, + DBusError *error) { + + DBusMessage *m = NULL, *reply = NULL; + uint32_t pid = 0; + + m = dbus_message_new_method_call( + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "GetConnectionUnixProcessID"); + if (!m) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL); + goto finish; + } + + if (!dbus_message_append_args( + m, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID)) { + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL); + goto finish; + } + + reply = dbus_connection_send_with_reply_and_block(connection, m, -1, error); + if (!reply) + goto finish; + + if (dbus_set_error_from_message(error, reply)) + goto finish; + + if (!dbus_message_get_args( + reply, error, + DBUS_TYPE_UINT32, &pid, + DBUS_TYPE_INVALID)) + goto finish; + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + return (pid_t) pid; +} diff --git a/src/shared/dbus-common.h b/src/shared/dbus-common.h index 2bfcdfa5d4..859812900d 100644 --- a/src/shared/dbus-common.h +++ b/src/shared/dbus-common.h @@ -199,4 +199,6 @@ void bus_async_unregister_and_exit(DBusConnection *bus, const char *name); DBusHandlerResult bus_exit_idle_filter(DBusConnection *bus, DBusMessage *m, void *userdata); +pid_t bus_get_unix_process_id(DBusConnection *connection, const char *name, DBusError *error); + #endif diff --git a/src/shared/polkit.c b/src/shared/polkit.c index 07d18e7d5f..14e27cdc60 100644 --- a/src/shared/polkit.c +++ b/src/shared/polkit.c @@ -27,56 +27,6 @@ #include "dbus-common.h" #include "polkit.h" -/* This mimics dbus_bus_get_unix_user() */ -static pid_t get_unix_process_id( - DBusConnection *connection, - const char *name, - DBusError *error) { - - DBusMessage *m = NULL, *reply = NULL; - uint32_t pid = 0; - - m = dbus_message_new_method_call( - DBUS_SERVICE_DBUS, - DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS, - "GetConnectionUnixProcessID"); - if (!m) { - dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL); - goto finish; - } - - if (!dbus_message_append_args( - m, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID)) { - dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL); - goto finish; - } - - reply = dbus_connection_send_with_reply_and_block(connection, m, -1, error); - if (!reply) - goto finish; - - if (dbus_set_error_from_message(error, reply)) - goto finish; - - if (!dbus_message_get_args( - reply, error, - DBUS_TYPE_UINT32, &pid, - DBUS_TYPE_INVALID)) - goto finish; - -finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - - return (pid_t) pid; -} - int verify_polkit( DBusConnection *c, DBusMessage *request, @@ -104,7 +54,7 @@ int verify_polkit( if (!sender) return -EINVAL; - pid_raw = get_unix_process_id(c, sender, error); + pid_raw = bus_get_unix_process_id(c, sender, error); if (pid_raw == 0) return -EINVAL; diff --git a/src/shared/util.c b/src/shared/util.c index 0b4d4d6746..15c7f4d325 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -1690,7 +1690,8 @@ char *cescape(const char *s) { /* Does C style string escaping. */ - if (!(r = new(char, strlen(s)*4 + 1))) + r = new(char, strlen(s)*4 + 1); + if (!r) return NULL; for (f = s, t = r; *f; f++) -- cgit v1.2.3-54-g00ecf From f13b388f97bc3ba8db844bd3413d510e2466a0b6 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Mon, 16 Apr 2012 23:32:22 +0200 Subject: udev: install udevd as /usr/lib/systemd/systemd-udevd --- .gitignore | 2 +- Makefile.am | 55 +++++++------ NEWS | 10 ++- man/systemd-udevd.xml | 150 ++++++++++++++++++++++++++++++++++ man/udev.xml | 6 +- man/udevadm.xml | 16 ++-- man/udevd.xml | 150 ---------------------------------- units/.gitignore | 6 +- units/systemd-udev-control.socket | 10 +++ units/systemd-udev-kernel.socket | 10 +++ units/systemd-udev-settle.service.in | 26 ++++++ units/systemd-udev-trigger.service.in | 11 +++ units/systemd-udev.service.in | 14 ++++ units/udev-control.socket | 10 --- units/udev-kernel.socket | 10 --- units/udev-settle.service.in | 26 ------ units/udev-trigger.service.in | 11 --- units/udev.service.in | 14 ---- 18 files changed, 276 insertions(+), 261 deletions(-) create mode 100644 man/systemd-udevd.xml delete mode 100644 man/udevd.xml create mode 100644 units/systemd-udev-control.socket create mode 100644 units/systemd-udev-kernel.socket create mode 100644 units/systemd-udev-settle.service.in create mode 100644 units/systemd-udev-trigger.service.in create mode 100644 units/systemd-udev.service.in delete mode 100644 units/udev-control.socket delete mode 100644 units/udev-kernel.socket delete mode 100644 units/udev-settle.service.in delete mode 100644 units/udev-trigger.service.in delete mode 100644 units/udev.service.in (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index af7a017b6d..08beb9afec 100644 --- a/.gitignore +++ b/.gitignore @@ -112,7 +112,7 @@ stamp-* /mtd_probe /scsi_id /udevadm -/udevd +/systemd-udevd /v4l_id /test-libudev /test-udev diff --git a/Makefile.am b/Makefile.am index 421d27c886..90c225ec01 100644 --- a/Makefile.am +++ b/Makefile.am @@ -101,6 +101,7 @@ check_PROGRAMS = check_DATA = noinst_PROGRAMS = TESTS = +udevlibexec_PROGRAMS = AM_CPPFLAGS = \ -include $(top_builddir)/config.h \ @@ -296,8 +297,8 @@ dist_systemunit_DATA = \ units/systemd-ask-password-wall.path \ units/systemd-ask-password-console.path \ units/syslog.target \ - units/udev-control.socket \ - units/udev-kernel.socket + units/systemd-udev-control.socket \ + units/systemd-udev-kernel.socket nodist_systemunit_DATA = \ units/getty@.service \ @@ -323,9 +324,9 @@ nodist_systemunit_DATA = \ units/fsck-root.service \ units/rescue.service \ units/user@.service \ - units/udev.service \ - units/udev-trigger.service \ - units/udev-settle.service + units/systemd-udev.service \ + units/systemd-udev-trigger.service \ + units/systemd-udev-settle.service dist_userunit_DATA = \ units/user/default.target \ @@ -359,9 +360,9 @@ EXTRA_DIST += \ units/fsck@.service.in \ units/fsck-root.service.in \ units/user@.service.in \ - units/udev.service \ - units/udev-trigger.service \ - units/udev-settle.service \ + units/systemd-udev.service \ + units/systemd-udev-trigger.service \ + units/systemd-udev-settle.service \ introspect.awk \ man/custom-html.xsl @@ -1358,7 +1359,7 @@ libudev_private_la_LIBADD = \ MANPAGES += \ man/udev.7 \ man/udevadm.8 \ - man/udevd.8 + man/systemd-udevd.8 udev-confdirs: -mkdir -p $(DESTDIR)$(sysconfdir)/udev/rules.d @@ -1398,30 +1399,30 @@ CLEANFILES += \ src/udev/udev.pc EXTRA_DIST += \ - units/udev.service.in \ - units/udev-trigger.service.in \ - units/udev-settle.service.in + units/systemd-udev.service.in \ + units/systemd-udev-trigger.service.in \ + units/systemd-udev-settle.service.in CLEANFILES += \ - units/udev.service \ - units/udev-trigger.service \ - units/udev-settle.service + units/systemd-udev.service \ + units/systemd-udev-trigger.service \ + units/systemd-udev-settle.service systemd-install-hook: mkdir -p $(DESTDIR)$(systemunitdir)/sockets.target.wants - ln -sf ../udev-control.socket $(DESTDIR)$(systemunitdir)/sockets.target.wants/udev-control.socket - ln -sf ../udev-kernel.socket $(DESTDIR)$(systemunitdir)/sockets.target.wants/udev-kernel.socket + ln -sf ../systemd-udev-control.socket $(DESTDIR)$(systemunitdir)/sockets.target.wants/systemd-udev-control.socket + ln -sf ../systemd-udev-kernel.socket $(DESTDIR)$(systemunitdir)/sockets.target.wants/systemd-udev-kernel.socket mkdir -p $(DESTDIR)$(systemunitdir)/basic.target.wants - ln -sf ../udev.service $(DESTDIR)$(systemunitdir)/basic.target.wants/udev.service - ln -sf ../udev-trigger.service $(DESTDIR)$(systemunitdir)/basic.target.wants/udev-trigger.service + ln -sf ../systemd-udev.service $(DESTDIR)$(systemunitdir)/basic.target.wants/systemd-udev.service + ln -sf ../systemd-udev-trigger.service $(DESTDIR)$(systemunitdir)/basic.target.wants/systemd-udev-trigger.service INSTALL_DATA_HOOKS += systemd-install-hook bin_PROGRAMS += \ udevadm -udevlibexec_PROGRAMS = \ - udevd +rootlibexec_PROGRAMS += \ + systemd-udevd noinst_LTLIBRARIES += \ libudev-core.la @@ -1469,10 +1470,10 @@ libudev_core_la_LIBADD += \ libsystemd-acl.la endif -udevd_SOURCES = \ +systemd_udevd_SOURCES = \ src/udev/udevd.c -udevd_LDADD = \ +systemd_udevd_LDADD = \ libudev-core.la udevadm_SOURCES = \ @@ -3227,6 +3228,9 @@ install-data-hook: systemd-install-data-hook $(INSTALL_DATA_HOOKS) distclean-local: $(DISTCLEAN_LOCAL_HOOKS) +clean-local: + rm -rf $(abs_srcdir)/install-tree + DISTCHECK_CONFIGURE_FLAGS = \ --with-dbuspolicydir=$$dc_install_base/$(dbuspolicydir) \ --with-dbussessionservicedir=$$dc_install_base/$(dbussessionservicedir) \ @@ -3249,3 +3253,8 @@ doc-sync: all git-tag: git tag "v$(VERSION)" -m "systemd $(VERSION)" + +install-tree: all + rm -rf $(abs_srcdir)/install-tree + make install DESTDIR=$(abs_srcdir)/install-tree + tree $(abs_srcdir)/install-tree diff --git a/NEWS b/NEWS index 52196dc0b1..dc04d0a345 100644 --- a/NEWS +++ b/NEWS @@ -8,8 +8,9 @@ CHANGES WITH 182: udev though, will require the *build* of the systemd tree, but udev can be properly *run* without systems. - * udev: /lib/udev/devices/ are not read anymore; tmpfiles should - be used to create workarounds for broken subsystems. + * udev: /lib/udev/devices/ are not read anymore; systemde-tmpfiles + should be used to create dead device nodes as workarounds for broken + subsystems. * udev: RUN+="socket:..." and udev_monitor_new_from_socket() is no longer supported. udev_monitor_new_from_netlink() needs to be @@ -22,6 +23,11 @@ CHANGES WITH 182: pulled-in by udev to ge started, but they can no longer be directly forked by udev rules. + * udev: the daemon binary is called systemd-udevd now and installed + in /usr/lib/systemd/. Standalone builds or non-systemd systems need + to adapt to that, create symlink, or rename the binary after building + it. + * systemd-logingctl and systemd-journalctl have been renamed to logingctl and journalctl to match systemctl. diff --git a/man/systemd-udevd.xml b/man/systemd-udevd.xml new file mode 100644 index 0000000000..1be356f8c6 --- /dev/null +++ b/man/systemd-udevd.xml @@ -0,0 +1,150 @@ + + + + + + + udevd + systemd + + + Developer + Kay + Sievers + kay@vrfy.org + + + + + + systemd-udevd + 8 + + + + + systemd-udevdevent managing daemon + + + + + systemd-udevd + + + + + + + + + + + Description + systemd-udevd listens to kernel uevents. For every event, systemd-udevd executes matching + instructions specified in udev rules. See + udev7 + . + The behavior of the running daemon can be changed with + udevadm control. + + + Options + + + + + Detach and run in the background. + + + + + + Print debug messages to stderr. + + + + + + Limit the number of parallel executed events. + + + + + + Number of seconds to delay the execution of RUN instructions. + This might be useful when debugging system crashes during coldplug + cause by loading non-working kernel modules. + + + + + + Specify when systemd-udevd should resolve names of users and groups. + When set to (the default) names will be + resolved when the rules are parsed. When set to + names will be resolved for every event. + When set to names will never be resolved + and all devices will be owned by root. + + + + + + Print version number. + + + + + + Print help text. + + + + + + Environment + + + UDEV_LOG= + + Set the logging priority. + + + + + + Kernel command line + + + udev.log-priority= + + Set the logging priority. + + + + udev.children-max= + + Limit the number of parallel executed events. + + + + udev.exec-delay= + + Number of seconds to delay the execution of RUN instructions. + This might be useful when debugging system crashes during coldplug + cause by loading non-working kernel modules. + + + + + + + See Also + + udev7 + , + udevadm8 + + + diff --git a/man/udev.xml b/man/udev.xml index 458d351658..bd0dec6a6a 100644 --- a/man/udev.xml +++ b/man/udev.xml @@ -41,7 +41,7 @@ names provide a way to reliably identify devices based on their properties or current configuration.
- The udev daemon, udevd + The udev daemon, systemd-udevd 8, receives device uevents directly from the kernel whenever a device is added or removed from the system, or it changes its state. When udev receives a device event, it matches its configured set of rules @@ -512,7 +512,7 @@ Apply the permissions specified in this rule to the static device node with the specified name. Static device node creation can be requested by kernel modules. - These nodes might not have a corresponding kernel device at the time udevd is + These nodes might not have a corresponding kernel device at the time systemd-udevd is started; they can trigger automatic kernel module loading. @@ -691,7 +691,7 @@ See Also - udevd8 + systemd-udevd8 , udevadm8 diff --git a/man/udevadm.xml b/man/udevadm.xml index 11900d202e..cbf22b4b5e 100644 --- a/man/udevadm.xml +++ b/man/udevadm.xml @@ -322,13 +322,13 @@ - Signal and wait for udevd to exit. + Signal and wait for systemd-udevd to exit. - Set the internal log level of udevd. Valid values are the numerical + Set the internal log level of systemd-udevd. Valid values are the numerical syslog priorities or their textual representations: , and . @@ -336,20 +336,20 @@ - Signal udevd to stop executing new events. Incoming events + Signal systemd-udevd to stop executing new events. Incoming events will be queued. - Signal udevd to enable the execution of events. + Signal systemd-udevd to enable the execution of events. - Signal udevd to reload the rules files and other databases like the kernel + Signal systemd-udevd to reload the rules files and other databases like the kernel module index. Reloading rules and databases does not apply any changes to already existing devices; the new configuration will only be applied to new events. @@ -363,14 +363,14 @@ value - Set the maximum number of events, udevd will handle at the + Set the maximum number of events, systemd-udevd will handle at the same time. seconds - The maximum number seconds to wait for a reply from udevd. + The maximum number seconds to wait for a reply from systemd-udevd. @@ -470,7 +470,7 @@ udev7 - udevd8 + systemd-udevd8 diff --git a/man/udevd.xml b/man/udevd.xml deleted file mode 100644 index 7c4e174cbe..0000000000 --- a/man/udevd.xml +++ /dev/null @@ -1,150 +0,0 @@ - - - - - - - udevd - systemd - - - Developer - Kay - Sievers - kay@vrfy.org - - - - - - udevd - 8 - - - - - udevdevent managing daemon - - - - - udevd - - - - - - - - - - - Description - udevd listens to kernel uevents. For every event, udevd executes matching - instructions specified in udev rules. See - udev7 - . - The behavior of the running daemon can be changed with - udevadm control. - - - Options - - - - - Detach and run in the background. - - - - - - Print debug messages to stderr. - - - - - - Limit the number of parallel executed events. - - - - - - Number of seconds to delay the execution of RUN instructions. - This might be useful when debugging system crashes during coldplug - cause by loading non-working kernel modules. - - - - - - Specify when udevd should resolve names of users and groups. - When set to (the default) names will be - resolved when the rules are parsed. When set to - names will be resolved for every event. - When set to names will never be resolved - and all devices will be owned by root. - - - - - - Print version number. - - - - - - Print help text. - - - - - - Environment - - - UDEV_LOG= - - Set the logging priority. - - - - - - Kernel command line - - - udev.log-priority= - - Set the logging priority. - - - - udev.children-max= - - Limit the number of parallel executed events. - - - - udev.exec-delay= - - Number of seconds to delay the execution of RUN instructions. - This might be useful when debugging system crashes during coldplug - cause by loading non-working kernel modules. - - - - - - - See Also - - udev7 - , - udevadm8 - - - diff --git a/units/.gitignore b/units/.gitignore index 3cf64237a8..eda676a480 100644 --- a/units/.gitignore +++ b/units/.gitignore @@ -41,6 +41,6 @@ systemd-update-utmp-shutdown.service test-env-replace systemd-binfmt.service emergency.service -/udev-settle.service -/udev-trigger.service -/udev.service +/systemd-udev-settle.service +/systemd-udev-trigger.service +/systemd-udev.service diff --git a/units/systemd-udev-control.socket b/units/systemd-udev-control.socket new file mode 100644 index 0000000000..f80f774427 --- /dev/null +++ b/units/systemd-udev-control.socket @@ -0,0 +1,10 @@ +[Unit] +Description=udev Control Socket +DefaultDependencies=no +ConditionCapability=CAP_MKNOD + +[Socket] +Service=udev.service +ListenSequentialPacket=/run/udev/control +SocketMode=0600 +PassCredentials=yes diff --git a/units/systemd-udev-kernel.socket b/units/systemd-udev-kernel.socket new file mode 100644 index 0000000000..23fa9d5e11 --- /dev/null +++ b/units/systemd-udev-kernel.socket @@ -0,0 +1,10 @@ +[Unit] +Description=udev Kernel Socket +DefaultDependencies=no +ConditionCapability=CAP_MKNOD + +[Socket] +Service=udev.service +ReceiveBuffer=134217728 +ListenNetlink=kobject-uevent 1 +PassCredentials=yes diff --git a/units/systemd-udev-settle.service.in b/units/systemd-udev-settle.service.in new file mode 100644 index 0000000000..0a5e8c51ff --- /dev/null +++ b/units/systemd-udev-settle.service.in @@ -0,0 +1,26 @@ +# This service is usually not enabled by default. If enabled, it +# acts as a barrier for basic.target -- so all later services will +# wait for udev completely finishing its coldplug run. +# +# If needed, to work around broken or non-hotplug-aware services, +# it might be enabled unconditionally, or pulled-in on-demand by +# the services that assume a fully populated /dev at startup. It +# should not be used or pulled-in ever on systems without such +# legacy services running. + +[Unit] +Description=udev Wait for Complete Device Initialization +DefaultDependencies=no +Wants=udev.service +After=udev-trigger.service +Before=basic.target +ConditionCapability=CAP_MKNOD + +[Service] +Type=oneshot +TimeoutSec=180 +RemainAfterExit=yes +ExecStart=@bindir@/udevadm settle + +[Install] +WantedBy=basic.target diff --git a/units/systemd-udev-trigger.service.in b/units/systemd-udev-trigger.service.in new file mode 100644 index 0000000000..ce937ae899 --- /dev/null +++ b/units/systemd-udev-trigger.service.in @@ -0,0 +1,11 @@ +[Unit] +Description=udev Coldplug all Devices +Wants=udev.service +After=udev-kernel.socket udev-control.socket +DefaultDependencies=no +ConditionCapability=CAP_MKNOD + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@bindir@/udevadm trigger --type=subsystems --action=add ; @bindir@/udevadm trigger --type=devices --action=add diff --git a/units/systemd-udev.service.in b/units/systemd-udev.service.in new file mode 100644 index 0000000000..60d8601217 --- /dev/null +++ b/units/systemd-udev.service.in @@ -0,0 +1,14 @@ +[Unit] +Description=udev Kernel Device Manager +Wants=udev-control.socket udev-kernel.socket +After=udev-control.socket udev-kernel.socket +Before=basic.target +DefaultDependencies=no +ConditionCapability=CAP_MKNOD + +[Service] +Type=notify +OOMScoreAdjust=-1000 +Sockets=systemd-udev-control.socket systemd-udev-kernel.socket +Restart=on-failure +ExecStart=@rootlibexecdir@/systemd-udevd diff --git a/units/udev-control.socket b/units/udev-control.socket deleted file mode 100644 index f80f774427..0000000000 --- a/units/udev-control.socket +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=udev Control Socket -DefaultDependencies=no -ConditionCapability=CAP_MKNOD - -[Socket] -Service=udev.service -ListenSequentialPacket=/run/udev/control -SocketMode=0600 -PassCredentials=yes diff --git a/units/udev-kernel.socket b/units/udev-kernel.socket deleted file mode 100644 index 23fa9d5e11..0000000000 --- a/units/udev-kernel.socket +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=udev Kernel Socket -DefaultDependencies=no -ConditionCapability=CAP_MKNOD - -[Socket] -Service=udev.service -ReceiveBuffer=134217728 -ListenNetlink=kobject-uevent 1 -PassCredentials=yes diff --git a/units/udev-settle.service.in b/units/udev-settle.service.in deleted file mode 100644 index 0a5e8c51ff..0000000000 --- a/units/udev-settle.service.in +++ /dev/null @@ -1,26 +0,0 @@ -# This service is usually not enabled by default. If enabled, it -# acts as a barrier for basic.target -- so all later services will -# wait for udev completely finishing its coldplug run. -# -# If needed, to work around broken or non-hotplug-aware services, -# it might be enabled unconditionally, or pulled-in on-demand by -# the services that assume a fully populated /dev at startup. It -# should not be used or pulled-in ever on systems without such -# legacy services running. - -[Unit] -Description=udev Wait for Complete Device Initialization -DefaultDependencies=no -Wants=udev.service -After=udev-trigger.service -Before=basic.target -ConditionCapability=CAP_MKNOD - -[Service] -Type=oneshot -TimeoutSec=180 -RemainAfterExit=yes -ExecStart=@bindir@/udevadm settle - -[Install] -WantedBy=basic.target diff --git a/units/udev-trigger.service.in b/units/udev-trigger.service.in deleted file mode 100644 index ce937ae899..0000000000 --- a/units/udev-trigger.service.in +++ /dev/null @@ -1,11 +0,0 @@ -[Unit] -Description=udev Coldplug all Devices -Wants=udev.service -After=udev-kernel.socket udev-control.socket -DefaultDependencies=no -ConditionCapability=CAP_MKNOD - -[Service] -Type=oneshot -RemainAfterExit=yes -ExecStart=@bindir@/udevadm trigger --type=subsystems --action=add ; @bindir@/udevadm trigger --type=devices --action=add diff --git a/units/udev.service.in b/units/udev.service.in deleted file mode 100644 index 7336e0eec7..0000000000 --- a/units/udev.service.in +++ /dev/null @@ -1,14 +0,0 @@ -[Unit] -Description=udev Kernel Device Manager -Wants=udev-control.socket udev-kernel.socket -After=udev-control.socket udev-kernel.socket -Before=basic.target -DefaultDependencies=no -ConditionCapability=CAP_MKNOD - -[Service] -Type=notify -OOMScoreAdjust=-1000 -Sockets=udev-control.socket udev-kernel.socket -Restart=on-failure -ExecStart=@udevlibexecdir@/udevd -- cgit v1.2.3-54-g00ecf From acf9b2f154fb443ea5654ea866d90556885b2e4f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 17 Apr 2012 16:04:42 +0200 Subject: update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 08beb9afec..ea031c4524 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/build-aux /test-watchdog /test-journal-send /systemd-multi-seat-x -- cgit v1.2.3-54-g00ecf From 2b93b027d3a68b5d7ae26d0c2cd487eb5019d2a9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 24 Apr 2012 16:42:42 +0200 Subject: remount: consolidate remount-api-vfs and remount-rootfs into one This has the advantage of removing a confusing warning by mount if the root directory is not listed in fstab. --- .gitignore | 1 + Makefile.am | 19 ++-- src/core/special.h | 2 +- src/core/swap.c | 4 +- src/remount-api-vfs/Makefile | 1 - src/remount-api-vfs/remount-api-vfs.c | 166 ------------------------------ src/remount-fs/Makefile | 1 + src/remount-fs/remount-fs.c | 168 +++++++++++++++++++++++++++++++ units/.gitignore | 2 +- units/quotacheck.service.in | 2 +- units/remount-rootfs.service | 20 ---- units/systemd-remount-api-vfs.service.in | 20 ---- units/systemd-remount-fs.service.in | 20 ++++ 13 files changed, 203 insertions(+), 223 deletions(-) delete mode 120000 src/remount-api-vfs/Makefile delete mode 100644 src/remount-api-vfs/remount-api-vfs.c create mode 120000 src/remount-fs/Makefile create mode 100644 src/remount-fs/remount-fs.c delete mode 100644 units/remount-rootfs.service delete mode 100644 units/systemd-remount-api-vfs.service.in create mode 100644 units/systemd-remount-fs.service.in (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index ea031c4524..a921c76ff6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/systemd-remount-fs /build-aux /test-watchdog /test-journal-send diff --git a/Makefile.am b/Makefile.am index ab7fca677a..b503b0124e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -225,7 +225,7 @@ rootlibexec_PROGRAMS = \ systemd-shutdownd \ systemd-shutdown \ systemd-modules-load \ - systemd-remount-api-vfs \ + systemd-remount-fs \ systemd-reply-password \ systemd-fsck \ systemd-timestamp \ @@ -287,7 +287,6 @@ dist_systemunit_DATA = \ units/sys-kernel-debug.mount \ units/sys-fs-fuse-connections.mount \ units/tmp.mount \ - units/remount-rootfs.service \ units/printer.target \ units/sound.target \ units/bluetooth.target \ @@ -308,7 +307,7 @@ nodist_systemunit_DATA = \ units/systemd-initctl.service \ units/systemd-shutdownd.service \ units/systemd-modules-load.service \ - units/systemd-remount-api-vfs.service \ + units/systemd-remount-fs.service \ units/systemd-update-utmp-runlevel.service \ units/systemd-update-utmp-shutdown.service \ units/systemd-tmpfiles-setup.service \ @@ -345,7 +344,7 @@ EXTRA_DIST += \ units/systemd-initctl.service.in \ units/systemd-shutdownd.service.in \ units/systemd-modules-load.service.in \ - units/systemd-remount-api-vfs.service.in \ + units/systemd-remount-fs.service.in \ units/systemd-update-utmp-runlevel.service.in \ units/systemd-update-utmp-shutdown.service.in \ units/systemd-tmpfiles-setup.service.in \ @@ -1110,12 +1109,12 @@ systemd_rc_local_generator_LDADD = \ libsystemd-shared.la # ------------------------------------------------------------------------------ -systemd_remount_api_vfs_SOURCES = \ - src/remount-api-vfs/remount-api-vfs.c \ +systemd_remount_fs_SOURCES = \ + src/remount-fs/remount-fs.c \ src/core/mount-setup.c \ src/core/mount-setup.h -systemd_remount_api_vfs_LDADD = \ +systemd_remount_fs_LDADD = \ libsystemd-shared.la # ------------------------------------------------------------------------------ @@ -3059,13 +3058,11 @@ systemd-install-data-hook: rm -f systemd-update-utmp-shutdown.service && \ $(LN_S) ../systemd-update-utmp-shutdown.service systemd-update-utmp-shutdown.service ) ( cd $(DESTDIR)$(systemunitdir)/local-fs.target.wants && \ - rm -f systemd-remount-api-vfs.service \ + rm -f systemd-remount-fs.service \ fsck-root.service \ - remount-rootfs.service \ tmp.mount && \ - $(LN_S) ../systemd-remount-api-vfs.service systemd-remount-api-vfs.service && \ + $(LN_S) ../systemd-remount-fs.service systemd-remount-fs.service && \ $(LN_S) ../fsck-root.service fsck-root.service && \ - $(LN_S) ../remount-rootfs.service remount-rootfs.service && \ $(LN_S) ../tmp.mount tmp.mount ) ( cd $(DESTDIR)$(userunitdir) && \ rm -f shutdown.target sockets.target bluetooth.target printer.target sound.target && \ diff --git a/src/core/special.h b/src/core/special.h index f1ff1b94c0..2db4711c2e 100644 --- a/src/core/special.h +++ b/src/core/special.h @@ -65,7 +65,7 @@ #define SPECIAL_FSCK_SERVICE "fsck@.service" #define SPECIAL_QUOTACHECK_SERVICE "quotacheck.service" #define SPECIAL_QUOTAON_SERVICE "quotaon.service" -#define SPECIAL_REMOUNT_ROOTFS_SERVICE "remount-rootfs.service" +#define SPECIAL_REMOUNT_FS_SERVICE "systemd-remount-fs.service" /* Services systemd relies on */ #define SPECIAL_DBUS_SERVICE "dbus.service" diff --git a/src/core/swap.c b/src/core/swap.c index fea3f6887a..d2f949118b 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -211,9 +211,9 @@ static int swap_add_device_links(Swap *s) { UNIT(s)->manager->running_as == MANAGER_SYSTEM); else /* File based swap devices need to be ordered after - * remount-rootfs.service, since they might need a + * systemd-remount-fs.service, since they might need a * writable file system. */ - return unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_REMOUNT_ROOTFS_SERVICE, NULL, true); + return unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_REMOUNT_FS_SERVICE, NULL, true); } static int swap_add_default_dependencies(Swap *s) { diff --git a/src/remount-api-vfs/Makefile b/src/remount-api-vfs/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/remount-api-vfs/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/remount-api-vfs/remount-api-vfs.c b/src/remount-api-vfs/remount-api-vfs.c deleted file mode 100644 index 373ae25520..0000000000 --- a/src/remount-api-vfs/remount-api-vfs.c +++ /dev/null @@ -1,166 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include -#include -#include -#include -#include -#include - -#include "log.h" -#include "util.h" -#include "set.h" -#include "mount-setup.h" -#include "exit-status.h" - -/* Goes through /etc/fstab and remounts all API file systems, applying - * options that are in /etc/fstab that systemd might not have - * respected */ - -int main(int argc, char *argv[]) { - int ret = EXIT_FAILURE; - FILE *f = NULL; - struct mntent* me; - Hashmap *pids = NULL; - - if (argc > 1) { - log_error("This program takes no argument."); - return EXIT_FAILURE; - } - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - f = setmntent("/etc/fstab", "r"); - if (!f) { - if (errno == ENOENT) { - ret = EXIT_SUCCESS; - goto finish; - } - - log_error("Failed to open /etc/fstab: %m"); - goto finish; - } - - pids = hashmap_new(trivial_hash_func, trivial_compare_func); - if (!pids) { - log_error("Failed to allocate set"); - goto finish; - } - - ret = EXIT_SUCCESS; - - while ((me = getmntent(f))) { - pid_t pid; - int k; - char *s; - - if (!mount_point_is_api(me->mnt_dir)) - continue; - - log_debug("Remounting %s", me->mnt_dir); - - pid = fork(); - if (pid < 0) { - log_error("Failed to fork: %m"); - ret = EXIT_FAILURE; - continue; - } - - if (pid == 0) { - const char *arguments[5]; - /* Child */ - - arguments[0] = "/bin/mount"; - arguments[1] = me->mnt_dir; - arguments[2] = "-o"; - arguments[3] = "remount"; - arguments[4] = NULL; - - execv("/bin/mount", (char **) arguments); - - log_error("Failed to execute /bin/mount: %m"); - _exit(EXIT_FAILURE); - } - - /* Parent */ - - s = strdup(me->mnt_dir); - if (!s) { - log_error("Out of memory."); - ret = EXIT_FAILURE; - continue; - } - - - k = hashmap_put(pids, UINT_TO_PTR(pid), s); - if (k < 0) { - log_error("Failed to add PID to set: %s", strerror(-k)); - ret = EXIT_FAILURE; - continue; - } - } - - while (!hashmap_isempty(pids)) { - siginfo_t si; - char *s; - - zero(si); - if (waitid(P_ALL, 0, &si, WEXITED) < 0) { - - if (errno == EINTR) - continue; - - log_error("waitid() failed: %m"); - ret = EXIT_FAILURE; - break; - } - - s = hashmap_remove(pids, UINT_TO_PTR(si.si_pid)); - if (s) { - if (!is_clean_exit(si.si_code, si.si_status)) { - if (si.si_code == CLD_EXITED) - log_error("/bin/mount for %s exited with exit status %i.", s, si.si_status); - else - log_error("/bin/mount for %s terminated by signal %s.", s, signal_to_string(si.si_status)); - - ret = EXIT_FAILURE; - } - - free(s); - } - } - -finish: - - if (pids) - hashmap_free_free(pids); - - if (f) - endmntent(f); - - return ret; -} diff --git a/src/remount-fs/Makefile b/src/remount-fs/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/remount-fs/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/remount-fs/remount-fs.c b/src/remount-fs/remount-fs.c new file mode 100644 index 0000000000..35b71548a3 --- /dev/null +++ b/src/remount-fs/remount-fs.c @@ -0,0 +1,168 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "util.h" +#include "set.h" +#include "mount-setup.h" +#include "exit-status.h" + +/* Goes through /etc/fstab and remounts all API file systems, applying + * options that are in /etc/fstab that systemd might not have + * respected */ + +int main(int argc, char *argv[]) { + int ret = EXIT_FAILURE; + FILE *f = NULL; + struct mntent* me; + Hashmap *pids = NULL; + + if (argc > 1) { + log_error("This program takes no argument."); + return EXIT_FAILURE; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + f = setmntent("/etc/fstab", "r"); + if (!f) { + if (errno == ENOENT) { + ret = EXIT_SUCCESS; + goto finish; + } + + log_error("Failed to open /etc/fstab: %m"); + goto finish; + } + + pids = hashmap_new(trivial_hash_func, trivial_compare_func); + if (!pids) { + log_error("Failed to allocate set"); + goto finish; + } + + ret = EXIT_SUCCESS; + + while ((me = getmntent(f))) { + pid_t pid; + int k; + char *s; + + /* Remount the root fs and all API VFS */ + if (!mount_point_is_api(me->mnt_dir) && + !path_equal(me->mnt_dir, "/")) + continue; + + log_debug("Remounting %s", me->mnt_dir); + + pid = fork(); + if (pid < 0) { + log_error("Failed to fork: %m"); + ret = EXIT_FAILURE; + continue; + } + + if (pid == 0) { + const char *arguments[5]; + /* Child */ + + arguments[0] = "/bin/mount"; + arguments[1] = me->mnt_dir; + arguments[2] = "-o"; + arguments[3] = "remount"; + arguments[4] = NULL; + + execv("/bin/mount", (char **) arguments); + + log_error("Failed to execute /bin/mount: %m"); + _exit(EXIT_FAILURE); + } + + /* Parent */ + + s = strdup(me->mnt_dir); + if (!s) { + log_error("Out of memory."); + ret = EXIT_FAILURE; + continue; + } + + + k = hashmap_put(pids, UINT_TO_PTR(pid), s); + if (k < 0) { + log_error("Failed to add PID to set: %s", strerror(-k)); + ret = EXIT_FAILURE; + continue; + } + } + + while (!hashmap_isempty(pids)) { + siginfo_t si; + char *s; + + zero(si); + if (waitid(P_ALL, 0, &si, WEXITED) < 0) { + + if (errno == EINTR) + continue; + + log_error("waitid() failed: %m"); + ret = EXIT_FAILURE; + break; + } + + s = hashmap_remove(pids, UINT_TO_PTR(si.si_pid)); + if (s) { + if (!is_clean_exit(si.si_code, si.si_status)) { + if (si.si_code == CLD_EXITED) + log_error("/bin/mount for %s exited with exit status %i.", s, si.si_status); + else + log_error("/bin/mount for %s terminated by signal %s.", s, signal_to_string(si.si_status)); + + ret = EXIT_FAILURE; + } + + free(s); + } + } + +finish: + + if (pids) + hashmap_free_free(pids); + + if (f) + endmntent(f); + + return ret; +} diff --git a/units/.gitignore b/units/.gitignore index 16cbf2df2b..68c174f1f5 100644 --- a/units/.gitignore +++ b/units/.gitignore @@ -28,7 +28,7 @@ systemd-readahead-replay.service serial-getty@.service systemd-kmsg-syslogd.service systemd-modules-load.service -systemd-remount-api-vfs.service +/systemd-remount-fs.service systemd-vconsole-setup.service systemd-auto-serial-getty.service systemd-shutdownd.service diff --git a/units/quotacheck.service.in b/units/quotacheck.service.in index d28b533f9c..d3343df264 100644 --- a/units/quotacheck.service.in +++ b/units/quotacheck.service.in @@ -8,7 +8,7 @@ [Unit] Description=File System Quota Check DefaultDependencies=no -After=systemd-readahead-collect.service systemd-readahead-replay.service remount-rootfs.service +After=systemd-readahead-collect.service systemd-readahead-replay.service systemd-remount-fs.service Before=local-fs.target shutdown.target ConditionPathExists=/sbin/quotacheck diff --git a/units/remount-rootfs.service b/units/remount-rootfs.service deleted file mode 100644 index 52a70743bc..0000000000 --- a/units/remount-rootfs.service +++ /dev/null @@ -1,20 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -[Unit] -Description=Remount Root FS -DefaultDependencies=no -Conflicts=shutdown.target -After=systemd-readahead-collect.service systemd-readahead-replay.service fsck-root.service -Before=local-fs-pre.target local-fs.target shutdown.target -Wants=local-fs-pre.target -ConditionPathExists=/etc/fstab - -[Service] -Type=oneshot -RemainAfterExit=yes -ExecStart=/bin/mount / -o remount diff --git a/units/systemd-remount-api-vfs.service.in b/units/systemd-remount-api-vfs.service.in deleted file mode 100644 index 60ff9c3f42..0000000000 --- a/units/systemd-remount-api-vfs.service.in +++ /dev/null @@ -1,20 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -[Unit] -Description=Remount API VFS -DefaultDependencies=no -Conflicts=shutdown.target -After=systemd-readahead-collect.service systemd-readahead-replay.service -Before=local-fs-pre.target local-fs.target shutdown.target -Wants=local-fs-pre.target -ConditionPathExists=/etc/fstab - -[Service] -Type=oneshot -RemainAfterExit=yes -ExecStart=@rootlibexecdir@/systemd-remount-api-vfs diff --git a/units/systemd-remount-fs.service.in b/units/systemd-remount-fs.service.in new file mode 100644 index 0000000000..5074ab5ddc --- /dev/null +++ b/units/systemd-remount-fs.service.in @@ -0,0 +1,20 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Remount Root and Kernel File Systems +DefaultDependencies=no +Conflicts=shutdown.target +After=systemd-readahead-collect.service systemd-readahead-replay.service fsck-root.service +Before=local-fs-pre.target local-fs.target shutdown.target +Wants=local-fs-pre.target +ConditionPathExists=/etc/fstab + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-remount-fs -- cgit v1.2.3-54-g00ecf From eecd1362f7f4de432483b5d77c56726c3621a83a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 5 May 2012 00:34:48 +0200 Subject: logind: implement delay inhibitor locks in addition to block inhibitor locks This is useful to allow applications to synchronously save data before the system is suspended or shut down. --- .gitignore | 1 + Makefile.am | 17 +- TODO | 10 + man/logind.conf.xml | 14 ++ man/systemd-inhibit.xml | 179 +++++++++++++++ src/login/inhibit.c | 353 +++++++++++++++++++++++++++++ src/login/logind-dbus.c | 196 ++++++++++++---- src/login/logind-gperf.gperf | 1 + src/login/logind-inhibit.c | 47 +++- src/login/logind-inhibit.h | 15 +- src/login/logind.c | 21 +- src/login/logind.conf | 1 + src/login/logind.h | 10 + src/login/org.freedesktop.login1.policy.in | 32 ++- src/shared/dbus-common.c | 11 +- src/shared/dbus-common.h | 1 + 16 files changed, 843 insertions(+), 66 deletions(-) create mode 100644 man/systemd-inhibit.xml create mode 100644 src/login/inhibit.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index a921c76ff6..7f22255c52 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/systemd-inhibit /systemd-remount-fs /build-aux /test-watchdog diff --git a/Makefile.am b/Makefile.am index fd08001254..c85a401db4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -481,7 +481,8 @@ MANPAGES = \ man/systemd-machine-id-setup.1 \ man/systemd-detect-virt.1 \ man/journald.conf.5 \ - man/journalctl.1 + man/journalctl.1 \ + man/systemd-inhibit.1 MANPAGES_ALIAS = \ man/reboot.8 \ @@ -2666,6 +2667,20 @@ loginctl_LDADD = \ rootbin_PROGRAMS += \ loginctl +systemd_inhibit_SOURCES = \ + src/login/inhibit.c + +systemd_inhibit_CFLAGS = \ + $(AM_CFLAGS) \ + $(DBUS_CFLAGS) + +systemd_inhibit_LDADD = \ + libsystemd-shared.la \ + libsystemd-dbus.la + +rootbin_PROGRAMS += \ + systemd-inhibit + test_login_SOURCES = \ src/login/test-login.c diff --git a/TODO b/TODO index d39c8fca66..b241a511ab 100644 --- a/TODO +++ b/TODO @@ -25,6 +25,16 @@ Features: * improve !/proc/*/loginuid situation: make /proc/*/loginuid less dependent on CONFIG_AUDIT, or use the users cgroup information when /proc/*/loginuid is not available. +* pam_systemd: try to get old session id from cgroup, if audit sessionid cannot be determined + +* logind: auto-suspend, auto-shutdown: + IdleAction=(none|suspend|hibernate|poweroff) + IdleActionDelay=... + SessionIdleMode=(explicit|ignore|login) + ForceShutdown=(yes|no) + +* logind: use "sleep" as generic term for "suspend", "hibernate", ... + * services which create their own subcgroups break cgroup-empty notification (needs to be fixed in the kernel) * don't delete /tmp/systemd-namespace-* before a process is gone down diff --git a/man/logind.conf.xml b/man/logind.conf.xml index 6a10fa9868..deca1cdd7f 100644 --- a/man/logind.conf.xml +++ b/man/logind.conf.xml @@ -146,6 +146,20 @@ defaults to cpu. + + + InhibitDelayMaxSec= + + Specifies the maximum + time a suspend or reboot is delayed + due to an inhibitor lock of type + delay being taken + before it is ignored and the operation + executed anyway. Defaults to + 5s. + + + Note that setting diff --git a/man/systemd-inhibit.xml b/man/systemd-inhibit.xml new file mode 100644 index 0000000000..34095902c2 --- /dev/null +++ b/man/systemd-inhibit.xml @@ -0,0 +1,179 @@ + + + + + + + + + systemd-inhibit + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-inhibit + 1 + + + + systemd-inhibit + Execute a program with an inhibition lock taken + + + + + systemd-inhibit OPTIONS COMMAND ARGUMENTS + + + systemd-inhibit OPTIONS --list + + + + + Description + + systemd-inhibit may be used + to execute a program with a shutdown, suspend or idle + inhibitor lock taken. The lock will be acquired before + the specified command line is executed and released + afterwards. + + Inhibitor locks may be used to block or delay + suspend and shutdown requests from the user, as well + as automatic idle handling of the OS. This may be used + to avoid system suspends while an optical disc is + being recorded, or similar operations that should not + be interrupted. + + + + Options + + The following options are understood: + + + + + + + Prints a short help + text and exits. + + + + + + Prints a short version + string and exits. + + + + + + Takes a colon + separated list of one or more + operations to inhibit: + shutdown, + suspend, + idle, for + inhibiting reboot/power-off/halt/kexec, + suspending/hibernating, resp. the + automatic idle + detection. + + + + + + Takes a short human + readable descriptive string for the + program taking the lock. If not passed + defaults to the command line + string. + + + + + + Takes a short human + readable descriptive string for the + reason for taking the lock. Defaults + to "Unknown reason". + + + + + + Takes either + block or + delay and describes + how the lock is applied. If + block is used (the + default), the lock prohibits any of + the requested operations without time + limit, and only privileged users may + override it. If + delay is used, the + lock can only delay the requested + operations for a limited time. If the + time elapses the lock is ignored and + the operation executed. The time limit + may be specified in + systemd-logind.conf5. + + + + + + Lists all active + inhibition locks instead of acquiring + one. + + + + + + + + + Exit status + + Returns the exit status of the executed program. + + + + See Also + + systemd1, + systemd-logind.conf5 + + + + diff --git a/src/login/inhibit.c b/src/login/inhibit.c new file mode 100644 index 0000000000..a817c84b36 --- /dev/null +++ b/src/login/inhibit.c @@ -0,0 +1,353 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include "dbus-common.h" +#include "util.h" +#include "build.h" +#include "strv.h" + +static const char* arg_what = "idle:suspend:shutdown"; +static const char* arg_who = NULL; +static const char* arg_why = "Unknown reason"; +static const char* arg_mode = "block"; + +static enum { + ACTION_INHIBIT, + ACTION_LIST +} arg_action = ACTION_INHIBIT; + +static int inhibit(DBusConnection *bus, DBusError *error) { + DBusMessage *m = NULL, *reply = NULL; + int fd; + + assert(bus); + + m = dbus_message_new_method_call( + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "Inhibit"); + if (!m) + return -ENOMEM; + + if (!dbus_message_append_args(m, + DBUS_TYPE_STRING, &arg_what, + DBUS_TYPE_STRING, &arg_who, + DBUS_TYPE_STRING, &arg_why, + DBUS_TYPE_STRING, &arg_mode, + DBUS_TYPE_INVALID)) { + fd = -ENOMEM; + goto finish; + } + + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error); + if (!reply) { + fd = -EIO; + goto finish; + } + + if (!dbus_message_get_args(reply, error, + DBUS_TYPE_UNIX_FD, &fd, + DBUS_TYPE_INVALID)){ + fd = -EIO; + goto finish; + } + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + return fd; +} + +static int print_inhibitors(DBusConnection *bus, DBusError *error) { + DBusMessage *m, *reply; + unsigned n = 0; + DBusMessageIter iter, sub, sub2; + int r; + + assert(bus); + + m = dbus_message_new_method_call( + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "ListInhibitors"); + if (!m) + return -ENOMEM; + + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error); + if (!reply) { + r = -EIO; + goto finish; + } + + if (!dbus_message_iter_init(reply, &iter)) { + r = -ENOMEM; + goto finish; + } + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { + r = -EIO; + goto finish; + } + dbus_message_iter_recurse(&iter, &sub); + + printf("%-21s %-20s %-20s %-5s %6s %6s\n", + "WHAT", + "WHO", + "WHY", + "MODE", + "UID", + "PID"); + + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + const char *what, *who, *why, *mode; + char *ewho, *ewhy; + dbus_uint32_t uid, pid; + + if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) { + r = -EIO; + goto finish; + } + + dbus_message_iter_recurse(&sub, &sub2); + + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &what, true) < 0 || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &who, true) < 0 || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &why, true) < 0 || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &mode, true) < 0 || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 || + bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &pid, false) < 0) { + r = -EIO; + goto finish; + } + + ewho = ellipsize(who, 20, 66); + ewhy = ellipsize(why, 20, 66); + + printf("%-21s %-20s %-20s %-5s %6lu %6lu\n", + what, ewho ? ewho : who, ewhy ? ewhy : why, mode, (unsigned long) uid, (unsigned long) pid); + + free(ewho); + free(ewhy); + + dbus_message_iter_next(&sub); + + n++; + } + + printf("\n%u inhibitors listed.\n", n); + r = 0; + +finish: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + return r; +} + +static int help(void) { + + printf("%s [OPTIONS...] {COMMAND} ...\n\n" + "Execute a process while inhibiting shutdown/suspend/idle.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --what=WHAT Operations to inhibit, colon separated list of idle,\n" + " suspend, shutdown\n" + " --who=STRING A descriptive string who is inhibiting\n" + " --why=STRING A descriptive string why is being inhibited\n" + " --mode=MODE One of block or delay\n" + " --list List active inhibitors\n", + program_invocation_short_name); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_WHAT, + ARG_WHO, + ARG_WHY, + ARG_MODE, + ARG_LIST, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "what", required_argument, NULL, ARG_WHAT }, + { "who", required_argument, NULL, ARG_WHO }, + { "why", required_argument, NULL, ARG_WHY }, + { "mode", required_argument, NULL, ARG_MODE }, + { "list", no_argument, NULL, ARG_LIST }, + { NULL, 0, NULL, 0 } + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "+h", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + puts(PACKAGE_STRING); + puts(DISTRIBUTION); + puts(SYSTEMD_FEATURES); + return 0; + + case ARG_WHAT: + arg_what = optarg; + break; + + case ARG_WHO: + arg_who = optarg; + break; + + case ARG_WHY: + arg_why = optarg; + break; + + case ARG_MODE: + arg_mode = optarg; + break; + + case ARG_LIST: + arg_action = ACTION_LIST; + break; + + default: + log_error("Unknown option code %c", c); + return -EINVAL; + } + } + + if (arg_action == ACTION_INHIBIT && optind >= argc) { + log_error("Missing command line to execute."); + return -EINVAL; + } + + return 1; +} + +int main(int argc, char *argv[]) { + int r, exit_code = 0; + DBusConnection *bus = NULL; + DBusError error; + int fd = -1; + + dbus_error_init(&error); + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); + if (!bus) { + log_error("Failed to connect to bus: %s", bus_error_message(&error)); + r = -EIO; + goto finish; + } + + if (arg_action == ACTION_LIST) { + + r = print_inhibitors(bus, &error); + if (r < 0) { + log_error("Failed to list inhibitors: %s", bus_error_message_or_strerror(&error, -r)); + goto finish; + } + + } else { + char *w = NULL; + pid_t pid; + + if (!arg_who) + arg_who = w = strv_join(argv + optind, " "); + + fd = inhibit(bus, &error); + free(w); + + if (fd < 0) { + log_error("Failed to inhibit: %s", bus_error_message_or_strerror(&error, -r)); + r = fd; + goto finish; + } + + pid = fork(); + if (pid < 0) { + log_error("Failed to fork: %m"); + r = -errno; + goto finish; + } + + if (pid == 0) { + /* Child */ + + close_nointr_nofail(fd); + execvp(argv[optind], argv + optind); + log_error("Failed to execute %s: %m", argv[optind]); + _exit(EXIT_FAILURE); + } + + r = wait_for_terminate_and_warn(argv[optind], pid); + if (r >= 0) + exit_code = r; + } + +finish: + if (bus) { + dbus_connection_close(bus); + dbus_connection_unref(bus); + } + + dbus_error_free(&error); + + if (fd >= 0) + close_nointr_nofail(fd); + + return r < 0 ? EXIT_FAILURE : exit_code; +} diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 3fe6c872b7..a361b93dc7 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -144,10 +144,11 @@ " \n" \ " \n" \ " \n" \ + " \n" \ " \n" \ " \n" \ " \n" \ - " \n" \ + " \n" \ " \n" \ " \n" \ " \n" \ @@ -173,6 +174,9 @@ " \n" \ " \n" \ " \n" \ + " \n" \ + " \n" \ + " \n" \ " \n" \ " \n" \ " \n" \ @@ -183,7 +187,9 @@ " \n" \ " \n" \ " \n" \ - " \n" \ + " \n" \ + " \n" \ + " \n" \ " \n" #define INTROSPECTION_BEGIN \ @@ -239,7 +245,7 @@ static int bus_manager_append_inhibited(DBusMessageIter *i, const char *property InhibitWhat w; const char *p; - w = manager_inhibit_what(m); + w = manager_inhibit_what(m, streq(property, "BlockInhibited") ? INHIBIT_BLOCK : INHIBIT_DELAY); p = inhibit_what_to_string(w); if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &p)) @@ -638,9 +644,10 @@ fail: static int bus_manager_inhibit(Manager *m, DBusConnection *connection, DBusMessage *message, DBusError *error, DBusMessage **_reply) { Inhibitor *i = NULL; char *id = NULL; - const char *who, *why, *what; + const char *who, *why, *what, *mode; pid_t pid; InhibitWhat w; + InhibitMode mm; unsigned long ul; int r, fifo_fd = -1; DBusMessage *reply = NULL; @@ -657,6 +664,7 @@ static int bus_manager_inhibit(Manager *m, DBusConnection *connection, DBusMessa DBUS_TYPE_STRING, &what, DBUS_TYPE_STRING, &who, DBUS_TYPE_STRING, &why, + DBUS_TYPE_STRING, &mode, DBUS_TYPE_INVALID)) { r = -EIO; goto fail; @@ -668,7 +676,16 @@ static int bus_manager_inhibit(Manager *m, DBusConnection *connection, DBusMessa goto fail; } - r = verify_polkit(connection, message, "org.freedesktop.login1.inhibit", false, NULL, error); + mm = inhibit_mode_from_string(mode); + if (mm < 0) { + r = -EINVAL; + goto fail; + } + + r = verify_polkit(connection, message, + m == INHIBIT_BLOCK ? + "org.freedesktop.login1.inhibit-block" : + "org.freedesktop.login1.inhibit-delay", false, NULL, error); if (r < 0) goto fail; @@ -701,6 +718,7 @@ static int bus_manager_inhibit(Manager *m, DBusConnection *connection, DBusMessa goto fail; i->what = w; + i->mode = mm; i->pid = pid; i->uid = (uid_t) ul; i->why = strdup(why); @@ -918,6 +936,76 @@ static int have_multiple_sessions( return false; } +static int send_start_unit(DBusConnection *connection, const char *name, DBusError *error) { + DBusMessage *message, *reply; + const char *mode = "replace"; + + assert(connection); + assert(name); + + message = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartUnit"); + if (!message) + return -ENOMEM; + + if (!dbus_message_append_args(message, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &mode, + DBUS_TYPE_INVALID)) { + dbus_message_unref(message); + return -ENOMEM; + } + + reply = dbus_connection_send_with_reply_and_block(connection, message, -1, error); + dbus_message_unref(message); + + if (!reply) + return -EIO; + + dbus_message_unref(reply); + return 0; +} + +static int send_prepare_for_shutdown(Manager *m, bool _active) { + dbus_bool_t active = _active; + DBusMessage *message; + int r = 0; + + assert(m); + + message = dbus_message_new_signal("/org/freedesktop/login1", "org.freedesktop.login1.Manager", "PrepareForShutdown"); + if (!message) + return -ENOMEM; + + if (!dbus_message_append_args(message, DBUS_TYPE_BOOLEAN, &active, DBUS_TYPE_INVALID) || + !dbus_connection_send(m->bus, message, NULL)) + r = -ENOMEM; + + dbus_message_unref(message); + return r; +} + +static int delay_shutdown(Manager *m, const char *name) { + assert(m); + + if (!m->delayed_shutdown) { + /* Tell everybody to prepare for shutdown */ + send_prepare_for_shutdown(m, true); + + /* Update timestamp for timeout */ + m->delayed_shutdown_timestamp = now(CLOCK_MONOTONIC); + } + + /* Remember what we want to do, possibly overriding what kind + * of shutdown we previously queued. */ + m->delayed_shutdown = name; + + return 0; +} + static const BusProperty bus_login_manager_properties[] = { { "ControlGroupHierarchy", bus_property_append_string, "s", offsetof(Manager, cgroup_path), true }, { "Controllers", bus_property_append_strv, "as", offsetof(Manager, controllers), true }, @@ -929,7 +1017,9 @@ static const BusProperty bus_login_manager_properties[] = { { "IdleHint", bus_manager_append_idle_hint, "b", 0 }, { "IdleSinceHint", bus_manager_append_idle_hint_since, "t", 0 }, { "IdleSinceHintMonotonic", bus_manager_append_idle_hint_since, "t", 0 }, - { "Inhibited", bus_manager_append_inhibited, "s", 0 }, + { "BlockInhibited", bus_manager_append_inhibited, "s", 0 }, + { "DelayInhibited", bus_manager_append_inhibited, "s", 0 }, + { "InhibitDelayMaxUSec", bus_property_append_usec, "t", offsetof(Manager, inhibit_delay_max) }, { NULL, } }; @@ -1228,26 +1318,28 @@ static DBusHandlerResult manager_message_handler( dbus_message_iter_init_append(reply, &iter); - if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sssuu)", &sub)) + if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssssuu)", &sub)) goto oom; HASHMAP_FOREACH(inhibitor, m->inhibitors, i) { DBusMessageIter sub2; dbus_uint32_t uid, pid; - const char *what, *who, *why; + const char *what, *who, *why, *mode; if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2)) goto oom; - what = inhibit_what_to_string(inhibitor->what); + what = strempty(inhibit_what_to_string(inhibitor->what)); who = strempty(inhibitor->who); why = strempty(inhibitor->why); + mode = strempty(inhibit_mode_to_string(inhibitor->mode)); uid = (dbus_uint32_t) inhibitor->uid; pid = (dbus_uint32_t) inhibitor->pid; if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &what) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &who) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &why) || + !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &mode) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) || !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &pid)) goto oom; @@ -1641,10 +1733,8 @@ static DBusHandlerResult manager_message_handler( } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "PowerOff") || dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "Reboot")) { dbus_bool_t interactive; - bool multiple_sessions, inhibit; - DBusMessage *forward, *freply; + bool multiple_sessions, blocked, delayed; const char *name, *action; - const char *mode = "replace"; if (!dbus_message_get_args( message, @@ -1658,7 +1748,7 @@ static DBusHandlerResult manager_message_handler( return bus_send_error_reply(connection, message, &error, r); multiple_sessions = r > 0; - inhibit = manager_is_inhibited(m, INHIBIT_SHUTDOWN, NULL); + blocked = manager_is_inhibited(m, INHIBIT_SHUTDOWN, INHIBIT_BLOCK, NULL); if (multiple_sessions) { action = streq(dbus_message_get_member(message), "PowerOff") ? @@ -1670,7 +1760,7 @@ static DBusHandlerResult manager_message_handler( return bus_send_error_reply(connection, message, &error, r); } - if (inhibit) { + if (blocked) { action = streq(dbus_message_get_member(message), "PowerOff") ? "org.freedesktop.login1.power-off-ignore-inhibit" : "org.freedesktop.login1.reboot-ignore-inhibit"; @@ -1680,7 +1770,7 @@ static DBusHandlerResult manager_message_handler( return bus_send_error_reply(connection, message, &error, r); } - if (!multiple_sessions && !inhibit) { + if (!multiple_sessions && !blocked) { action = streq(dbus_message_get_member(message), "PowerOff") ? "org.freedesktop.login1.power-off" : "org.freedesktop.login1.reboot"; @@ -1690,32 +1780,26 @@ static DBusHandlerResult manager_message_handler( return bus_send_error_reply(connection, message, &error, r); } - forward = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartUnit"); - if (!forward) - return bus_send_error_reply(connection, message, NULL, -ENOMEM); - name = streq(dbus_message_get_member(message), "PowerOff") ? SPECIAL_POWEROFF_TARGET : SPECIAL_REBOOT_TARGET; - if (!dbus_message_append_args(forward, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_STRING, &mode, - DBUS_TYPE_INVALID)) { - dbus_message_unref(forward); - return bus_send_error_reply(connection, message, NULL, -ENOMEM); - } - - freply = dbus_connection_send_with_reply_and_block(connection, forward, -1, &error); - dbus_message_unref(forward); + delayed = + m->inhibit_delay_max > 0 && + manager_is_inhibited(m, INHIBIT_SHUTDOWN, INHIBIT_DELAY, NULL); - if (!freply) - return bus_send_error_reply(connection, message, &error, -EIO); - - dbus_message_unref(freply); + if (delayed) { + /* Shutdown is delayed, keep in mind what we + * want to do, and start a timeout */ + r = delay_shutdown(m, name); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + } else { + /* Shutdown is not delayed, execute it + * immediately */ + r = send_start_unit(connection, name, &error); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + } reply = dbus_message_new_method_return(message); if (!reply) @@ -1732,7 +1816,7 @@ static DBusHandlerResult manager_message_handler( return bus_send_error_reply(connection, message, &error, r); multiple_sessions = r > 0; - inhibit = manager_is_inhibited(m, INHIBIT_SHUTDOWN, NULL); + inhibit = manager_is_inhibited(m, INHIBIT_SHUTDOWN, INHIBIT_BLOCK, NULL); if (multiple_sessions) { action = streq(dbus_message_get_member(message), "CanPowerOff") ? @@ -1943,3 +2027,39 @@ finish: return r; } + +int manager_dispatch_delayed_shutdown(Manager *manager) { + const char *name; + DBusError error; + bool delayed; + int r; + + assert(manager); + + if (!manager->delayed_shutdown) + return 0; + + /* Continue delay? */ + delayed = + manager->delayed_shutdown_timestamp + manager->inhibit_delay_max > now(CLOCK_MONOTONIC) && + manager_is_inhibited(manager, INHIBIT_SHUTDOWN, INHIBIT_DELAY, NULL); + if (delayed) + return 0; + + /* Reset delay data */ + name = manager->delayed_shutdown; + manager->delayed_shutdown = NULL; + + /* Actually do the shutdown */ + dbus_error_init(&error); + r = send_start_unit(manager->bus, name, &error); + if (r < 0) { + log_warning("Failed to send delayed shutdown message: %s", bus_error_message_or_strerror(&error, -r)); + return r; + } + + /* Tell people about it */ + send_prepare_for_shutdown(manager, false); + + return 1; +} diff --git a/src/login/logind-gperf.gperf b/src/login/logind-gperf.gperf index 940fe10e89..d8ef92a016 100644 --- a/src/login/logind-gperf.gperf +++ b/src/login/logind-gperf.gperf @@ -20,3 +20,4 @@ Login.KillOnlyUsers, config_parse_strv, 0, offsetof(Manager, kill_only_u Login.KillExcludeUsers, config_parse_strv, 0, offsetof(Manager, kill_exclude_users) Login.Controllers, config_parse_strv, 0, offsetof(Manager, controllers) Login.ResetControllers, config_parse_strv, 0, offsetof(Manager, reset_controllers) +Login.InhibitDelayMaxSec,config_parse_usec, 0, offsetof(Manager, inhibit_delay_max) diff --git a/src/login/logind-inhibit.c b/src/login/logind-inhibit.c index 78afee3139..e4eefd0def 100644 --- a/src/login/logind-inhibit.c +++ b/src/login/logind-inhibit.c @@ -97,9 +97,11 @@ int inhibitor_save(Inhibitor *i) { fprintf(f, "# This is private data. Do not parse.\n" "WHAT=%s\n" + "MODE=%s\n" "UID=%lu\n" "PID=%lu\n", inhibit_what_to_string(i->what), + inhibit_mode_to_string(i->mode), (unsigned long) i->uid, (unsigned long) i->pid); @@ -152,9 +154,10 @@ int inhibitor_start(Inhibitor *i) { dual_timestamp_get(&i->since); - log_debug("Inhibitor %s (%s) pid=%lu uid=%lu started.", + log_debug("Inhibitor %s (%s) pid=%lu uid=%lu mode=%s started.", strna(i->who), strna(i->why), - (unsigned long) i->pid, (unsigned long) i->uid); + (unsigned long) i->pid, (unsigned long) i->uid, + inhibit_mode_to_string(i->mode)); inhibitor_save(i); @@ -169,9 +172,10 @@ int inhibitor_stop(Inhibitor *i) { assert(i); if (i->started) - log_debug("Inhibitor %s (%s) pid=%lu uid=%lu stopped.", + log_debug("Inhibitor %s (%s) pid=%lu uid=%lu mode=%s stopped.", strna(i->who), strna(i->why), - (unsigned long) i->pid, (unsigned long) i->uid); + (unsigned long) i->pid, (unsigned long) i->uid, + inhibit_mode_to_string(i->mode)); if (i->state_file) unlink(i->state_file); @@ -185,13 +189,15 @@ int inhibitor_stop(Inhibitor *i) { int inhibitor_load(Inhibitor *i) { InhibitWhat w; + InhibitMode mm; int r; char *cc, *what = NULL, *uid = NULL, *pid = NULL, *who = NULL, - *why = NULL; + *why = NULL, + *mode = NULL; r = parse_env_file(i->state_file, NEWLINE, "WHAT", &what, @@ -199,17 +205,25 @@ int inhibitor_load(Inhibitor *i) { "PID", &pid, "WHO", &who, "WHY", &why, + "MODE", &mode, "FIFO", &i->fifo_path, NULL); if (r < 0) goto finish; - w = inhibit_what_from_string(what); + w = what ? inhibit_what_from_string(what) : 0; if (w >= 0) i->what = w; - parse_uid(uid, &i->uid); - parse_pid(pid, &i->pid); + mm = mode ? inhibit_mode_from_string(mode) : INHIBIT_BLOCK; + if (mm >= 0) + i->mode = mm; + + if (uid) + parse_uid(uid, &i->uid); + + if (pid) + parse_pid(pid, &i->pid); if (who) { cc = cunescape(who); @@ -314,7 +328,7 @@ void inhibitor_remove_fifo(Inhibitor *i) { } } -InhibitWhat manager_inhibit_what(Manager *m) { +InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm) { Inhibitor *i; Iterator j; InhibitWhat what = 0; @@ -322,12 +336,13 @@ InhibitWhat manager_inhibit_what(Manager *m) { assert(m); HASHMAP_FOREACH(i, m->inhibitor_fds, j) - what |= i->what; + if (i->mode == mm) + what |= i->what; return what; } -bool manager_is_inhibited(Manager *m, InhibitWhat w, dual_timestamp *since) { +bool manager_is_inhibited(Manager *m, InhibitWhat w, InhibitMode mm, dual_timestamp *since) { Inhibitor *i; Iterator j; struct dual_timestamp ts = { 0, 0 }; @@ -340,6 +355,9 @@ bool manager_is_inhibited(Manager *m, InhibitWhat w, dual_timestamp *since) { if (!(i->what & w)) continue; + if (i->mode != mm) + continue; + if (!inhibited || i->since.monotonic < ts.monotonic) ts = i->since; @@ -391,3 +409,10 @@ InhibitWhat inhibit_what_from_string(const char *s) { return what; } + +static const char* const inhibit_mode_table[_INHIBIT_MODE_MAX] = { + [INHIBIT_BLOCK] = "block", + [INHIBIT_DELAY] = "delay" +}; + +DEFINE_STRING_TABLE_LOOKUP(inhibit_mode, InhibitMode); diff --git a/src/login/logind-inhibit.h b/src/login/logind-inhibit.h index 1d47cfc68d..823af39918 100644 --- a/src/login/logind-inhibit.h +++ b/src/login/logind-inhibit.h @@ -37,6 +37,13 @@ typedef enum InhibitWhat { _INHIBIT_WHAT_INVALID = -1 } InhibitWhat; +typedef enum InhibitMode { + INHIBIT_BLOCK, + INHIBIT_DELAY, + _INHIBIT_MODE_MAX, + _INHIBIT_MODE_INVALID = -1 +} InhibitMode; + struct Inhibitor { Manager *manager; @@ -48,6 +55,7 @@ struct Inhibitor { InhibitWhat what; char *who; char *why; + InhibitMode mode; pid_t pid; uid_t uid; @@ -70,10 +78,13 @@ int inhibitor_stop(Inhibitor *i); int inhibitor_create_fifo(Inhibitor *i); void inhibitor_remove_fifo(Inhibitor *i); -InhibitWhat manager_inhibit_what(Manager *m); -bool manager_is_inhibited(Manager *m, InhibitWhat w, dual_timestamp *since); +InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm); +bool manager_is_inhibited(Manager *m, InhibitWhat w, InhibitMode mm, dual_timestamp *since); const char *inhibit_what_to_string(InhibitWhat k); InhibitWhat inhibit_what_from_string(const char *s); +const char *inhibit_mode_to_string(InhibitMode k); +InhibitMode inhibit_mode_from_string(const char *s); + #endif diff --git a/src/login/logind.c b/src/login/logind.c index 6b7012ec73..5860bfc8d7 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -50,6 +50,7 @@ Manager *manager_new(void) { m->udev_vcsa_fd = -1; m->epoll_fd = -1; m->n_autovts = 6; + m->inhibit_delay_max = 5 * USEC_PER_SEC; m->devices = hashmap_new(string_hash_func, string_compare_func); m->seats = hashmap_new(string_hash_func, string_compare_func); @@ -1163,7 +1164,7 @@ int manager_get_idle_hint(Manager *m, dual_timestamp *t) { assert(m); - idle_hint = !manager_is_inhibited(m, INHIBIT_IDLE, t); + idle_hint = !manager_is_inhibited(m, INHIBIT_IDLE, INHIBIT_BLOCK, t); HASHMAP_FOREACH(s, m->sessions, i) { dual_timestamp k; @@ -1264,15 +1265,28 @@ int manager_run(Manager *m) { for (;;) { struct epoll_event event; int n; + int msec = -1; manager_gc(m, true); + if (manager_dispatch_delayed_shutdown(m) > 0) + continue; + if (dbus_connection_dispatch(m->bus) != DBUS_DISPATCH_COMPLETE) continue; manager_gc(m, true); - n = epoll_wait(m->epoll_fd, &event, 1, -1); + if (m->delayed_shutdown) { + usec_t x, y; + + x = now(CLOCK_MONOTONIC); + y = m->delayed_shutdown_timestamp + m->inhibit_delay_max; + + msec = x >= y ? 0 : (int) ((y - x) / USEC_PER_MSEC); + } + + n = epoll_wait(m->epoll_fd, &event, 1, msec); if (n < 0) { if (errno == EINTR || errno == EAGAIN) continue; @@ -1281,6 +1295,9 @@ int manager_run(Manager *m) { return -errno; } + if (n == 0) + continue; + switch (event.data.u32) { case FD_SEAT_UDEV: diff --git a/src/login/logind.conf b/src/login/logind.conf index 6a2cefa576..c0779a7685 100644 --- a/src/login/logind.conf +++ b/src/login/logind.conf @@ -14,3 +14,4 @@ #KillExcludeUsers=root #Controllers= #ResetControllers=cpu +#InhibitDelayMaxSec=5 diff --git a/src/login/logind.h b/src/login/logind.h index 4e9dcd5fed..2c0545206d 100644 --- a/src/login/logind.h +++ b/src/login/logind.h @@ -81,6 +81,14 @@ struct Manager { Hashmap *cgroups; Hashmap *session_fds; Hashmap *inhibitor_fds; + + /* If a shutdown was delayed due to a inhibitor this contains + the unit name we are supposed to start after the delay is + over */ + const char *delayed_shutdown; + usec_t delayed_shutdown_timestamp; + + usec_t inhibit_delay_max; }; enum { @@ -132,6 +140,8 @@ DBusHandlerResult bus_message_filter(DBusConnection *c, DBusMessage *message, vo int manager_send_changed(Manager *manager, const char *properties); +int manager_dispatch_delayed_shutdown(Manager *manager); + /* gperf lookup function */ const struct ConfigPerfItem* logind_gperf_lookup(const char *key, unsigned length); diff --git a/src/login/org.freedesktop.login1.policy.in b/src/login/org.freedesktop.login1.policy.in index a2dc4025ce..76ed2bea38 100644 --- a/src/login/org.freedesktop.login1.policy.in +++ b/src/login/org.freedesktop.login1.policy.in @@ -16,9 +16,9 @@ The systemd Project http://www.freedesktop.org/wiki/Software/systemd - + <_description>Allow applications to inhibit system shutdown and suspend - <_message>Authentication is required to allow an application to inhibit system shutdown or suspend + <_message>Authentication is required to allow an application to inhibit system shutdown or suspend. auth_admin_keep yes @@ -26,9 +26,19 @@ + + <_description>Allow applications to delay system shutdown and suspend + <_message>Authentication is required to allow an application to delay system shutdown or suspend. + + yes + yes + yes + + + <_description>Allow non-logged-in users to run programs - <_message>Authentication is required to allow a non-logged-in user to run programs + <_message>Authentication is required to allow a non-logged-in user to run programs. auth_admin_keep auth_admin_keep @@ -38,7 +48,7 @@ <_description>Allow attaching devices to seats - <_message>Authentication is required to allow attaching a device to a seat + <_message>Authentication is required to allow attaching a device to a seat. auth_admin_keep auth_admin_keep @@ -48,7 +58,7 @@ <_description>Flush device to seat attachments - <_message>Authentication is required to allow resetting how devices are attached to seats + <_message>Authentication is required to allow resetting how devices are attached to seats. auth_admin_keep auth_admin_keep @@ -58,7 +68,7 @@ <_description>Power off the system - <_message>Authentication is required to allow powering off the system + <_message>Authentication is required to allow powering off the system. auth_admin_keep auth_admin_keep @@ -68,7 +78,7 @@ <_description>Power off the system when other users are logged in - <_message>Authentication is required to allow powering off the system while other users are logged in + <_message>Authentication is required to allow powering off the system while other users are logged in. auth_admin_keep auth_admin_keep @@ -78,7 +88,7 @@ <_description>Power off the system when an application asked to inhibit it - <_message>Authentication is required to allow powering off the system while an application asked to inhibit it + <_message>Authentication is required to allow powering off the system while an application asked to inhibit it. auth_admin_keep auth_admin_keep @@ -88,7 +98,7 @@ <_description>Reboot the system - <_message>Authentication is required to allow rebooting the system + <_message>Authentication is required to allow rebooting the system. auth_admin_keep auth_admin_keep @@ -98,7 +108,7 @@ <_description>Reboot the system when other users are logged in - <_message>Authentication is required to allow rebooting the system while other users are logged in + <_message>Authentication is required to allow rebooting the system while other users are logged in. auth_admin_keep auth_admin_keep @@ -108,7 +118,7 @@ <_description>Reboot the system when an application asked to inhibit it - <_message>Authentication is required to allow rebooting the system while an application asked to inhibit it + <_message>Authentication is required to allow rebooting the system while an application asked to inhibit it. auth_admin_keep auth_admin_keep diff --git a/src/shared/dbus-common.c b/src/shared/dbus-common.c index e161273cd8..ddb50b1eca 100644 --- a/src/shared/dbus-common.c +++ b/src/shared/dbus-common.c @@ -245,7 +245,8 @@ int bus_connect_system_polkit(DBusConnection **_bus, DBusError *error) { } const char *bus_error_message(const DBusError *error) { - assert(error); + if (!error) + return NULL; /* Sometimes the D-Bus server is a little bit too verbose with * its error messages, so let's override them here */ @@ -255,6 +256,14 @@ const char *bus_error_message(const DBusError *error) { return error->message; } +const char *bus_error_message_or_strerror(const DBusError *error, int err) { + + if (error && dbus_error_is_set(error)) + return bus_error_message(error); + + return strerror(err); +} + DBusHandlerResult bus_default_message_handler( DBusConnection *c, DBusMessage *message, diff --git a/src/shared/dbus-common.h b/src/shared/dbus-common.h index edb8107347..9ae35df9c5 100644 --- a/src/shared/dbus-common.h +++ b/src/shared/dbus-common.h @@ -91,6 +91,7 @@ int bus_connect_system_ssh(const char *user, const char *host, DBusConnection ** int bus_connect_system_polkit(DBusConnection **_bus, DBusError *error); const char *bus_error_message(const DBusError *error); +const char *bus_error_message_or_strerror(const DBusError *error, int err); typedef int (*BusPropertyCallback)(DBusMessageIter *iter, const char *property, void *data); typedef int (*BusPropertySetCallback)(DBusMessageIter *iter, const char *property, void *data); -- cgit v1.2.3-54-g00ecf From 6edd7d0a09171ea5ae8e01b7b1cbcb0bdfbfeb16 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 5 May 2012 02:06:58 +0200 Subject: sleep: implement suspend/hibernate as first class targets --- .gitignore | 1 + Makefile.am | 18 +++++++++- TODO | 5 --- man/systemctl.xml | 10 ++++++ man/systemd.special.xml | 34 +++++++++++++++++++ src/core/shutdown.c | 6 +++- src/core/special.h | 2 ++ src/shared/util.c | 3 +- src/sleep/Makefile | 1 + src/sleep/sleep.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++ src/systemctl/systemctl.c | 16 +++++++-- units/.gitignore | 2 ++ units/hibernate.service.in | 16 +++++++++ units/hibernate.target | 14 ++++++++ units/sleep.target | 13 ++++++++ units/suspend.service.in | 16 +++++++++ units/suspend.target | 14 ++++++++ 17 files changed, 243 insertions(+), 11 deletions(-) create mode 120000 src/sleep/Makefile create mode 100644 src/sleep/sleep.c create mode 100644 units/hibernate.service.in create mode 100644 units/hibernate.target create mode 100644 units/sleep.target create mode 100644 units/suspend.service.in create mode 100644 units/suspend.target (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 7f22255c52..e61f1cdbad 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/systemd-sleep /systemd-inhibit /systemd-remount-fs /build-aux diff --git a/Makefile.am b/Makefile.am index dbcfeb384f..002a86aeba 100644 --- a/Makefile.am +++ b/Makefile.am @@ -67,6 +67,7 @@ usergeneratordir=$(pkglibexecdir)/user-generators pkgincludedir=$(includedir)/systemd systemgeneratordir=$(rootlibexecdir)/system-generators systemshutdowndir=$(rootlibexecdir)/system-shutdown +systemsleepdir=$(rootlibexecdir)/system-sleep systemunitdir=$(rootprefix)/lib/systemd/system udevlibexecdir=$(rootprefix)/lib/udev udevhomedir = $(libexecdir)/udev @@ -117,6 +118,7 @@ AM_CPPFLAGS = \ -DSYSTEMD_CGROUP_AGENT_PATH=\"$(rootlibexecdir)/systemd-cgroups-agent\" \ -DSYSTEMD_BINARY_PATH=\"$(rootlibexecdir)/systemd\" \ -DSYSTEMD_SHUTDOWN_BINARY_PATH=\"$(rootlibexecdir)/systemd-shutdown\" \ + -DSYSTEMD_SLEEP_BINARY_PATH=\"$(rootlibexecdir)/systemd-sleep\" \ -DSYSTEMCTL_BINARY_PATH=\"$(rootbindir)/systemctl\" \ -DSYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH=\"$(rootbindir)/systemd-tty-ask-password-agent\" \ -DSYSTEMD_STDIO_BRIDGE_BINARY_PATH=\"$(bindir)/systemd-stdio-bridge\" \ @@ -230,7 +232,8 @@ rootlibexec_PROGRAMS = \ systemd-fsck \ systemd-timestamp \ systemd-ac-power \ - systemd-sysctl + systemd-sysctl \ + systemd-sleep systemgenerator_PROGRAMS = \ systemd-getty-generator @@ -266,6 +269,7 @@ dist_systemunit_DATA = \ units/nss-lookup.target \ units/nss-user-lookup.target \ units/mail-transfer-agent.target \ + units/hibernate.target \ units/http-daemon.target \ units/poweroff.target \ units/reboot.target \ @@ -276,7 +280,9 @@ dist_systemunit_DATA = \ units/final.target \ units/umount.target \ units/sigpwr.target \ + units/sleep.target \ units/sockets.target \ + units/suspend.target \ units/swap.target \ units/systemd-initctl.socket \ units/systemd-shutdownd.socket \ @@ -318,12 +324,14 @@ nodist_systemunit_DATA = \ units/systemd-sysctl.service \ units/halt.service \ units/emergency.service \ + units/hibernate.service \ units/poweroff.service \ units/reboot.service \ units/kexec.service \ units/fsck@.service \ units/fsck-root.service \ units/rescue.service \ + units/suspend.service \ units/user@.service \ units/systemd-udev.service \ units/systemd-udev-trigger.service \ @@ -1062,6 +1070,13 @@ systemd_sysctl_SOURCES = \ systemd_sysctl_LDADD = \ libsystemd-shared.la +# ------------------------------------------------------------------------------ +systemd_sleep_SOURCES = \ + src/sleep/sleep.c + +systemd_sleep_LDADD = \ + libsystemd-shared.la + # ------------------------------------------------------------------------------ systemd_fsck_SOURCES = \ src/fsck/fsck.c @@ -3027,6 +3042,7 @@ systemd-install-data-hook: $(DESTDIR)$(prefix)/lib/sysctl.d \ $(DESTDIR)$(sysconfdir)/sysctl.d \ $(DESTDIR)$(systemshutdowndir) \ + $(DESTDIR)$(systemsleepdir) \ $(DESTDIR)$(systemgeneratordir) \ $(DESTDIR)$(usergeneratordir) $(MKDIR_P) -m 0755 \ diff --git a/TODO b/TODO index cd7aecf49e..aeba2b5165 100644 --- a/TODO +++ b/TODO @@ -53,9 +53,6 @@ Features: * ExecOnFailure=/usr/bin/foo -* logind: add "mode" flag to poweroff/suspend inhibit logic so that we can - support both "inhibit" and "delay" mode. - * fedora: make sshd and pam_loginuid work in nspawn containers * fix utmp for console logins in containers @@ -72,8 +69,6 @@ Features: * journald: allow forwarding of log data to specific TTY instea dof console -* suspend/hibernate/hybrid support, auto-suspend logic with idle hint - * add RequiredBy to [Install] * udev: move to LGPL diff --git a/man/systemctl.xml b/man/systemctl.xml index dd0ff786ec..9e113eb0fe 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -1149,6 +1149,16 @@ option) and will fail otherwise. + + suspend + + Suspend the system. + + + hibernate + + Hibernate the system. + diff --git a/man/systemd.special.xml b/man/systemd.special.xml index 39c3802210..984e998aba 100644 --- a/man/systemd.special.xml +++ b/man/systemd.special.xml @@ -58,6 +58,7 @@ exit.service, final.service, graphical.target, + hibernate.target, http-daemon.target, halt.target, kbrequest.target, @@ -80,7 +81,9 @@ runlevel5.target, shutdown.target, sigpwr.target, + sleep.target, sockets.target, + suspend.target, swap.target, sysinit.target, syslog.service, @@ -239,6 +242,15 @@ during installation. + + hibernate.target + + A special target unit + for hibernating the + system. This pulls in + sleep.target. + + http-daemon.target @@ -590,6 +602,19 @@ power fails. + + sleep.target + + A special target unit + that is pulled in by + suspend.target + and + hibernate.target + and may be used to hook units + into the sleep state + logic. + + sockets.target @@ -604,6 +629,15 @@ during installation. + + suspend.target + + A special target unit + for suspending the + system. This pulls in + sleep.target. + + swap.target diff --git a/src/core/shutdown.c b/src/core/shutdown.c index 2494bb86a1..a8dfe2614f 100644 --- a/src/core/shutdown.c +++ b/src/core/shutdown.c @@ -309,6 +309,7 @@ int main(int argc, char *argv[]) { unsigned retries; bool need_umount = true, need_swapoff = true, need_loop_detach = true, need_dm_detach = true; bool killed_everbody = false, in_container, use_watchdog = false; + char *arguments[3]; log_parse_environment(); log_set_target(LOG_TARGET_CONSOLE); /* syslog will die if not gone yet */ @@ -442,7 +443,10 @@ int main(int argc, char *argv[]) { if (retries >= FINALIZE_ATTEMPTS) log_error("Too many iterations, giving up."); - execute_directory(SYSTEM_SHUTDOWN_PATH, NULL, NULL); + arguments[0] = NULL; + arguments[1] = argv[1]; + arguments[2] = NULL; + execute_directory(SYSTEM_SHUTDOWN_PATH, NULL, arguments); /* If we are in a container, just exit, this will kill our * container for good. */ diff --git a/src/core/special.h b/src/core/special.h index 2db4711c2e..bc9b330ffb 100644 --- a/src/core/special.h +++ b/src/core/special.h @@ -36,6 +36,8 @@ #define SPECIAL_REBOOT_TARGET "reboot.target" #define SPECIAL_KEXEC_TARGET "kexec.target" #define SPECIAL_EXIT_TARGET "exit.target" +#define SPECIAL_SUSPEND_TARGET "suspend.target" +#define SPECIAL_HIBERNATE_TARGET "hibernate.target" /* Special boot targets */ #define SPECIAL_RESCUE_TARGET "rescue.target" diff --git a/src/shared/util.c b/src/shared/util.c index 8a0b2a1b46..d8d3f1a16d 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -4106,8 +4106,7 @@ void execute_directory(const char *directory, DIR *d, char *argv[]) { _argv[1] = NULL; argv = _argv; } else - if (!argv[0]) - argv[0] = path; + argv[0] = path; execv(path, argv); diff --git a/src/sleep/Makefile b/src/sleep/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/sleep/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c new file mode 100644 index 0000000000..7062dc242f --- /dev/null +++ b/src/sleep/sleep.c @@ -0,0 +1,83 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "log.h" +#include "util.h" + +int main(int argc, char *argv[]) { + const char *verb; + char* arguments[4]; + int r; + FILE *f; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + if (argc != 2) { + log_error("Invalid number of arguments."); + r = -EINVAL; + goto finish; + } + + if (streq(argv[1], "suspend")) + verb = "mem"; + else if (streq(argv[1], "hibernate")) + verb = "disk"; + else { + log_error("Unknown action '%s'.", argv[1]); + r = -EINVAL; + goto finish; + } + + f = fopen("/sys/power/state", "we"); + if (!f) { + log_error("Failed to open /sys/power/state: %m"); + r = -errno; + goto finish; + } + + arguments[0] = NULL; + arguments[1] = (char*) "pre"; + arguments[2] = argv[1]; + arguments[3] = NULL; + execute_directory(SYSTEMD_SLEEP_BINARY_PATH, NULL, arguments); + + fputs(verb, f); + fputc('\n', f); + fflush(f); + + r = ferror(f) ? -errno : 0; + + arguments[1] = (char*) "post"; + execute_directory(SYSTEMD_SLEEP_BINARY_PATH, NULL, arguments); + + fclose(f); + +finish: + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + +} diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index acede4e765..762b5be346 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -97,6 +97,8 @@ static enum action { ACTION_REBOOT, ACTION_KEXEC, ACTION_EXIT, + ACTION_SUSPEND, + ACTION_HIBERNATE, ACTION_RUNLEVEL2, ACTION_RUNLEVEL3, ACTION_RUNLEVEL4, @@ -1605,6 +1607,10 @@ static enum action verb_to_action(const char *verb) { return ACTION_DEFAULT; else if (streq(verb, "exit")) return ACTION_EXIT; + else if (streq(verb, "suspend")) + return ACTION_SUSPEND; + else if (streq(verb, "hibernate")) + return ACTION_HIBERNATE; else return ACTION_INVALID; } @@ -1623,7 +1629,9 @@ static int start_unit(DBusConnection *bus, char **args) { [ACTION_RESCUE] = SPECIAL_RESCUE_TARGET, [ACTION_EMERGENCY] = SPECIAL_EMERGENCY_TARGET, [ACTION_DEFAULT] = SPECIAL_DEFAULT_TARGET, - [ACTION_EXIT] = SPECIAL_EXIT_TARGET + [ACTION_EXIT] = SPECIAL_EXIT_TARGET, + [ACTION_SUSPEND] = SPECIAL_SUSPEND_TARGET, + [ACTION_HIBERNATE] = SPECIAL_HIBERNATE_TARGET }; int r, ret = 0; @@ -4201,7 +4209,9 @@ static int systemctl_help(void) { " poweroff Shut down and power-off the system\n" " reboot Shut down and reboot the system\n" " kexec Shut down and reboot the system with kexec\n" - " exit Ask for user instance termination\n", + " exit Request user instance exit\n" + " suspend Suspend the system\n" + " hibernate Hibernate the system\n", program_invocation_short_name); return 0; @@ -5135,6 +5145,8 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError { "poweroff", EQUAL, 1, start_special }, { "reboot", EQUAL, 1, start_special }, { "kexec", EQUAL, 1, start_special }, + { "suspend", EQUAL, 1, start_special }, + { "hibernate", EQUAL, 1, start_special }, { "default", EQUAL, 1, start_special }, { "rescue", EQUAL, 1, start_special }, { "emergency", EQUAL, 1, start_special }, diff --git a/units/.gitignore b/units/.gitignore index 68c174f1f5..dc5e1d47d3 100644 --- a/units/.gitignore +++ b/units/.gitignore @@ -1,3 +1,5 @@ +/hibernate.service +/suspend.service /console-getty.service /systemd-journald.service user@.service diff --git a/units/hibernate.service.in b/units/hibernate.service.in new file mode 100644 index 0000000000..6dba653d37 --- /dev/null +++ b/units/hibernate.service.in @@ -0,0 +1,16 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Hibernate +DefaultDependencies=no +Requires=sleep.target +After=sleep.target + +[Service] +Type=oneshot +ExecStart=@rootlibexecdir@/systemd-sleep hibernate diff --git a/units/hibernate.target b/units/hibernate.target new file mode 100644 index 0000000000..05238a7cbf --- /dev/null +++ b/units/hibernate.target @@ -0,0 +1,14 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See systemd.special(7) for details + +[Unit] +Description=Hibernate +DefaultDependencies=no +BindTo=hibernate.service +After=hibernate.service diff --git a/units/sleep.target b/units/sleep.target new file mode 100644 index 0000000000..9f4b247eb5 --- /dev/null +++ b/units/sleep.target @@ -0,0 +1,13 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See systemd.special(7) for details + +[Unit] +Description=Sleep +DefaultDependencies=no +RefuseManualStart=yes diff --git a/units/suspend.service.in b/units/suspend.service.in new file mode 100644 index 0000000000..3cf819e476 --- /dev/null +++ b/units/suspend.service.in @@ -0,0 +1,16 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Suspend +DefaultDependencies=no +Requires=sleep.target +After=sleep.target + +[Service] +Type=oneshot +ExecStart=@rootlibexecdir@/systemd-sleep suspend diff --git a/units/suspend.target b/units/suspend.target new file mode 100644 index 0000000000..3ddb44975d --- /dev/null +++ b/units/suspend.target @@ -0,0 +1,14 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See systemd.special(7) for details + +[Unit] +Description=Suspend +DefaultDependencies=no +BindTo=suspend.service +After=suspend.service -- cgit v1.2.3-54-g00ecf From 27b5482cc08b7fac1b6b15d980d42ae04f3ae1ca Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 16 May 2012 03:23:28 +0200 Subject: delta: add systemd-delta tool to find overriden configuration and unit files --- .gitignore | 1 + Makefile.am | 11 ++++++++++- TODO | 4 ++++ 3 files changed, 15 insertions(+), 1 deletion(-) (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index e61f1cdbad..01fba83563 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/systemd-delta /systemd-sleep /systemd-inhibit /systemd-remount-fs diff --git a/Makefile.am b/Makefile.am index 7c1b431093..7d6d744ee9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -214,7 +214,8 @@ bin_PROGRAMS = \ systemd-cgtop \ systemd-stdio-bridge \ systemd-nspawn \ - systemd-detect-virt + systemd-detect-virt \ + systemd-delta dist_bin_SCRIPTS = \ src/analyze/systemd-analyze @@ -452,6 +453,7 @@ MANPAGES = \ man/systemd.1 \ man/systemctl.1 \ man/systemd-cgls.1 \ + man/systemd-delta.1 \ man/systemd-cgtop.1 \ man/systemd-nspawn.1 \ man/systemd-tmpfiles.8 \ @@ -1114,6 +1116,13 @@ systemd_detect_virt_SOURCES = \ systemd_detect_virt_LDADD = \ libsystemd-shared.la +# ------------------------------------------------------------------------------ +systemd_delta_SOURCES = \ + src/delta/delta.c + +systemd_delta_LDADD = \ + libsystemd-shared.la + # ------------------------------------------------------------------------------ systemd_getty_generator_SOURCES = \ src/getty-generator/getty-generator.c diff --git a/TODO b/TODO index 5d18a64b17..d1756d89a6 100644 --- a/TODO +++ b/TODO @@ -23,6 +23,10 @@ Bugfixes: Features: +* (attempt to) make Debianites happy: + - implement .d/ auto includes for unit files + - add syntax to reset ExecStart= lists (and similar) + * actually queue the new default unit after switch-root * remove old root in switch-root logic -- cgit v1.2.3-54-g00ecf From 6b1dc2bd3cdb3bd932b0692be636ddd2879edb92 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 22 May 2012 19:23:33 +0200 Subject: mount: replace PID1 internal fstab parser with generator Bit by bit we should remove non-unit parsing from PID 1 and move into generators, to clean up our code base a bit and clearly separate parsers. --- .gitignore | 1 + Makefile.am | 12 +- TODO | 6 +- src/core/automount.c | 12 +- src/core/dbus-mount.c | 6 - src/core/dbus-swap.c | 2 - src/core/mount.c | 312 ++++++-------------- src/core/mount.h | 2 - src/core/swap.c | 104 +++---- src/core/swap.h | 4 - src/cryptsetup/cryptsetup-generator.c | 1 + src/fstab-generator/Makefile | 1 + src/fstab-generator/fstab-generator.c | 517 ++++++++++++++++++++++++++++++++++ 13 files changed, 657 insertions(+), 323 deletions(-) create mode 120000 src/fstab-generator/Makefile create mode 100644 src/fstab-generator/fstab-generator.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 01fba83563..e8d2feae4b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/systemd-fstab-generator /systemd-delta /systemd-sleep /systemd-inhibit diff --git a/Makefile.am b/Makefile.am index 4c1b295cc5..08f8bfc534 100644 --- a/Makefile.am +++ b/Makefile.am @@ -237,7 +237,8 @@ rootlibexec_PROGRAMS = \ systemd-sleep systemgenerator_PROGRAMS = \ - systemd-getty-generator + systemd-getty-generator \ + systemd-fstab-generator dist_bashcompletion_DATA = \ bash-completion/systemd-bash-completion.sh @@ -1133,6 +1134,15 @@ systemd_getty_generator_LDADD = \ libsystemd-label.la \ libsystemd-shared.la +# ------------------------------------------------------------------------------ +systemd_fstab_generator_SOURCES = \ + src/fstab-generator/fstab-generator.c \ + src/core/mount-setup.c + +systemd_fstab_generator_LDADD = \ + libsystemd-label.la \ + libsystemd-shared.la + # ------------------------------------------------------------------------------ systemd_rc_local_generator_SOURCES = \ src/rc-local-generator/rc-local-generator.c diff --git a/TODO b/TODO index da8bef6548..a967efa31b 100644 --- a/TODO +++ b/TODO @@ -23,6 +23,8 @@ Bugfixes: Features: +* replace sysvpath by a new more generic property + * make sure show-logs checks for utf8 validity, not ascii validity * add CapbilityBoundingSet to system.conf to set system-wide caps bounds, and same for TimerSlackNS @@ -31,8 +33,6 @@ Features: * readahead: when bumping /sys readahead variable save mtime and compare later to detect changes -* in rescue mode don't pull in sockets - * Document boot options such as forcefsck * (attempt to) make Debianites happy: @@ -51,8 +51,6 @@ Features: * move passno parsing to fstab generator -* actually queue the new default unit after switch-root - * improve !/proc/*/loginuid situation: make /proc/*/loginuid less dependent on CONFIG_AUDIT, or use the users cgroup information when /proc/*/loginuid is not available. diff --git a/src/core/automount.c b/src/core/automount.c index f190417fa3..e13259b388 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -156,14 +156,12 @@ static int automount_add_default_dependencies(Automount *a) { assert(a); - if (UNIT(a)->manager->running_as == MANAGER_SYSTEM) { - - if ((r = unit_add_dependency_by_name(UNIT(a), UNIT_BEFORE, SPECIAL_BASIC_TARGET, NULL, true)) < 0) - return r; + if (UNIT(a)->manager->running_as != MANAGER_SYSTEM) + return 0; - if ((r = unit_add_two_dependencies_by_name(UNIT(a), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true)) < 0) - return r; - } + r = unit_add_two_dependencies_by_name(UNIT(a), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true); + if (r < 0) + return r; return 0; } diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c index f6db4682af..26b04abe5d 100644 --- a/src/core/dbus-mount.c +++ b/src/core/dbus-mount.c @@ -80,8 +80,6 @@ static int bus_mount_append_what(DBusMessageIter *i, const char *property, void d = m->parameters_proc_self_mountinfo.what; else if (m->from_fragment && m->parameters_fragment.what) d = m->parameters_fragment.what; - else if (m->from_etc_fstab && m->parameters_etc_fstab.what) - d = m->parameters_etc_fstab.what; else d = ""; @@ -103,8 +101,6 @@ static int bus_mount_append_options(DBusMessageIter *i, const char *property, vo d = m->parameters_proc_self_mountinfo.options; else if (m->from_fragment && m->parameters_fragment.options) d = m->parameters_fragment.options; - else if (m->from_etc_fstab && m->parameters_etc_fstab.options) - d = m->parameters_etc_fstab.options; else d = ""; @@ -126,8 +122,6 @@ static int bus_mount_append_type(DBusMessageIter *i, const char *property, void d = m->parameters_proc_self_mountinfo.fstype; else if (m->from_fragment && m->parameters_fragment.fstype) d = m->parameters_fragment.fstype; - else if (m->from_etc_fstab && m->parameters_etc_fstab.fstype) - d = m->parameters_etc_fstab.fstype; else d = ""; diff --git a/src/core/dbus-swap.c b/src/core/dbus-swap.c index f327ca8b02..3ede0e606e 100644 --- a/src/core/dbus-swap.c +++ b/src/core/dbus-swap.c @@ -75,8 +75,6 @@ static int bus_swap_append_priority(DBusMessageIter *i, const char *property, vo j = s->parameters_proc_swaps.priority; else if (s->from_fragment) j = s->parameters_fragment.priority; - else if (s->from_etc_fstab) - j = s->parameters_etc_fstab.priority; else j = -1; diff --git a/src/core/mount.c b/src/core/mount.c index b597e9bfe2..e8f8856414 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -68,9 +68,13 @@ static void mount_init(Unit *u) { exec_context_init(&m->exec_context); - /* The stdio/kmsg bridge socket is on /, in order to avoid a - * dep loop, don't use kmsg logging for -.mount */ - if (!unit_has_name(u, "-.mount")) { + if (unit_has_name(u, "-.mount")) { + /* Don't allow start/stop for root directory */ + UNIT(m)->refuse_manual_start = true; + UNIT(m)->refuse_manual_stop = true; + } else { + /* The stdio/kmsg bridge socket is on /, in order to avoid a + * dep loop, don't use kmsg logging for -.mount */ m->exec_context.std_output = u->manager->default_std_output; m->exec_context.std_error = u->manager->default_std_error; } @@ -116,7 +120,6 @@ static void mount_done(Unit *u) { free(m->where); m->where = NULL; - mount_parameters_done(&m->parameters_etc_fstab); mount_parameters_done(&m->parameters_proc_self_mountinfo); mount_parameters_done(&m->parameters_fragment); @@ -129,13 +132,11 @@ static void mount_done(Unit *u) { unit_unwatch_timer(u, &m->timer_watch); } -static MountParameters* get_mount_parameters_configured(Mount *m) { +static MountParameters* get_mount_parameters_fragment(Mount *m) { assert(m); if (m->from_fragment) return &m->parameters_fragment; - else if (m->from_etc_fstab) - return &m->parameters_etc_fstab; return NULL; } @@ -146,7 +147,7 @@ static MountParameters* get_mount_parameters(Mount *m) { if (m->from_proc_self_mountinfo) return &m->parameters_proc_self_mountinfo; - return get_mount_parameters_configured(m); + return get_mount_parameters_fragment(m); } static int mount_add_mount_links(Mount *m) { @@ -156,7 +157,7 @@ static int mount_add_mount_links(Mount *m) { assert(m); - pm = get_mount_parameters_configured(m); + pm = get_mount_parameters_fragment(m); /* Adds in links to other mount points that might lie below or * above us in the hierarchy */ @@ -171,7 +172,7 @@ static int mount_add_mount_links(Mount *m) { if (UNIT(n)->load_state != UNIT_LOADED) continue; - pn = get_mount_parameters_configured(n); + pn = get_mount_parameters_fragment(n); if (path_startswith(m->where, n->where)) { @@ -336,160 +337,113 @@ static bool needs_quota(MountParameters *p) { mount_test_option(p->options, "grpjquota"); } -static int mount_add_fstab_links(Mount *m) { - const char *target, *after, *tu_wants = NULL; - MountParameters *p; - Unit *tu; - int r; - bool noauto, nofail, automount; - - assert(m); - - if (UNIT(m)->manager->running_as != MANAGER_SYSTEM) - return 0; - - if (!(p = get_mount_parameters_configured(m))) - return 0; - - if (p != &m->parameters_etc_fstab) - return 0; - - noauto = !!mount_test_option(p->options, "noauto"); - nofail = !!mount_test_option(p->options, "nofail"); - automount = - mount_test_option(p->options, "comment=systemd.automount") || - mount_test_option(p->options, "x-systemd.automount"); - - if (mount_is_network(p)) { - target = SPECIAL_REMOTE_FS_TARGET; - after = tu_wants = SPECIAL_REMOTE_FS_PRE_TARGET; - } else { - target = SPECIAL_LOCAL_FS_TARGET; - after = SPECIAL_LOCAL_FS_PRE_TARGET; - } - - r = manager_load_unit(UNIT(m)->manager, target, NULL, NULL, &tu); - if (r < 0) - return r; - - if (tu_wants) { - r = unit_add_dependency_by_name(tu, UNIT_WANTS, tu_wants, NULL, true); - if (r < 0) - return r; - } - - if (after) { - r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, NULL, true); - if (r < 0) - return r; - } - - if (automount) { - Unit *am; - - if ((r = unit_load_related_unit(UNIT(m), ".automount", &am)) < 0) - return r; - - /* If auto is configured as well also pull in the - * mount right-away, but don't rely on it. */ - if (!noauto) /* automount + auto */ - if ((r = unit_add_dependency(tu, UNIT_WANTS, UNIT(m), true)) < 0) - return r; - - /* Install automount unit */ - if (!nofail) /* automount + fail */ - return unit_add_two_dependencies(tu, UNIT_AFTER, UNIT_REQUIRES, am, true); - else /* automount + nofail */ - return unit_add_two_dependencies(tu, UNIT_AFTER, UNIT_WANTS, am, true); - - } else if (!noauto) { - - /* Automatically add mount points that aren't natively - * configured to local-fs.target */ - - if (!nofail) /* auto + fail */ - return unit_add_two_dependencies(tu, UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true); - else /* auto + nofail */ - return unit_add_dependency(tu, UNIT_WANTS, UNIT(m), true); - } - - return 0; -} - static int mount_add_device_links(Mount *m) { MountParameters *p; int r; assert(m); - if (!(p = get_mount_parameters_configured(m))) + p = get_mount_parameters_fragment(m); + if (!p) return 0; if (!p->what) return 0; if (!mount_is_bind(p) && - !path_equal(m->where, "/") && - p == &m->parameters_etc_fstab) { - bool nofail, noauto; - - noauto = !!mount_test_option(p->options, "noauto"); - nofail = !!mount_test_option(p->options, "nofail"); - - if ((r = unit_add_node_link(UNIT(m), p->what, - !noauto && nofail && - UNIT(m)->manager->running_as == MANAGER_SYSTEM)) < 0) + !path_equal(m->where, "/")) { + r = unit_add_node_link(UNIT(m), p->what, false); + if (r < 0) return r; } if (p->passno > 0 && !mount_is_bind(p) && - UNIT(m)->manager->running_as == MANAGER_SYSTEM && - !path_equal(m->where, "/")) { + !path_equal(m->where, "/") && + UNIT(m)->manager->running_as == MANAGER_SYSTEM) { char *name; Unit *fsck; /* Let's add in the fsck service */ /* aka SPECIAL_FSCK_SERVICE */ - if (!(name = unit_name_from_path_instance("fsck", p->what, ".service"))) + name = unit_name_from_path_instance("fsck", p->what, ".service"); + if (!name) return -ENOMEM; - if ((r = manager_load_unit_prepare(UNIT(m)->manager, name, NULL, NULL, &fsck)) < 0) { + r = manager_load_unit_prepare(UNIT(m)->manager, name, NULL, NULL, &fsck); + if (r < 0) { log_warning("Failed to prepare unit %s: %s", name, strerror(-r)); free(name); return r; } - free(name); SERVICE(fsck)->fsck_passno = p->passno; - if ((r = unit_add_two_dependencies(UNIT(m), UNIT_AFTER, UNIT_REQUIRES, fsck, true)) < 0) + r = unit_add_two_dependencies(UNIT(m), UNIT_AFTER, UNIT_REQUIRES, fsck, true); + if (r < 0) return r; } return 0; } +static int mount_add_quota_links(Mount *m) { + int r; + MountParameters *p; + + assert(m); + + if (UNIT(m)->manager->running_as != MANAGER_SYSTEM) + return 0; + + p = get_mount_parameters_fragment(m); + if (!p) + return 0; + + if (!needs_quota(p)) + return 0; + + r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTACHECK_SERVICE, NULL, true); + if (r < 0) + return r; + + r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTAON_SERVICE, NULL, true); + if (r < 0) + return r; + + return 0; +} + static int mount_add_default_dependencies(Mount *m) { int r; MountParameters *p; + const char *after; assert(m); if (UNIT(m)->manager->running_as != MANAGER_SYSTEM) return 0; - p = get_mount_parameters_configured(m); - if (p && needs_quota(p)) { - if ((r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTACHECK_SERVICE, NULL, true)) < 0 || - (r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTAON_SERVICE, NULL, true)) < 0) - return r; - } + p = get_mount_parameters_fragment(m); + if (!p) + return 0; - if (!path_equal(m->where, "/")) - if ((r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true)) < 0) - return r; + if (path_equal(m->where, "/")) + return 0; + + if (mount_is_network(p)) + after = SPECIAL_REMOTE_FS_PRE_TARGET; + else + after = SPECIAL_LOCAL_FS_PRE_TARGET; + + r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_WANTS, UNIT_AFTER, after, NULL, true); + if (r < 0) + return r; + + r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true); + if (r < 0) + return r; return 0; } @@ -505,7 +459,8 @@ static int mount_fix_timeouts(Mount *m) { assert(m); - if (!(p = get_mount_parameters_configured(m))) + p = get_mount_parameters_fragment(m); + if (!p) return 0; /* Allow configuration how long we wait for a device that @@ -550,7 +505,7 @@ static int mount_verify(Mount *m) { if (UNIT(m)->load_state != UNIT_LOADED) return 0; - if (!m->from_etc_fstab && !m->from_fragment && !m->from_proc_self_mountinfo) + if (!m->from_fragment && !m->from_proc_self_mountinfo) return -ENOENT; if (!(e = unit_name_from_path(m->where, ".mount"))) @@ -599,11 +554,6 @@ static int mount_load(Unit *u) { if (UNIT(m)->fragment_path) m->from_fragment = true; - else if (m->from_etc_fstab) - /* We always add several default dependencies to fstab mounts, - * but we do not want the implicit complementing of Wants= with After= - * in the target unit that this mount unit will be hooked into. */ - UNIT(m)->default_dependencies = false; if (!m->where) if (!(m->where = unit_name_to_path(u->id))) @@ -637,10 +587,11 @@ static int mount_load(Unit *u) { if ((r = mount_add_automount_links(m)) < 0) return r; - if ((r = mount_add_fstab_links(m)) < 0) + r = mount_add_quota_links(m); + if (r < 0) return r; - if (UNIT(m)->default_dependencies || m->from_etc_fstab) + if (UNIT(m)->default_dependencies) if ((r = mount_add_default_dependencies(m)) < 0) return r; @@ -775,7 +726,6 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) { "%sWhat: %s\n" "%sFile System Type: %s\n" "%sOptions: %s\n" - "%sFrom /etc/fstab: %s\n" "%sFrom /proc/self/mountinfo: %s\n" "%sFrom fragment: %s\n" "%sDirectoryMode: %04o\n", @@ -785,7 +735,6 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) { prefix, strna(p->what), prefix, strna(p->fstype), prefix, strna(p->options), - prefix, yes_no(m->from_etc_fstab), prefix, yes_no(m->from_proc_self_mountinfo), prefix, yes_no(m->from_fragment), prefix, m->directory_mode); @@ -969,7 +918,7 @@ static void mount_enter_mounting(Mount *m) { mkdir_p(m->where, m->directory_mode); /* Create the source directory for bind-mounts if needed */ - p = get_mount_parameters_configured(m); + p = get_mount_parameters_fragment(m); if (p && mount_is_bind(p)) mkdir_p(p->what, m->directory_mode); @@ -982,12 +931,6 @@ static void mount_enter_mounting(Mount *m) { "-t", m->parameters_fragment.fstype ? m->parameters_fragment.fstype : "auto", m->parameters_fragment.options ? "-o" : NULL, m->parameters_fragment.options, NULL); - else if (m->from_etc_fstab) - r = exec_command_set( - m->control_command, - "/bin/mount", - m->where, - NULL); else r = -ENOENT; @@ -1046,14 +989,7 @@ static void mount_enter_remounting(Mount *m) { NULL); free(buf); - } else if (m->from_etc_fstab) - r = exec_command_set( - m->control_command, - "/bin/mount", - m->where, - "-o", "remount", - NULL); - else + } else r = -ENOENT; if (r < 0) @@ -1232,7 +1168,7 @@ static bool mount_check_gc(Unit *u) { assert(m); - return m->from_etc_fstab || m->from_proc_self_mountinfo; + return m->from_proc_self_mountinfo; } static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) { @@ -1416,7 +1352,6 @@ static int mount_add_one( const char *options, const char *fstype, int passno, - bool from_proc_self_mountinfo, bool set_flags) { int r; Unit *u; @@ -1430,8 +1365,6 @@ static int mount_add_one( assert(options); assert(fstype); - assert(!set_flags || from_proc_self_mountinfo); - /* Ignore API mount points. They should never be referenced in * dependencies ever. */ if (mount_point_is_api(where) || mount_point_ignore(where)) @@ -1483,21 +1416,15 @@ static int mount_add_one( goto fail; } - if (from_proc_self_mountinfo) { - p = &MOUNT(u)->parameters_proc_self_mountinfo; - - if (set_flags) { - MOUNT(u)->is_mounted = true; - MOUNT(u)->just_mounted = !MOUNT(u)->from_proc_self_mountinfo; - MOUNT(u)->just_changed = !streq_ptr(p->options, o); - } - - MOUNT(u)->from_proc_self_mountinfo = true; - } else { - p = &MOUNT(u)->parameters_etc_fstab; - MOUNT(u)->from_etc_fstab = true; + p = &MOUNT(u)->parameters_proc_self_mountinfo; + if (set_flags) { + MOUNT(u)->is_mounted = true; + MOUNT(u)->just_mounted = !MOUNT(u)->from_proc_self_mountinfo; + MOUNT(u)->just_changed = !streq_ptr(p->options, o); } + MOUNT(u)->from_proc_self_mountinfo = true; + free(p->what); p->what = w; @@ -1545,68 +1472,6 @@ static int mount_find_pri(char *options) { return (int) r; } -static int mount_load_etc_fstab(Manager *m) { - FILE *f; - int r = 0; - struct mntent* me; - - assert(m); - - errno = 0; - f = setmntent("/etc/fstab", "r"); - if (!f) - return errno == ENOENT ? 0 : -errno; - - while ((me = getmntent(f))) { - char *where, *what; - int k; - - if (!(what = fstab_node_to_udev_node(me->mnt_fsname))) { - r = -ENOMEM; - goto finish; - } - - if (!(where = strdup(me->mnt_dir))) { - free(what); - r = -ENOMEM; - goto finish; - } - - if (what[0] == '/') - path_kill_slashes(what); - - if (where[0] == '/') - path_kill_slashes(where); - - if (streq(me->mnt_type, "swap")) { - int pri; - - if ((pri = mount_find_pri(me->mnt_opts)) < 0) - k = pri; - else - k = swap_add_one(m, - what, - NULL, - pri, - !!mount_test_option(me->mnt_opts, "noauto"), - !!mount_test_option(me->mnt_opts, "nofail"), - false); - } else - k = mount_add_one(m, what, where, me->mnt_opts, me->mnt_type, me->mnt_passno, false, false); - - free(what); - free(where); - - if (k < 0) - r = k; - } - -finish: - - endmntent(f); - return r; -} - static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) { int r = 0; unsigned i; @@ -1659,7 +1524,7 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) { goto finish; } - if ((k = mount_add_one(m, d, p, o, fstype, 0, true, set_flags)) < 0) + if ((k = mount_add_one(m, d, p, o, fstype, 0, set_flags)) < 0) r = k; clean_up: @@ -1715,9 +1580,6 @@ static int mount_enumerate(Manager *m) { return -errno; } - if ((r = mount_load_etc_fstab(m)) < 0) - goto fail; - if ((r = mount_load_proc_self_mountinfo(m, false)) < 0) goto fail; diff --git a/src/core/mount.h b/src/core/mount.h index 3153a2a99d..ad9efc34d5 100644 --- a/src/core/mount.h +++ b/src/core/mount.h @@ -75,11 +75,9 @@ struct Mount { char *where; - MountParameters parameters_etc_fstab; MountParameters parameters_proc_self_mountinfo; MountParameters parameters_fragment; - bool from_etc_fstab:1; bool from_proc_self_mountinfo:1; bool from_fragment:1; diff --git a/src/core/swap.c b/src/core/swap.c index ba4413bd3c..f677d65bd2 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -87,7 +87,7 @@ static void swap_init(Unit *u) { s->exec_context.std_output = u->manager->default_std_output; s->exec_context.std_error = u->manager->default_std_error; - s->parameters_etc_fstab.priority = s->parameters_proc_swaps.priority = s->parameters_fragment.priority = -1; + s->parameters_proc_swaps.priority = s->parameters_fragment.priority = -1; s->timer_watch.type = WATCH_INVALID; @@ -116,9 +116,8 @@ static void swap_done(Unit *u) { free(s->what); s->what = NULL; - free(s->parameters_etc_fstab.what); free(s->parameters_fragment.what); - s->parameters_etc_fstab.what = s->parameters_fragment.what = NULL; + s->parameters_fragment.what = NULL; exec_context_done(&s->exec_context); exec_command_done_array(s->exec_command, _SWAP_EXEC_COMMAND_MAX); @@ -166,28 +165,16 @@ static int swap_add_mount_links(Swap *s) { static int swap_add_target_links(Swap *s) { Unit *tu; - SwapParameters *p; int r; assert(s); - if (s->from_fragment) - p = &s->parameters_fragment; - else if (s->from_etc_fstab) - p = &s->parameters_etc_fstab; - else + if (!s->from_fragment) return 0; if ((r = manager_load_unit(UNIT(s)->manager, SPECIAL_SWAP_TARGET, NULL, NULL, &tu)) < 0) return r; - if (!p->noauto && - !p->nofail && - s->from_etc_fstab && - UNIT(s)->manager->running_as == MANAGER_SYSTEM) - if ((r = unit_add_dependency(tu, UNIT_WANTS, UNIT(s), true)) < 0) - return r; - return unit_add_dependency(UNIT(s), UNIT_BEFORE, tu, true); } @@ -201,8 +188,6 @@ static int swap_add_device_links(Swap *s) { if (s->from_fragment) p = &s->parameters_fragment; - else if (s->from_etc_fstab) - p = &s->parameters_etc_fstab; else return 0; @@ -222,11 +207,12 @@ static int swap_add_default_dependencies(Swap *s) { assert(s); - if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) { + if (UNIT(s)->manager->running_as != MANAGER_SYSTEM) + return 0; - if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true)) < 0) - return r; - } + r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true); + if (r < 0) + return r; return 0; } @@ -278,8 +264,6 @@ static int swap_load(Unit *u) { if (!s->what) { if (s->parameters_fragment.what) s->what = strdup(s->parameters_fragment.what); - else if (s->parameters_etc_fstab.what) - s->what = strdup(s->parameters_etc_fstab.what); else if (s->parameters_proc_swaps.what) s->what = strdup(s->parameters_proc_swaps.what); else @@ -315,7 +299,7 @@ static int swap_load(Unit *u) { return swap_verify(s); } -int swap_add_one( +static int swap_add_one( Manager *m, const char *what, const char *what_proc_swaps, @@ -329,9 +313,11 @@ int swap_add_one( bool delete = false; int r; SwapParameters *p; + Swap *first; assert(m); assert(what); + assert(what_proc_swaps); e = unit_name_from_path(what, ".swap"); if (!e) @@ -339,8 +325,7 @@ int swap_add_one( u = manager_get_unit(m, e); - if (what_proc_swaps && - u && + if (u && SWAP(u)->from_proc_swaps && !path_equal(SWAP(u)->parameters_proc_swaps.what, what_proc_swaps)) return -EEXIST; @@ -368,54 +353,37 @@ int swap_add_one( } else delete = false; - if (what_proc_swaps) { - Swap *first; + p = &SWAP(u)->parameters_proc_swaps; - p = &SWAP(u)->parameters_proc_swaps; + if (!p->what) { + if (!(wp = strdup(what_proc_swaps))) { + r = -ENOMEM; + goto fail; + } - if (!p->what) { - if (!(wp = strdup(what_proc_swaps))) { + if (!m->swaps_by_proc_swaps) + if (!(m->swaps_by_proc_swaps = hashmap_new(string_hash_func, string_compare_func))) { r = -ENOMEM; goto fail; } - if (!m->swaps_by_proc_swaps) - if (!(m->swaps_by_proc_swaps = hashmap_new(string_hash_func, string_compare_func))) { - r = -ENOMEM; - goto fail; - } - - free(p->what); - p->what = wp; - - first = hashmap_get(m->swaps_by_proc_swaps, wp); - LIST_PREPEND(Swap, same_proc_swaps, first, SWAP(u)); - - if ((r = hashmap_replace(m->swaps_by_proc_swaps, wp, first)) < 0) - goto fail; - } - - if (set_flags) { - SWAP(u)->is_active = true; - SWAP(u)->just_activated = !SWAP(u)->from_proc_swaps; - } - - SWAP(u)->from_proc_swaps = true; + free(p->what); + p->what = wp; - } else { - p = &SWAP(u)->parameters_etc_fstab; + first = hashmap_get(m->swaps_by_proc_swaps, wp); + LIST_PREPEND(Swap, same_proc_swaps, first, SWAP(u)); - if (!(wp = strdup(what))) { - r = -ENOMEM; + if ((r = hashmap_replace(m->swaps_by_proc_swaps, wp, first)) < 0) goto fail; - } - - free(p->what); - p->what = wp; + } - SWAP(u)->from_etc_fstab = true; + if (set_flags) { + SWAP(u)->is_active = true; + SWAP(u)->just_activated = !SWAP(u)->from_proc_swaps; } + SWAP(u)->from_proc_swaps = true; + p->priority = priority; p->noauto = noauto; p->nofail = nofail; @@ -567,8 +535,6 @@ static void swap_dump(Unit *u, FILE *f, const char *prefix) { p = &s->parameters_proc_swaps; else if (s->from_fragment) p = &s->parameters_fragment; - else - p = &s->parameters_etc_fstab; fprintf(f, "%sSwap State: %s\n" @@ -577,7 +543,6 @@ static void swap_dump(Unit *u, FILE *f, const char *prefix) { "%sPriority: %i\n" "%sNoAuto: %s\n" "%sNoFail: %s\n" - "%sFrom /etc/fstab: %s\n" "%sFrom /proc/swaps: %s\n" "%sFrom fragment: %s\n", prefix, swap_state_to_string(s->state), @@ -586,7 +551,6 @@ static void swap_dump(Unit *u, FILE *f, const char *prefix) { prefix, p->priority, prefix, yes_no(p->noauto), prefix, yes_no(p->nofail), - prefix, yes_no(s->from_etc_fstab), prefix, yes_no(s->from_proc_swaps), prefix, yes_no(s->from_fragment)); @@ -732,8 +696,6 @@ static void swap_enter_activating(Swap *s) { if (s->from_fragment) priority = s->parameters_fragment.priority; - else if (s->from_etc_fstab) - priority = s->parameters_etc_fstab.priority; else priority = -1; @@ -928,7 +890,7 @@ static bool swap_check_gc(Unit *u) { assert(s); - return s->from_etc_fstab || s->from_proc_swaps; + return s->from_proc_swaps; } static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) { @@ -1269,8 +1231,6 @@ static int swap_enumerate(Manager *m) { return -errno; } - /* We rely on mount.c to load /etc/fstab for us */ - if ((r = swap_load_proc_swaps(m, false)) < 0) swap_shutdown(m); diff --git a/src/core/swap.h b/src/core/swap.h index 2a6fceece2..ff12169b68 100644 --- a/src/core/swap.h +++ b/src/core/swap.h @@ -71,11 +71,9 @@ struct Swap { char *what; - SwapParameters parameters_etc_fstab; SwapParameters parameters_proc_swaps; SwapParameters parameters_fragment; - bool from_etc_fstab:1; bool from_proc_swaps:1; bool from_fragment:1; @@ -108,8 +106,6 @@ struct Swap { extern const UnitVTable swap_vtable; -int swap_add_one(Manager *m, const char *what, const char *what_proc_swaps, int prio, bool no_auto, bool no_fail, bool set_flags); - int swap_add_one_mount_link(Swap *s, Mount *m); int swap_dispatch_reload(Manager *m); diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c index edc28998b2..c0b1cf0939 100644 --- a/src/cryptsetup/cryptsetup-generator.c +++ b/src/cryptsetup/cryptsetup-generator.c @@ -112,6 +112,7 @@ static int create_disk( } fprintf(f, + "# Automatically generated by systemd-cryptsetup-generator\n\n" "[Unit]\n" "Description=Cryptography Setup for %%I\n" "Conflicts=umount.target\n" diff --git a/src/fstab-generator/Makefile b/src/fstab-generator/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/fstab-generator/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c new file mode 100644 index 0000000000..39af18a9c8 --- /dev/null +++ b/src/fstab-generator/fstab-generator.c @@ -0,0 +1,517 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "log.h" +#include "util.h" +#include "unit-name.h" +#include "path-util.h" +#include "mount-setup.h" +#include "special.h" +#include "mkdir.h" + +static const char *arg_dest = "/tmp"; + +static int device_name(const char *path, char **unit) { + char *p; + + assert(path); + + if (!is_device_path(path)) + return 0; + + p = unit_name_from_path(path, ".device"); + if (!p) + return -ENOMEM; + + *unit = p; + return 1; +} + +static int mount_find_pri(struct mntent *me, int *ret) { + char *end, *pri; + unsigned long r; + + assert(me); + assert(ret); + + pri = hasmntopt(me, "pri"); + if (!pri) + return 0; + + pri += 4; + + errno = 0; + r = strtoul(pri, &end, 10); + if (errno != 0) + return -errno; + + if (end == pri || (*end != ',' && *end != 0)) + return -EINVAL; + + *ret = (int) r; + return 1; +} + +static int add_swap(const char *what, struct mntent *me) { + char *name = NULL, *unit = NULL, *lnk = NULL, *device = NULL; + FILE *f = NULL; + bool noauto, nofail; + int r, pri = -1; + + assert(what); + assert(me); + + r = mount_find_pri(me, &pri); + if (r < 0) { + log_error("Failed to parse priority"); + return pri; + } + + noauto = !!hasmntopt(me, "noauto"); + nofail = !!hasmntopt(me, "nofail"); + + name = unit_name_from_path(what, ".swap"); + if (!name) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + unit = join(arg_dest, "/", name, NULL); + if (!unit) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + f = fopen(unit, "wxe"); + if (!f) { + r = -errno; + log_error("Failed to create unit file: %m"); + goto finish; + } + + fputs("# Automatically generated by systemd-fstab-generator\n\n" + "[Unit]\n" + "DefaultDependencies=no\n" + "Conflicts=" SPECIAL_UMOUNT_TARGET "\n" + "Before=" SPECIAL_UMOUNT_TARGET "\n", f); + + if (!noauto && !nofail) + fputs("Before=" SPECIAL_SWAP_TARGET "\n", f); + + fprintf(f, + "\n" + "[Swap]\n" + "What=%s\n", + what); + + if (pri >= 0) + fprintf(f, + "Priority=%i\n", + pri); + + fflush(f); + if (ferror(f)) { + log_error("Failed to write unit file: %m"); + r = -errno; + goto finish; + } + + if (!noauto) { + lnk = join(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL); + if (!lnk) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + mkdir_parents(lnk, 0755); + if (symlink(unit, lnk) < 0) { + log_error("Failed to create symlink: %m"); + r = -errno; + goto finish; + } + + r = device_name(what, &device); + if (r < 0) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + if (r > 0) { + free(lnk); + lnk = join(arg_dest, "/", device, ".wants/", name, NULL); + if (!lnk) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + mkdir_parents(lnk, 0755); + if (symlink(unit, lnk) < 0) { + log_error("Failed to create symlink: %m"); + r = -errno; + goto finish; + } + } + } + + r = 0; +finish: + if (f) + fclose(f); + + free(unit); + free(lnk); + free(name); + free(device); + + return r; +} + +static bool mount_is_bind(struct mntent *me) { + assert(me); + + return + hasmntopt(me, "bind") || + streq(me->mnt_opts, "bind"); +} + +static bool mount_is_network(struct mntent *me) { + assert(me); + + return + hasmntopt(me, "_netdev") || + fstype_is_network(me->mnt_type); +} + +static int add_mount(const char *what, const char *where, struct mntent *me) { + char *name = NULL, *unit = NULL, *lnk = NULL, *device = NULL, *automount_name = NULL, *automount_unit = NULL; + FILE *f = NULL; + bool noauto, nofail, automount, isbind, isnetwork; + int r; + const char *post, *pre; + + assert(what); + assert(where); + assert(me); + + if (streq(me->mnt_type, "autofs")) + return 0; + + if (!is_path(where)) { + log_warning("Mount point %s is not a valid path, ignoring.", where); + return 0; + } + + if (mount_point_is_api(where) || + mount_point_ignore(where)) + return 0; + + isnetwork = mount_is_network(me); + isbind = mount_is_bind(me); + + noauto = !!hasmntopt(me, "noauto"); + nofail = !!hasmntopt(me, "nofail"); + automount = + hasmntopt(me, "comment=systemd.automount") || + hasmntopt(me, "x-systemd.automount"); + + if (isnetwork) { + post = SPECIAL_REMOTE_FS_TARGET; + pre = SPECIAL_REMOTE_FS_PRE_TARGET; + } else { + post = SPECIAL_LOCAL_FS_TARGET; + pre = SPECIAL_LOCAL_FS_PRE_TARGET; + } + + name = unit_name_from_path(where, ".mount"); + if (!name) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + unit = join(arg_dest, "/", name, NULL); + if (!unit) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + f = fopen(unit, "wxe"); + if (!f) { + r = -errno; + log_error("Failed to create unit file: %m"); + goto finish; + } + + fputs("# Automatically generated by systemd-fstab-generator\n\n" + "[Unit]\n" + "DefaultDependencies=no\n", f); + + if (!path_equal(where, "/")) + fprintf(f, + "After=%s\n" + "Wants=%s\n" + "Conflicts=" SPECIAL_UMOUNT_TARGET "\n" + "Before=" SPECIAL_UMOUNT_TARGET "\n", + pre, + pre); + + + if (!noauto && !nofail && !automount) + fprintf(f, + "Before=%s\n", + post); + + fprintf(f, + "\n" + "[Mount]\n" + "What=%s\n" + "Where=%s\n" + "Type=%s\n" + "FsckPassNo=%i\n", + what, + where, + me->mnt_type, + me->mnt_passno); + + if (!isempty(me->mnt_opts) && + !streq(me->mnt_opts, "defaults")) + fprintf(f, + "Options=%s\n", + me->mnt_opts); + + fflush(f); + if (ferror(f)) { + log_error("Failed to write unit file: %m"); + r = -errno; + goto finish; + } + + if (!noauto) { + lnk = join(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL); + if (!lnk) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + mkdir_parents(lnk, 0755); + if (symlink(unit, lnk) < 0) { + log_error("Failed to create symlink: %m"); + r = -errno; + goto finish; + } + + if (!isbind && + !path_equal(where, "/")) { + + r = device_name(what, &device); + if (r < 0) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + if (r > 0) { + free(lnk); + lnk = join(arg_dest, "/", device, ".wants/", name, NULL); + if (!lnk) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + mkdir_parents(lnk, 0755); + if (symlink(unit, lnk) < 0) { + log_error("Failed to creat symlink: %m"); + r = -errno; + goto finish; + } + } + } + } + + if (automount && !path_equal(where, "/")) { + automount_name = unit_name_from_path(where, ".automount"); + if (!name) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + automount_unit = join(arg_dest, "/", automount_name, NULL); + if (!automount_unit) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + fclose(f); + f = fopen(automount_unit, "wxe"); + if (!f) { + r = -errno; + log_error("Failed to create unit file: %m"); + goto finish; + } + + fprintf(f, + "# Automatically generated by systemd-fstab-generator\n\n" + "[Unit]\n" + "DefaultDependencies=no\n" + "Conflicts=" SPECIAL_UMOUNT_TARGET "\n" + "Before=" SPECIAL_UMOUNT_TARGET " %s\n" + "\n" + "[Automount]\n" + "Where=%s\n", + post, + where); + + fflush(f); + if (ferror(f)) { + log_error("Failed to write unit file: %m"); + r = -errno; + goto finish; + } + + free(lnk); + lnk = join(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL); + if (!lnk) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + mkdir_parents(lnk, 0755); + if (symlink(automount_unit, lnk) < 0) { + log_error("Failed to create symlink: %m"); + r = -errno; + goto finish; + } + } + + r = 0; +finish: + if (f) + fclose(f); + + free(unit); + free(lnk); + free(name); + free(device); + free(automount_name); + free(automount_unit); + + return r; +} + +static int parse_fstab(void) { + FILE *f; + int r = 0; + struct mntent *me; + + errno = 0; + f = setmntent("/etc/fstab", "r"); + if (!f) { + if (errno == ENOENT) + return 0; + + log_error("Failed to open /etc/fstab: %m"); + return -errno; + } + + while ((me = getmntent(f))) { + char *where, *what; + int k; + + what = fstab_node_to_udev_node(me->mnt_fsname); + if (!what) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + where = strdup(me->mnt_dir); + if (!where) { + log_error("Out of memory"); + free(what); + r = -ENOMEM; + goto finish; + } + + if (is_path(what)) + path_kill_slashes(what); + + if (is_path(where)) + path_kill_slashes(where); + + log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type); + + if (streq(me->mnt_type, "swap")) + k = add_swap(what, me); + else + k = add_mount(what, where, me); + + free(what); + free(where); + + if (k < 0) + r = k; + } + +finish: + endmntent(f); + return r; +} + +int main(int argc, char *argv[]) { + int r; + + if (argc > 2) { + log_error("This program takes one or no arguments."); + return EXIT_FAILURE; + } + + if (argc > 1) + arg_dest = argv[1]; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + log_set_max_level(LOG_DEBUG); + + umask(0022); + + r = parse_fstab(); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} -- cgit v1.2.3-54-g00ecf From d360705f0f1262d49cccb6507abeafb7cfb5bbe0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 22 May 2012 19:48:51 +0200 Subject: system-update: add system update generator http://freedesktop.org/wiki/Software/systemd/SystemUpdates --- .gitignore | 1 + Makefile.am | 14 +++- src/system-update-generator/Makefile | 1 + .../system-update-generator.c | 81 ++++++++++++++++++++++ units/system-update.target | 16 +++++ 5 files changed, 111 insertions(+), 2 deletions(-) create mode 120000 src/system-update-generator/Makefile create mode 100644 src/system-update-generator/system-update-generator.c create mode 100644 units/system-update.target (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index e8d2feae4b..e97b1c7d93 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/systemd-system-update-generator /systemd-fstab-generator /systemd-delta /systemd-sleep diff --git a/Makefile.am b/Makefile.am index 08f8bfc534..d5cf3095f4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -238,7 +238,8 @@ rootlibexec_PROGRAMS = \ systemgenerator_PROGRAMS = \ systemd-getty-generator \ - systemd-fstab-generator + systemd-fstab-generator \ + systemd-system-update-generator dist_bashcompletion_DATA = \ bash-completion/systemd-bash-completion.sh @@ -306,7 +307,8 @@ dist_systemunit_DATA = \ units/syslog.target \ units/systemd-udev-control.socket \ units/systemd-udev-kernel.socket \ - units/systemd-timedated-ntp.target + units/systemd-timedated-ntp.target \ + units/system-update.target nodist_systemunit_DATA = \ units/getty@.service \ @@ -1143,6 +1145,14 @@ systemd_fstab_generator_LDADD = \ libsystemd-label.la \ libsystemd-shared.la +# ------------------------------------------------------------------------------ +systemd_system_update_generator_SOURCES = \ + src/system-update-generator/system-update-generator.c + +systemd_system_update_generator_LDADD = \ + libsystemd-label.la \ + libsystemd-shared.la + # ------------------------------------------------------------------------------ systemd_rc_local_generator_SOURCES = \ src/rc-local-generator/rc-local-generator.c diff --git a/src/system-update-generator/Makefile b/src/system-update-generator/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/system-update-generator/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/system-update-generator/system-update-generator.c b/src/system-update-generator/system-update-generator.c new file mode 100644 index 0000000000..0cfccfb494 --- /dev/null +++ b/src/system-update-generator/system-update-generator.c @@ -0,0 +1,81 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "log.h" +#include "util.h" +#include "unit-name.h" +#include "path-util.h" + +static const char *arg_dest = "/tmp"; + +static int generate_symlink(void) { + struct stat st; + char *p; + + if (lstat("/system-update", &st) < 0) { + if (errno == ENOENT) + return 0; + + log_error("Failed to check for system update: %m"); + return -EINVAL; + } + + p = strappend(arg_dest, "/default.target"); + if (!p) { + log_error("Out of memory."); + return -ENOMEM; + } + + if (symlink(SYSTEM_DATA_UNIT_PATH "/system-update.target", p) < 0) { + free(p); + log_error("Failed to create symlink: %m"); + return -errno; + } + + free(p); + + return 0; +} + +int main(int argc, char *argv[]) { + int r; + + if (argc > 2) { + log_error("This program takes one or no arguments."); + return EXIT_FAILURE; + } + + if (argc > 1) + arg_dest = argv[1]; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + r = generate_symlink(); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/units/system-update.target b/units/system-update.target new file mode 100644 index 0000000000..5f638792d1 --- /dev/null +++ b/units/system-update.target @@ -0,0 +1,16 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=System Update +Documentation=http://freedesktop.org/wiki/Software/systemd/SystemUpdates +Documentation=man:systemd.special(7) +Requires=sysinit.target +Conflicts=shutdown.target +After=sysinit.target +Before=shutdown.target +AllowIsolate=yes -- cgit v1.2.3-54-g00ecf From eb125fb69376d86785066e1f42d0d231ad7dd9ff Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Sun, 10 Jun 2012 18:28:53 +0200 Subject: update .gitignore --- .gitignore | 1 + units/.gitignore | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index e97b1c7d93..abb7255c49 100644 --- a/.gitignore +++ b/.gitignore @@ -123,3 +123,4 @@ stamp-* /v4l_id /test-libudev /test-udev +/systemd-readahead-analyze diff --git a/units/.gitignore b/units/.gitignore index 626b12a8d6..bcbff337dd 100644 --- a/units/.gitignore +++ b/units/.gitignore @@ -46,4 +46,4 @@ emergency.service /systemd-udev-settle.service /systemd-udev-trigger.service /systemd-udev.service -/units/systemd-debug-shell.service +/systemd-debug-shell.service -- cgit v1.2.3-54-g00ecf From 87ce22cc0d097d9cd0297d0141eadba6c573c299 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 21 Jun 2012 23:53:20 +0200 Subject: readahead: merge three binaries into one since the binaries share much of the same code and we better load only one binary instead of two from disk at early boot let's merge the three readahead binaries into one. This also allows us to drop a lot of duplicated code. --- .gitignore | 4 +- Makefile.am | 25 +---- TODO | 2 - src/readahead/readahead-analyze.c | 25 ++--- src/readahead/readahead-collect.c | 118 ++------------------- src/readahead/readahead-common.h | 9 ++ src/readahead/readahead-replay.c | 94 ++--------------- src/readahead/readahead.c | 158 +++++++++++++++++++++++++++++ units/systemd-readahead-collect.service.in | 2 +- units/systemd-readahead-replay.service.in | 2 +- 10 files changed, 201 insertions(+), 238 deletions(-) create mode 100644 src/readahead/readahead.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index abb7255c49..7d04930a25 100644 --- a/.gitignore +++ b/.gitignore @@ -46,8 +46,7 @@ /systemd-user-sessions /systemd-shutdown /systemd-tmpfiles -/systemd-readahead-collect -/systemd-readahead-replay +/systemd-readahead /systemd-reply-password /systemd-gnome-ask-password-agent /systemd-ask-password @@ -123,4 +122,3 @@ stamp-* /v4l_id /test-libudev /test-udev -/systemd-readahead-analyze diff --git a/Makefile.am b/Makefile.am index 4616b30f7b..03e28e8c34 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2377,39 +2377,24 @@ endif # ------------------------------------------------------------------------------ if ENABLE_READAHEAD -systemd_readahead_collect_SOURCES = \ +systemd_readahead_SOURCES = \ + src/readahead/readahead.c \ src/readahead/readahead-collect.c \ - src/readahead/readahead-common.c \ - src/readahead/readahead-common.h - -systemd_readahead_collect_LDADD = \ - libsystemd-shared.la \ - libsystemd-daemon.la \ - libudev.la - -systemd_readahead_replay_SOURCES = \ src/readahead/readahead-replay.c \ + src/readahead/readahead-analyze.c \ src/readahead/readahead-common.c \ src/readahead/readahead-common.h -systemd_readahead_replay_LDADD = \ +systemd_readahead_LDADD = \ libsystemd-shared.la \ libsystemd-daemon.la \ libudev.la -systemd_readahead_analyze_SOURCES = \ - src/readahead/readahead-analyze.c \ - src/readahead/readahead-common.h - pkginclude_HEADERS += \ src/systemd/sd-readahead.h rootlibexec_PROGRAMS += \ - systemd-readahead-collect \ - systemd-readahead-replay - -bin_PROGRAMS += \ - systemd-readahead-analyze + systemd-readahead dist_systemunit_DATA += \ units/systemd-readahead-drop.service \ diff --git a/TODO b/TODO index 630721c3f2..908dadb974 100644 --- a/TODO +++ b/TODO @@ -35,8 +35,6 @@ Features: * supprto rd.xxx wherever else makes sense -* readahead: merge the three tools into one binary - * systemctl: when stopping a service which has triggres and warning about it actually check the TriggeredBy= deps fields * journal: hook up with EFI firmware log, new kmsg logic diff --git a/src/readahead/readahead-analyze.c b/src/readahead/readahead-analyze.c index 6ee355146a..5a5b9176d0 100644 --- a/src/readahead/readahead-analyze.c +++ b/src/readahead/readahead-analyze.c @@ -32,8 +32,7 @@ #include "readahead-common.h" - -int main(int argc, char *argv[]) +int main_analyze(const char *pack_path) { char line[1024]; char path[PATH_MAX]; @@ -48,30 +47,28 @@ int main(int argc, char *argv[]) struct stat st; int pagesize = getpagesize(); - if (argc != 2) - snprintf(path, PATH_MAX, "/.readahead"); - else - snprintf(path, PATH_MAX, "%s", argv[1]); + if (!pack_path) + pack_path = "/.readahead"; - pack = fopen(path, "r"); + pack = fopen(pack_path, "r"); if (!pack) { fprintf(stderr, "Pack file missing\n"); - exit(EXIT_FAILURE); + return EXIT_FAILURE; } if (!(fgets(line, sizeof(line), pack))) { fprintf(stderr, "Pack file corrupt\n"); - exit(EXIT_FAILURE); + return EXIT_FAILURE; } if (!strstr(line, READAHEAD_PACK_FILE_VERSION)) { fprintf(stderr, "Pack file version incompatible with this parser\n"); - exit(EXIT_FAILURE); + return EXIT_FAILURE; } if ((a = getc(pack)) == EOF) { fprintf(stderr, "Pack file corrupt\n"); - exit(EXIT_FAILURE); + return EXIT_FAILURE; } fprintf(stdout, " pct sections size: path\n"); @@ -88,14 +85,14 @@ int main(int argc, char *argv[]) if (fread(&inode, sizeof(inode), 1, pack) != 1) { fprintf(stderr, "Pack file corrupt\n"); - exit(EXIT_FAILURE); + return EXIT_FAILURE; } while (true) { if (fread(&b, sizeof(b), 1, pack) != 1 || fread(&c, sizeof(c), 1, pack) != 1) { fprintf(stderr, "Pack file corrupt\n"); - exit(EXIT_FAILURE); + return EXIT_FAILURE; } if ((b == 0) && (c == 0)) break; @@ -137,5 +134,5 @@ int main(int argc, char *argv[]) fprintf(stdout, "MISSING: %d\n", missing); fprintf(stdout, "TOTAL: %ld\n", tsize); - exit(EXIT_SUCCESS); + return EXIT_SUCCESS; } diff --git a/src/readahead/readahead-collect.c b/src/readahead/readahead-collect.c index c4bcd4e162..8dacf6af5c 100644 --- a/src/readahead/readahead-collect.c +++ b/src/readahead/readahead-collect.c @@ -62,10 +62,6 @@ * - does ioprio_set work with fadvise()? */ -static unsigned arg_files_max = 16*1024; -static off_t arg_file_size_max = READAHEAD_FILE_SIZE_MAX; -static usec_t arg_timeout = 2*USEC_PER_MINUTE; - static ReadaheadShared *shared = NULL; /* Avoid collisions with the NULL pointer */ @@ -592,108 +588,10 @@ finish: return r; } -static int help(void) { - - printf("%s [OPTIONS...] [DIRECTORY]\n\n" - "Collect read-ahead data on early boot.\n\n" - " -h --help Show this help\n" - " --max-files=INT Maximum number of files to read ahead\n" - " --max-file-size=BYTES Maximum size of files to read ahead\n" - " --timeout=USEC Maximum time to spend collecting data\n", - program_invocation_short_name); - - return 0; -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_FILES_MAX = 0x100, - ARG_FILE_SIZE_MAX, - ARG_TIMEOUT - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "files-max", required_argument, NULL, ARG_FILES_MAX }, - { "file-size-max", required_argument, NULL, ARG_FILE_SIZE_MAX }, - { "timeout", required_argument, NULL, ARG_TIMEOUT }, - { NULL, 0, NULL, 0 } - }; - - int c; - - assert(argc >= 0); - assert(argv); +int main_collect(const char *root) { - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) { - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_FILES_MAX: - if (safe_atou(optarg, &arg_files_max) < 0 || arg_files_max <= 0) { - log_error("Failed to parse maximum number of files %s.", optarg); - return -EINVAL; - } - break; - - case ARG_FILE_SIZE_MAX: { - unsigned long long ull; - - if (safe_atollu(optarg, &ull) < 0 || ull <= 0) { - log_error("Failed to parse maximum file size %s.", optarg); - return -EINVAL; - } - - arg_file_size_max = (off_t) ull; - break; - } - - case ARG_TIMEOUT: - if (parse_usec(optarg, &arg_timeout) < 0 || arg_timeout <= 0) { - log_error("Failed to parse timeout %s.", optarg); - return -EINVAL; - } - - break; - - case '?': - return -EINVAL; - - default: - log_error("Unknown option code %c", c); - return -EINVAL; - } - } - - if (optind != argc && - optind != argc-1) { - help(); - return -EINVAL; - } - - return 1; -} - -int main(int argc, char *argv[]) { - int r; - const char *root; - - log_set_target(LOG_TARGET_SAFE); - log_parse_environment(); - log_open(); - - umask(0022); - - r = parse_argv(argc, argv); - if (r <= 0) - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; - - root = optind < argc ? argv[optind] : "/"; + if (!root) + root = "/"; /* Skip this step on read-only media. Note that we check the * underlying block device here, not he read-only flag of the @@ -702,23 +600,23 @@ int main(int argc, char *argv[]) { * device is theoretically writable. */ if (fs_on_read_only(root) > 0) { log_info("Disabling readahead collector due to read-only media."); - return 0; + return EXIT_SUCCESS; } if (!enough_ram()) { log_info("Disabling readahead collector due to low memory."); - return 0; + return EXIT_SUCCESS; } shared = shared_get(); if (!shared) - return 1; + return EXIT_FAILURE; shared->collect = getpid(); __sync_synchronize(); if (collect(root) < 0) - return 1; + return EXIT_FAILURE; - return 0; + return EXIT_SUCCESS; } diff --git a/src/readahead/readahead-common.h b/src/readahead/readahead-common.h index 3056a02485..fd657a2d4d 100644 --- a/src/readahead/readahead-common.h +++ b/src/readahead/readahead-common.h @@ -26,11 +26,16 @@ #include #include "macro.h" +#include "util.h" #define READAHEAD_FILE_SIZE_MAX (10*1024*1024) #define READAHEAD_PACK_FILE_VERSION ";VERSION=2\n" +extern unsigned arg_files_max; +extern off_t arg_file_size_max; +extern usec_t arg_timeout; + int file_verify(int fd, const char *fn, off_t file_size_max, struct stat *st); int fs_on_ssd(const char *p); @@ -52,4 +57,8 @@ int block_bump_request_nr(const char *p); int block_get_readahead(const char *p, uint64_t *bytes); int block_set_readahead(const char *p, uint64_t bytes); +int main_collect(const char *root); +int main_replay(const char *root); +int main_analyze(const char *pack_path); + #endif diff --git a/src/readahead/readahead-replay.c b/src/readahead/readahead-replay.c index fc2c33fcc1..6e6db601d4 100644 --- a/src/readahead/readahead-replay.c +++ b/src/readahead/readahead-replay.c @@ -44,8 +44,6 @@ #include "readahead-common.h" #include "virt.h" -static off_t arg_file_size_max = READAHEAD_FILE_SIZE_MAX; - static ReadaheadShared *shared = NULL; static int unpack_file(FILE *pack) { @@ -289,103 +287,25 @@ finish: return r; } +int main_replay(const char *root) { -static int help(void) { - - printf("%s [OPTIONS...] [DIRECTORY]\n\n" - "Replay collected read-ahead data on early boot.\n\n" - " -h --help Show this help\n" - " --max-file-size=BYTES Maximum size of files to read ahead\n", - program_invocation_short_name); - - return 0; -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_FILE_SIZE_MAX - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "file-size-max", required_argument, NULL, ARG_FILE_SIZE_MAX }, - { NULL, 0, NULL, 0 } - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) { - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_FILE_SIZE_MAX: { - unsigned long long ull; - - if (safe_atollu(optarg, &ull) < 0 || ull <= 0) { - log_error("Failed to parse maximum file size %s.", optarg); - return -EINVAL; - } - - arg_file_size_max = (off_t) ull; - break; - } - - case '?': - return -EINVAL; - - default: - log_error("Unknown option code %c", c); - return -EINVAL; - } - } - - if (optind != argc && - optind != argc-1) { - help(); - return -EINVAL; - } - - return 1; -} - -int main(int argc, char*argv[]) { - int r; - const char *root; - - log_set_target(LOG_TARGET_SAFE); - log_parse_environment(); - log_open(); - - umask(0022); - - r = parse_argv(argc, argv); - if (r <= 0) - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; - - root = optind < argc ? argv[optind] : "/"; + if (!root) + root = "/"; if (!enough_ram()) { log_info("Disabling readahead replay due to low memory."); - return 0; + return EXIT_SUCCESS; } shared = shared_get(); if (!shared) - return 1; + return EXIT_FAILURE; shared->replay = getpid(); __sync_synchronize(); if (replay(root) < 0) - return 1; + return EXIT_FAILURE; - return 0; + return EXIT_SUCCESS; } diff --git a/src/readahead/readahead.c b/src/readahead/readahead.c new file mode 100644 index 0000000000..abeecc7634 --- /dev/null +++ b/src/readahead/readahead.c @@ -0,0 +1,158 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "def.h" +#include "readahead-common.h" + +unsigned arg_files_max = 16*1024; +off_t arg_file_size_max = READAHEAD_FILE_SIZE_MAX; +usec_t arg_timeout = 2*USEC_PER_MINUTE; + +static int help(void) { + + printf("%s [OPTIONS...] collect [DIRECTORY]\n\n" + "Collect read-ahead data on early boot.\n\n" + " -h --help Show this help\n" + " --max-files=INT Maximum number of files to read ahead\n" + " --file-size-max=BYTES Maximum size of files to read ahead\n" + " --timeout=USEC Maximum time to spend collecting data\n\n\n", + program_invocation_short_name); + + printf("%s [OPTIONS...] replay [DIRECTORY]\n\n" + "Replay collected read-ahead data on early boot.\n\n" + " -h --help Show this help\n" + " --file-size-max=BYTES Maximum size of files to read ahead\n\n\n", + program_invocation_short_name); + + printf("%s [OPTIONS...] analyze [PACK FILE]\n\n" + "Analyze collected read-ahead data.\n\n" + " -h --help Show this help\n", + program_invocation_short_name); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_FILES_MAX = 0x100, + ARG_FILE_SIZE_MAX, + ARG_TIMEOUT + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "files-max", required_argument, NULL, ARG_FILES_MAX }, + { "file-size-max", required_argument, NULL, ARG_FILE_SIZE_MAX }, + { "timeout", required_argument, NULL, ARG_TIMEOUT }, + { NULL, 0, NULL, 0 } + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_FILES_MAX: + if (safe_atou(optarg, &arg_files_max) < 0 || arg_files_max <= 0) { + log_error("Failed to parse maximum number of files %s.", optarg); + return -EINVAL; + } + break; + + case ARG_FILE_SIZE_MAX: { + unsigned long long ull; + + if (safe_atollu(optarg, &ull) < 0 || ull <= 0) { + log_error("Failed to parse maximum file size %s.", optarg); + return -EINVAL; + } + + arg_file_size_max = (off_t) ull; + break; + } + + case ARG_TIMEOUT: + if (parse_usec(optarg, &arg_timeout) < 0 || arg_timeout <= 0) { + log_error("Failed to parse timeout %s.", optarg); + return -EINVAL; + } + + break; + + case '?': + return -EINVAL; + + default: + log_error("Unknown option code %c", c); + return -EINVAL; + } + } + + if (optind != argc-1 && + optind != argc-2) { + help(); + return -EINVAL; + } + + return 1; +} + +int main(int argc, char *argv[]) { + int r; + + log_set_target(LOG_TARGET_SAFE); + log_parse_environment(); + log_open(); + + umask(0022); + + r = parse_argv(argc, argv); + if (r <= 0) + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + + if (streq(argv[optind], "collect")) + return main_collect(argv[optind+1]); + else if (streq(argv[optind], "replay")) + return main_replay(argv[optind+1]); + else if (streq(argv[optind], "analyze")) + return main_analyze(argv[optind+1]); + + log_error("Unknown verb %s.", argv[optind]); + return EXIT_FAILURE; +} diff --git a/units/systemd-readahead-collect.service.in b/units/systemd-readahead-collect.service.in index 55b6776649..c597cdc5aa 100644 --- a/units/systemd-readahead-collect.service.in +++ b/units/systemd-readahead-collect.service.in @@ -17,7 +17,7 @@ ConditionVirtualization=no [Service] Type=notify -ExecStart=@rootlibexecdir@/systemd-readahead-collect +ExecStart=@rootlibexecdir@/systemd-readahead collect RemainAfterExit=yes StandardOutput=null OOMScoreAdjust=1000 diff --git a/units/systemd-readahead-replay.service.in b/units/systemd-readahead-replay.service.in index 7324ba36c5..eddf36df95 100644 --- a/units/systemd-readahead-replay.service.in +++ b/units/systemd-readahead-replay.service.in @@ -16,7 +16,7 @@ ConditionVirtualization=no [Service] Type=notify -ExecStart=@rootlibexecdir@/systemd-readahead-replay +ExecStart=@rootlibexecdir@/systemd-readahead replay RemainAfterExit=yes StandardOutput=null OOMScoreAdjust=1000 -- cgit v1.2.3-54-g00ecf From b0193f1c1f1540bfccbdca02df82669b9308e4e2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 Jun 2012 13:08:48 +0200 Subject: systemctl: automatically turn paths and unescaped unit names into proper unit names This makes sure that systemctl status /home is implicitly translated to: systemctl status /home.mount Similar, /dev/foobar becomes dev-foobar.device. Also, all characters that cannot be part of a unit name are implicitly escaped. --- .gitignore | 1 + Makefile.am | 9 +- TODO | 4 - src/core/device.c | 6 +- src/shared/unit-name.c | 73 +++++++++++--- src/shared/unit-name.h | 2 + src/systemctl/systemctl.c | 249 +++++++++++++++++++++++++++++----------------- src/test/test-hostname.c | 3 +- src/test/test-unit-name.c | 82 +++++++++++++++ 9 files changed, 317 insertions(+), 112 deletions(-) create mode 100644 src/test/test-unit-name.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 7d04930a25..07321768a6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/test-unit-name /systemd-system-update-generator /systemd-fstab-generator /systemd-delta diff --git a/Makefile.am b/Makefile.am index e1e02b7286..276c2265d6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -906,7 +906,8 @@ noinst_PROGRAMS += \ test-env-replace \ test-strv \ test-install \ - test-watchdog + test-watchdog \ + test-unit-name TESTS += \ test-job-type \ @@ -955,6 +956,12 @@ test_hostname_SOURCES = \ test_hostname_LDADD = \ libsystemd-core.la +test_unit_name_SOURCES = \ + src/test/test-unit-name.c + +test_unit_name_LDADD = \ + libsystemd-core.la + test_daemon_SOURCES = \ src/test/test-daemon.c diff --git a/TODO b/TODO index f301ac1212..7785e32c9e 100644 --- a/TODO +++ b/TODO @@ -32,14 +32,10 @@ Features: * support rd.luks.allow-discards= kernel cmdline params in cryptsetup generator -* support rd.driver= kernel cmdline params in modules load - * systemctl: when stopping a service which has triggres and warning about it actually check the TriggeredBy= deps fields * journal: hook up with EFI firmware log, new kmsg logic -* falconindy: allow unescaped pathes for mount units, like "systmectl status /.mount"? - * handle C-A-Del in logind, like the power/suspend buttons? * nspawn: make use of device cgroup contrller by default diff --git a/src/core/device.c b/src/core/device.c index cd0a099546..f4c59b3a51 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -144,7 +144,8 @@ static int device_add_escaped_name(Unit *u, const char *dn) { assert(dn); assert(dn[0] == '/'); - if (!(e = unit_name_from_path(dn, ".device"))) + e = unit_name_from_path(dn, ".device"); + if (!e) return -ENOMEM; r = unit_add_name(u, e); @@ -165,7 +166,8 @@ static int device_find_escape_name(Manager *m, const char *dn, Unit **_u) { assert(dn[0] == '/'); assert(_u); - if (!(e = unit_name_from_path(dn, ".device"))) + e = unit_name_from_path(dn, ".device"); + if (!e) return -ENOMEM; u = manager_get_unit(m, e); diff --git a/src/shared/unit-name.c b/src/shared/unit-name.c index 91f464ee9c..8c8e592ab0 100644 --- a/src/shared/unit-name.c +++ b/src/shared/unit-name.c @@ -140,7 +140,8 @@ char *unit_name_to_prefix_and_instance(const char *n) { char *unit_name_to_prefix(const char *n) { const char *p; - if ((p = strchr(n, '@'))) + p = strchr(n, '@'); + if (p) return strndup(n, p - n); return unit_name_to_prefix_and_instance(n); @@ -158,7 +159,8 @@ char *unit_name_change_suffix(const char *n, const char *suffix) { a = e - n; b = strlen(suffix); - if (!(r = new(char, a + b + 1))) + r = new(char, a + b + 1); + if (!r) return NULL; memcpy(r, n, a); @@ -234,7 +236,8 @@ char *unit_name_build_escape(const char *prefix, const char *instance, const cha if (instance) { b = strlen(instance); - if (!(r = new(char, a*4 + 1 + b*4 + c + 1))) + r = new(char, a*4 + 1 + b*4 + c + 1); + if (!r) return NULL; t = do_escape(prefix, r); @@ -255,14 +258,14 @@ char *unit_name_build_escape(const char *prefix, const char *instance, const cha char *unit_name_escape(const char *f) { char *r, *t; - if (!(r = new(char, strlen(f)*4+1))) + r = new(char, strlen(f)*4+1); + if (!r) return NULL; t = do_escape(f, r); *t = 0; return r; - } char *unit_name_unescape(const char *f) { @@ -270,7 +273,8 @@ char *unit_name_unescape(const char *f) { assert(f); - if (!(r = strdup(f))) + r = strdup(f); + if (!r) return NULL; for (t = r; *f; f++) { @@ -347,13 +351,15 @@ char *unit_name_template(const char *f) { char *r; size_t a; - if (!(p = strchr(f, '@'))) + p = strchr(f, '@'); + if (!p) return strdup(f); assert_se(e = strrchr(f, '.')); a = p - f + 1; - if (!(r = new(char, a + strlen(e) + 1))) + r = new(char, a + strlen(e) + 1); + if (!r) return NULL; strcpy(mempcpy(r, f, a), e); @@ -367,7 +373,8 @@ char *unit_name_from_path(const char *path, const char *suffix) { assert(path); assert(suffix); - if (!(p = strdup(path))) + p = strdup(path); + if (!p) return NULL; path_kill_slashes(p); @@ -414,7 +421,8 @@ char *unit_name_to_path(const char *name) { assert(name); - if (!(w = unit_name_to_prefix(name))) + w = unit_name_to_prefix(name); + if (!w) return NULL; e = unit_name_unescape(w); @@ -430,7 +438,7 @@ char *unit_name_to_path(const char *name) { if (!w) return NULL; - e = w; + return w; } return e; @@ -441,7 +449,8 @@ char *unit_name_path_unescape(const char *f) { assert(f); - if (!(e = unit_name_unescape(f))) + e = unit_name_unescape(f); + if (!e) return NULL; if (e[0] != '/') { @@ -453,7 +462,7 @@ char *unit_name_path_unescape(const char *f) { if (!w) return NULL; - e = w; + return w; } return e; @@ -471,3 +480,41 @@ char *unit_dbus_path_from_name(const char *name) { return p; } + +char *unit_name_mangle(const char *name) { + char *r, *t; + const char *f; + + assert(name); + + /* Try to turn a string that might not be a unit name into a + * sensible unit name. */ + + if (path_startswith(name, "/dev/") || + path_startswith(name, "/sys/")) + return unit_name_from_path(name, ".device"); + + if (path_is_absolute(name)) + return unit_name_from_path(name, ".mount"); + + /* We'll only escape the obvious characters here, to play + * safe. */ + + r = new(char, strlen(name) * 4 + 1); + if (!r) + return NULL; + + for (f = name, t = r; *f; f++) { + + if (*f == '/') + *(t++) = '-'; + else if (!strchr("@" VALID_CHARS, *f)) + t = do_escape_char(*f, t); + else + *(t++) = *f; + } + + *t = 0; + + return r; +} diff --git a/src/shared/unit-name.h b/src/shared/unit-name.h index 7aab2e557d..64bb248907 100644 --- a/src/shared/unit-name.h +++ b/src/shared/unit-name.h @@ -56,4 +56,6 @@ char *unit_name_to_path(const char *name); char *unit_dbus_path_from_name(const char *name); +char *unit_name_mangle(const char *name); + #endif diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index ad0cd17e14..b8b9ed0954 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -1123,6 +1123,8 @@ static int load_unit(DBusConnection *bus, char **args) { STRV_FOREACH(name, args+1) { DBusMessage *reply; + bool b; + char *n; if (!(m = dbus_message_new_method_call( "org.freedesktop.systemd1", @@ -1134,15 +1136,19 @@ static int load_unit(DBusConnection *bus, char **args) { goto finish; } - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, name, - DBUS_TYPE_INVALID)) { + n = unit_name_mangle(*name); + b = dbus_message_append_args(m, + DBUS_TYPE_STRING, n ? &n : name, + DBUS_TYPE_INVALID); + free(n); + if (!b) { log_error("Could not append arguments to message."); r = -ENOMEM; goto finish; } - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); + if (!reply) { log_error("Failed to issue method call: %s", bus_error_message(&error)); r = -EIO; goto finish; @@ -1266,22 +1272,29 @@ static bool need_daemon_reload(DBusConnection *bus, const char *unit) { *interface = "org.freedesktop.systemd1.Unit", *property = "NeedDaemonReload", *path; + char *n; + bool k; /* We ignore all errors here, since this is used to show a warning only */ - if (!(m = dbus_message_new_method_call( + m = dbus_message_new_method_call( "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", - "GetUnit"))) + "GetUnit"); + if (!m) goto finish; - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &unit, - DBUS_TYPE_INVALID)) + n = unit_name_mangle(unit); + k = dbus_message_append_args(m, + DBUS_TYPE_STRING, n ? (const char**) &n : &unit, + DBUS_TYPE_INVALID); + free(n); + if (!k) goto finish; - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, NULL))) + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, NULL); + if (!reply) goto finish; if (!dbus_message_get_args(reply, NULL, @@ -1290,11 +1303,12 @@ static bool need_daemon_reload(DBusConnection *bus, const char *unit) { goto finish; dbus_message_unref(m); - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - path, - "org.freedesktop.DBus.Properties", - "Get"))) + m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + path, + "org.freedesktop.DBus.Properties", + "Get"); + if (!m) goto finish; if (!dbus_message_append_args(m, @@ -1305,7 +1319,8 @@ static bool need_daemon_reload(DBusConnection *bus, const char *unit) { } dbus_message_unref(reply); - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, NULL))) + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, NULL); + if (!reply) goto finish; if (!dbus_message_iter_init(reply, &iter) || @@ -1510,6 +1525,8 @@ static int check_one_unit(DBusConnection *bus, char *name, bool quiet) { const char *path = NULL; const char *state; int r = 3; /* According to LSB: "program is not running" */ + char *n; + bool b; assert(bus); assert(name); @@ -1527,9 +1544,12 @@ static int check_one_unit(DBusConnection *bus, char *name, bool quiet) { goto finish; } - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID)) { + n = unit_name_mangle(name); + b = dbus_message_append_args(m, + DBUS_TYPE_STRING, n ? &n : &name, + DBUS_TYPE_INVALID); + free(n); + if (!b) { log_error("Could not append arguments to message."); r = -ENOMEM; goto finish; @@ -1627,12 +1647,14 @@ static void check_triggering_units( const char *interface = "org.freedesktop.systemd1.Unit", *triggered_by_property = "TriggeredBy"; - char *unit_path = NULL; + char *unit_path = NULL, *n = NULL; bool print_warning_label = true; dbus_error_init(&error); - unit_path = unit_dbus_path_from_name(unit_name); + n = unit_name_mangle(unit_name); + unit_path = unit_dbus_path_from_name(n ? n : unit_name); + free(n); if (!unit_path) { log_error("Could not allocate dbus path."); goto finish; @@ -1718,6 +1740,8 @@ static int start_unit_one( DBusMessage *m = NULL, *reply = NULL; const char *path; int r; + char *n; + bool b; assert(bus); assert(method); @@ -1726,26 +1750,31 @@ static int start_unit_one( assert(error); assert(arg_no_block || s); - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - method))) { + m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + method); + if (!m) { log_error("Could not allocate message."); r = -ENOMEM; goto finish; } - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_STRING, &mode, - DBUS_TYPE_INVALID)) { + n = unit_name_mangle(name); + b = dbus_message_append_args(m, + DBUS_TYPE_STRING, n ? (const char **) &n : &name, + DBUS_TYPE_STRING, &mode, + DBUS_TYPE_INVALID); + free(n); + if (!b) { log_error("Could not append arguments to message."); r = -ENOMEM; goto finish; } - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error))) { + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error); + if (!reply) { if (arg_action != ACTION_SYSTEMCTL && error_is_no_service(error)) { /* There's always a fallback possible for @@ -2113,29 +2142,36 @@ static int kill_unit(DBusConnection *bus, char **args) { STRV_FOREACH(name, args+1) { DBusMessage *reply; + char *n; + bool b; - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "KillUnit"))) { + m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "KillUnit"); + if (!m) { log_error("Could not allocate message."); r = -ENOMEM; goto finish; } - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, name, - DBUS_TYPE_STRING, &arg_kill_who, - DBUS_TYPE_STRING, &arg_kill_mode, - DBUS_TYPE_INT32, &arg_signal, - DBUS_TYPE_INVALID)) { + n = unit_name_mangle(*name); + b = dbus_message_append_args(m, + DBUS_TYPE_STRING, n ? &n : name, + DBUS_TYPE_STRING, &arg_kill_who, + DBUS_TYPE_STRING, &arg_kill_mode, + DBUS_TYPE_INT32, &arg_signal, + DBUS_TYPE_INVALID); + free(n); + if (!b) { log_error("Could not append arguments to message."); r = -ENOMEM; goto finish; } - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); + if (!reply) { log_error("Failed to issue method call: %s", bus_error_message(&error)); dbus_error_free(&error); r = -EIO; @@ -3237,12 +3273,16 @@ static int show(DBusConnection *bus, char **args) { uint32_t id; if (safe_atou32(*name, &id) < 0) { - + char *p, *n; /* Interpret as unit name */ - char *p = unit_dbus_path_from_name(*name); - if (!p) + n = unit_name_mangle(*name); + p = unit_dbus_path_from_name(n ? n : *name); + free(n); + if (!p) { + log_error("Out of memory"); return -ENOMEM; + } r = show_one(args[0], bus, p, show_properties, &new_line); free(p); @@ -3255,8 +3295,10 @@ static int show(DBusConnection *bus, char **args) { /* Interpret as job id */ char *p; - if (asprintf(&p, "/org/freedesktop/systemd1/job/%u", id) < 0) + if (asprintf(&p, "/org/freedesktop/systemd1/job/%u", id) < 0) { + log_error("Out of memory"); return -ENOMEM; + } r = show_one(args[0], bus, p, show_properties, &new_line); free(p); @@ -3336,14 +3378,17 @@ static int snapshot(DBusConnection *bus, char **args) { const char *interface = "org.freedesktop.systemd1.Unit", *property = "Id"; + char *n; + bool b; dbus_error_init(&error); - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "CreateSnapshot"))) { + m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "CreateSnapshot"); + if (!m) { log_error("Could not allocate message."); return -ENOMEM; } @@ -3351,16 +3396,20 @@ static int snapshot(DBusConnection *bus, char **args) { if (strv_length(args) > 1) name = args[1]; - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_BOOLEAN, &cleanup, - DBUS_TYPE_INVALID)) { + n = unit_name_mangle(name); + b = dbus_message_append_args(m, + DBUS_TYPE_STRING, n ? (const char**) &n : &name, + DBUS_TYPE_BOOLEAN, &cleanup, + DBUS_TYPE_INVALID); + free(n); + if (!b) { log_error("Could not append arguments to message."); r = -ENOMEM; goto finish; } - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); + if (!reply) { log_error("Failed to issue method call: %s", bus_error_message(&error)); r = -EIO; goto finish; @@ -3375,11 +3424,12 @@ static int snapshot(DBusConnection *bus, char **args) { } dbus_message_unref(m); - if (!(m = dbus_message_new_method_call( + m = dbus_message_new_method_call( "org.freedesktop.systemd1", path, "org.freedesktop.DBus.Properties", - "Get"))) { + "Get"); + if (!m) { log_error("Could not allocate message."); return -ENOMEM; } @@ -3394,7 +3444,8 @@ static int snapshot(DBusConnection *bus, char **args) { } dbus_message_unref(reply); - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); + if (!reply) { log_error("Failed to issue method call: %s", bus_error_message(&error)); r = -EIO; goto finish; @@ -3446,26 +3497,33 @@ static int delete_snapshot(DBusConnection *bus, char **args) { STRV_FOREACH(name, args+1) { const char *path = NULL; + char *n; + bool b; - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GetUnit"))) { + m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetUnit"); + if (!m) { log_error("Could not allocate message."); r = -ENOMEM; goto finish; } - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, name, - DBUS_TYPE_INVALID)) { + n = unit_name_mangle(*name); + b = dbus_message_append_args(m, + DBUS_TYPE_STRING, n ? &n : name, + DBUS_TYPE_INVALID); + free(n); + if (!b) { log_error("Could not append arguments to message."); r = -ENOMEM; goto finish; } - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); + if (!reply) { log_error("Failed to issue method call: %s", bus_error_message(&error)); r = -EIO; goto finish; @@ -3480,18 +3538,20 @@ static int delete_snapshot(DBusConnection *bus, char **args) { } dbus_message_unref(m); - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Snapshot", - "Remove"))) { + m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Snapshot", + "Remove"); + if (!m) { log_error("Could not allocate message."); r = -ENOMEM; goto finish; } dbus_message_unref(reply); - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); + if (!reply) { log_error("Failed to issue method call: %s", bus_error_message(&error)); r = -EIO; goto finish; @@ -3602,26 +3662,33 @@ static int reset_failed(DBusConnection *bus, char **args) { STRV_FOREACH(name, args+1) { DBusMessage *reply; + char *n; + bool b; - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ResetFailedUnit"))) { + m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "ResetFailedUnit"); + if (!m) { log_error("Could not allocate message."); r = -ENOMEM; goto finish; } - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, name, - DBUS_TYPE_INVALID)) { + n = unit_name_mangle(*name); + b = dbus_message_append_args(m, + DBUS_TYPE_STRING, n ? &n : name, + DBUS_TYPE_INVALID); + free(n); + if (!b) { log_error("Could not append arguments to message."); r = -ENOMEM; goto finish; } - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); + if (!reply) { log_error("Failed to issue method call: %s", bus_error_message(&error)); r = -EIO; goto finish; @@ -4735,7 +4802,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { return -EINVAL; default: - log_error("Unknown option code %c", c); + log_error("Unknown option code '%c'.", c); return -EINVAL; } } @@ -4828,7 +4895,7 @@ static int halt_parse_argv(int argc, char *argv[]) { return -EINVAL; default: - log_error("Unknown option code %c", c); + log_error("Unknown option code '%c'.", c); return -EINVAL; } } @@ -4965,7 +5032,7 @@ static int shutdown_parse_argv(int argc, char *argv[]) { return -EINVAL; default: - log_error("Unknown option code %c", c); + log_error("Unknown option code '%c'.", c); return -EINVAL; } } @@ -5041,7 +5108,7 @@ static int telinit_parse_argv(int argc, char *argv[]) { return -EINVAL; default: - log_error("Unknown option code %c", c); + log_error("Unknown option code '%c'.", c); return -EINVAL; } } @@ -5066,7 +5133,7 @@ static int telinit_parse_argv(int argc, char *argv[]) { break; if (i >= ELEMENTSOF(table)) { - log_error("Unknown command %s.", argv[optind]); + log_error("Unknown command '%s'.", argv[optind]); return -EINVAL; } @@ -5104,7 +5171,7 @@ static int runlevel_parse_argv(int argc, char *argv[]) { return -EINVAL; default: - log_error("Unknown option code %c", c); + log_error("Unknown option code '%c'.", c); return -EINVAL; } } @@ -5405,7 +5472,7 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError break; if (i >= ELEMENTSOF(verbs)) { - log_error("Unknown operation %s", argv[optind]); + log_error("Unknown operation '%s'.", argv[optind]); return -EINVAL; } } diff --git a/src/test/test-hostname.c b/src/test/test-hostname.c index 8c1a60f940..ad4f285619 100644 --- a/src/test/test-hostname.c +++ b/src/test/test-hostname.c @@ -30,7 +30,8 @@ int main(int argc, char* argv[]) { int r; - if ((r = hostname_setup()) < 0) + r = hostname_setup(); + if (r < 0) fprintf(stderr, "hostname: %s\n", strerror(-r)); return 0; diff --git a/src/test/test-unit-name.c b/src/test/test-unit-name.c new file mode 100644 index 0000000000..9d636afe15 --- /dev/null +++ b/src/test/test-unit-name.c @@ -0,0 +1,82 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "unit-name.h" +#include "util.h" + +int main(int argc, char* argv[]) { + char *t, *k; + + assert_se(t = unit_name_mangle("/home")); + assert_se(k = unit_name_mangle(t)); + puts(t); + assert_se(streq(t, k)); + free(t); + free(k); + + assert_se(t = unit_name_mangle("/dev/sda")); + assert_se(k = unit_name_mangle(t)); + puts(t); + assert_se(streq(t, k)); + free(t); + free(k); + + assert_se(t = unit_name_mangle("üxknürz.service")); + assert_se(k = unit_name_mangle(t)); + puts(t); + assert_se(streq(t, k)); + free(t); + free(k); + + assert_se(t = unit_name_mangle("foobar-meh...waldi.service")); + assert_se(k = unit_name_mangle(t)); + puts(t); + assert_se(streq(t, k)); + free(t); + free(k); + + assert_se(t = unit_name_mangle("_____####----.....service")); + assert_se(k = unit_name_mangle(t)); + puts(t); + assert_se(streq(t, k)); + free(t); + free(k); + + assert_se(t = unit_name_mangle("_____##@;;;,,,##----.....service")); + assert_se(k = unit_name_mangle(t)); + puts(t); + assert_se(streq(t, k)); + free(t); + free(k); + + assert_se(t = unit_name_mangle("xxx@@@@/////\\\\\\\\\\yyy.service")); + assert_se(k = unit_name_mangle(t)); + puts(t); + assert_se(streq(t, k)); + free(t); + free(k); + + return 0; +} -- cgit v1.2.3-54-g00ecf From cbdca8525b4f36297cb9e5cb090a9648763ed1bf Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 13 Jul 2012 00:29:26 +0200 Subject: journal: beef up journal matches considerably we now can take multiple matches, and they will apply as AND if they apply to different fields and OR if they apply to the same fields. Also, terms of this kind can be combined with an overreaching OR. --- .gitignore | 2 + Makefile.am | 40 ++- man/journalctl.xml | 56 ++- man/systemd.journal-fields.xml | 7 +- src/journal/journal-file.c | 219 ++++++++++-- src/journal/journal-file.h | 4 + src/journal/journal-internal.h | 38 +- src/journal/journalctl.c | 10 +- src/journal/sd-journal.c | 704 +++++++++++++++++++++++--------------- src/journal/test-journal-match.c | 67 ++++ src/journal/test-journal-send.c | 4 + src/journal/test-journal-stream.c | 169 +++++++++ src/systemd/sd-journal.h | 1 + 13 files changed, 974 insertions(+), 347 deletions(-) create mode 100644 src/journal/test-journal-match.c create mode 100644 src/journal/test-journal-stream.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 07321768a6..c4a7b0ddec 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +/test-journal-match +/test-journal-stream /test-unit-name /systemd-system-update-generator /systemd-fstab-generator diff --git a/Makefile.am b/Makefile.am index aedd22052b..14f9455b51 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2232,29 +2232,13 @@ journalctl_LDADD = \ libsystemd-logs.la test_journal_SOURCES = \ - src/journal/test-journal.c \ - src/journal/sd-journal.c \ - src/journal/journal-file.c \ - src/journal/lookup3.c \ - src/journal/journal-send.c + src/journal/test-journal.c test_journal_LDADD = \ - libsystemd-label.la \ libsystemd-shared.la \ + libsystemd-journal-internal.la \ libsystemd-id128-internal.la -if HAVE_XZ -test_journal_SOURCES += \ - src/journal/compress.c - -test_journal_CFLAGS = \ - $(AM_CFLAGS) \ - $(XZ_CFLAGS) - -test_journal_LDADD += \ - $(XZ_LIBS) -endif - test_journal_send_SOURCES = \ src/journal/test-journal-send.c @@ -2263,6 +2247,22 @@ test_journal_send_LDADD = \ libsystemd-journal-internal.la \ libsystemd-id128-internal.la +test_journal_match_SOURCES = \ + src/journal/test-journal-match.c + +test_journal_match_LDADD = \ + libsystemd-shared.la \ + libsystemd-journal-internal.la \ + libsystemd-id128-internal.la + +test_journal_stream_SOURCES = \ + src/journal/test-journal-stream.c + +test_journal_stream_LDADD = \ + libsystemd-shared.la \ + libsystemd-journal-internal.la \ + libsystemd-id128-internal.la + libsystemd_journal_la_SOURCES = \ src/journal/sd-journal.c \ src/journal/journal-file.c \ @@ -2327,7 +2327,9 @@ UNINSTALL_EXEC_HOOKS += \ noinst_PROGRAMS += \ test-journal \ - test-journal-send + test-journal-send \ + test-journal-match \ + test-journal-stream pkginclude_HEADERS += \ src/systemd/sd-journal.h \ diff --git a/man/journalctl.xml b/man/journalctl.xml index bb964b01ed..f314fb6d26 100644 --- a/man/journalctl.xml +++ b/man/journalctl.xml @@ -49,7 +49,7 @@ - journalctl OPTIONS MATCH + journalctl OPTIONS MATCHES @@ -66,12 +66,25 @@ contents of the journal, starting with the oldest entry collected. - If a match argument is passed the output is - filtered accordingly. A match is in the format - FIELD=VALUE, - e.g. _SYSTEMD_UNIT=httpd.service. See + If one or more match arguments are passed the + output is filtered accordingly. A match is in the + format FIELD=VALUE, + e.g. _SYSTEMD_UNIT=httpd.service, + referring to the components of a structured journal + entry. See systemd.journal-fields7 - for a list of well-known fields. + for a list of well-known fields. If multiple matches + are specified matching different fields the log + entries are filtered by both, i.e. the resulting output + will show only entries matching all the specified + matches of this kind. If two matches apply to the same + field, then they are automatically matched as + alternatives, i.e. the resulting output will show + entries matching any of the specified matches for the + same field. Finally, if the character + "+" appears as separate word on the + command line all matches before and after are combined + in a disjunction (i.e. logical OR). Output is interleaved from all accessible journal files, whether they are rotated or currently @@ -271,6 +284,37 @@ + + Examples + + Without arguments all collected logs are shown + unfiltered: + + journalctl + + With one match specified all entries with a field matching the expression are shown: + + journalctl _SYSTEMD_UNIT=avahi-daemon.service + + If two different fields are matched only entries matching both expressions at the same time are shown: + + journalctl _SYSTEMD_UNIT=avahi-daemon.service _PID=28097 + + If two matches refer to the same field all entries matching either expression are shown: + + journalctl _SYSTEMD_UNIT=avahi-daemon.service _SYSTEMD_UNIT=dbus.service + + If the separator "+" is used + two expression may be combined in a logical OR. The + following will show all messages from the Avahi + service process with the PID 28097 plus all messages + from the D-Bus service (from any of its + processes): + + journalctl _SYSTEMD_UNIT=avahi-daemon.service _PID=28097 + _SYSTEMD_UNIT=dbus.service + + + See Also diff --git a/man/systemd.journal-fields.xml b/man/systemd.journal-fields.xml index fd0beb968f..4f664f43e5 100644 --- a/man/systemd.journal-fields.xml +++ b/man/systemd.journal-fields.xml @@ -299,7 +299,12 @@ addresses of journal entries are serialized into fields prefixed with double underscores. Note that these aren't proper fields when stored in the journal, - but addressing meta data of entries. + but addressing meta data of entries. They cannot be + written as part of structured log entries via calls + such as + sd_journal_send3. They + may also not be used as matches for + sd_journal_add_match3 diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index 9665a0535b..0aada9c419 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -1212,8 +1212,15 @@ static int generic_array_bisect(JournalFile *f, } } - if (k > n) + if (k > n) { + if (direction == DIRECTION_UP) { + i = n; + subtract_one = true; + goto found; + } + return 0; + } last_p = lp; @@ -1246,7 +1253,7 @@ found: *offset = p; if (idx) - *idx = t + i - (subtract_one ? 1 : 0); + *idx = t + i + (subtract_one ? -1 : 0); return 1; } @@ -1263,6 +1270,8 @@ static int generic_array_bisect_plus_one(JournalFile *f, uint64_t *idx) { int r; + bool step_back = false; + Object *o; assert(f); assert(test_object); @@ -1279,33 +1288,77 @@ static int generic_array_bisect_plus_one(JournalFile *f, if (r == TEST_FOUND) r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT; - if (r == TEST_RIGHT) { - Object *o; - - r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o); - if (r < 0) - return r; + /* if we are looking with DIRECTION_UP then we need to first + see if in the actual array there is a matching entry, and + return the last one of that. But if there isn't any we need + to return this one. Hence remember this, and return it + below. */ + if (r == TEST_LEFT) + step_back = direction == DIRECTION_UP; - if (ret) - *ret = o; - - if (offset) - *offset = extra; - - if (idx) - *idx = 0; - - return 1; + if (r == TEST_RIGHT) { + if (direction == DIRECTION_DOWN) + goto found; + else + return 0; } r = generic_array_bisect(f, first, n-1, needle, test_object, direction, ret, offset, idx); + if (r == 0 && step_back) + goto found; + if (r > 0 && idx) (*idx) ++; return r; + +found: + r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o); + if (r < 0) + return r; + + if (ret) + *ret = o; + + if (offset) + *offset = extra; + + if (idx) + *idx = 0; + + return 1; +} + +static int test_object_offset(JournalFile *f, uint64_t p, uint64_t needle) { + assert(f); + assert(p > 0); + + if (p == needle) + return TEST_FOUND; + else if (p < needle) + return TEST_LEFT; + else + return TEST_RIGHT; +} + +int journal_file_move_to_entry_by_offset( + JournalFile *f, + uint64_t p, + direction_t direction, + Object **ret, + uint64_t *offset) { + + return generic_array_bisect(f, + le64toh(f->header->entry_array_offset), + le64toh(f->header->n_entries), + p, + test_object_offset, + direction, + ret, offset, NULL); } + static int test_object_seqnum(JournalFile *f, uint64_t p, uint64_t needle) { Object *o; int r; @@ -1407,12 +1460,13 @@ int journal_file_move_to_entry_by_monotonic( Object *o; int r; - sd_id128_to_string(boot_id, t + 9); + assert(f); + sd_id128_to_string(boot_id, t + 9); r = journal_file_find_data_object(f, t, strlen(t), &o, NULL); if (r < 0) return r; - else if (r == 0) + if (r == 0) return -ENOENT; return generic_array_bisect_plus_one(f, @@ -1425,18 +1479,6 @@ int journal_file_move_to_entry_by_monotonic( ret, offset, NULL); } -static int test_object_offset(JournalFile *f, uint64_t p, uint64_t needle) { - assert(f); - assert(p > 0); - - if (p == needle) - return TEST_FOUND; - else if (p < needle) - return TEST_LEFT; - else - return TEST_RIGHT; -} - int journal_file_next_entry( JournalFile *f, Object *o, uint64_t p, @@ -1601,6 +1643,119 @@ int journal_file_next_entry_for_data( ret, offset); } +int journal_file_move_to_entry_by_offset_for_data( + JournalFile *f, + uint64_t data_offset, + uint64_t p, + direction_t direction, + Object **ret, uint64_t *offset) { + + int r; + Object *d; + + assert(f); + + r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d); + if (r < 0) + return r; + + return generic_array_bisect_plus_one(f, + le64toh(d->data.entry_offset), + le64toh(d->data.entry_array_offset), + le64toh(d->data.n_entries), + p, + test_object_offset, + direction, + ret, offset, NULL); +} + +int journal_file_move_to_entry_by_monotonic_for_data( + JournalFile *f, + uint64_t data_offset, + sd_id128_t boot_id, + uint64_t monotonic, + direction_t direction, + Object **ret, uint64_t *offset) { + + char t[9+32+1] = "_BOOT_ID="; + Object *o, *d; + int r; + uint64_t b, z; + + assert(f); + + /* First, seek by time */ + sd_id128_to_string(boot_id, t + 9); + r = journal_file_find_data_object(f, t, strlen(t), &o, &b); + if (r < 0) + return r; + if (r == 0) + return -ENOENT; + + r = generic_array_bisect_plus_one(f, + le64toh(o->data.entry_offset), + le64toh(o->data.entry_array_offset), + le64toh(o->data.n_entries), + monotonic, + test_object_monotonic, + direction, + NULL, &z, NULL); + if (r <= 0) + return r; + + /* And now, continue seeking until we find an entry that + * exists in both bisection arrays */ + + for (;;) { + Object *qo; + uint64_t p, q; + + r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d); + if (r < 0) + return r; + + r = generic_array_bisect_plus_one(f, + le64toh(d->data.entry_offset), + le64toh(d->data.entry_array_offset), + le64toh(d->data.n_entries), + z, + test_object_offset, + direction, + NULL, &p, NULL); + if (r <= 0) + return r; + + r = journal_file_move_to_object(f, OBJECT_DATA, b, &o); + if (r < 0) + return r; + + r = generic_array_bisect_plus_one(f, + le64toh(o->data.entry_offset), + le64toh(o->data.entry_array_offset), + le64toh(o->data.n_entries), + p, + test_object_offset, + direction, + &qo, &q, NULL); + + if (r <= 0) + return r; + + if (p == q) { + if (ret) + *ret = qo; + if (offset) + *offset = q; + + return 1; + } + + z = q; + } + + return 0; +} + int journal_file_move_to_entry_by_seqnum_for_data( JournalFile *f, uint64_t data_offset, diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h index a9925c0754..5c42ecdf6c 100644 --- a/src/journal/journal-file.h +++ b/src/journal/journal-file.h @@ -43,6 +43,7 @@ enum { WINDOW_DATA_HASH_TABLE = OBJECT_DATA_HASH_TABLE, WINDOW_FIELD_HASH_TABLE = OBJECT_FIELD_HASH_TABLE, WINDOW_ENTRY_ARRAY = OBJECT_ENTRY_ARRAY, + WINDOW_SIGNATURE = OBJECT_SIGNATURE, WINDOW_HEADER, _WINDOW_MAX }; @@ -106,12 +107,15 @@ int journal_file_skip_entry(JournalFile *f, Object *o, uint64_t p, int64_t skip, int journal_file_next_entry_for_data(JournalFile *f, Object *o, uint64_t p, uint64_t data_offset, direction_t direction, Object **ret, uint64_t *offset); +int journal_file_move_to_entry_by_offset(JournalFile *f, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *offset); int journal_file_move_to_entry_by_seqnum(JournalFile *f, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *offset); int journal_file_move_to_entry_by_realtime(JournalFile *f, uint64_t realtime, direction_t direction, Object **ret, uint64_t *offset); int journal_file_move_to_entry_by_monotonic(JournalFile *f, sd_id128_t boot_id, uint64_t monotonic, direction_t direction, Object **ret, uint64_t *offset); +int journal_file_move_to_entry_by_offset_for_data(JournalFile *f, uint64_t data_offset, uint64_t p, direction_t direction, Object **ret, uint64_t *offset); int journal_file_move_to_entry_by_seqnum_for_data(JournalFile *f, uint64_t data_offset, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *offset); int journal_file_move_to_entry_by_realtime_for_data(JournalFile *f, uint64_t data_offset, uint64_t realtime, direction_t direction, Object **ret, uint64_t *offset); +int journal_file_move_to_entry_by_monotonic_for_data(JournalFile *f, uint64_t data_offset, sd_id128_t boot_id, uint64_t monotonic, direction_t direction, Object **ret, uint64_t *offset); int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p, uint64_t *seqnum, Object **ret, uint64_t *offset); diff --git a/src/journal/journal-internal.h b/src/journal/journal-internal.h index 929dfcdcb8..482ef61b9a 100644 --- a/src/journal/journal-internal.h +++ b/src/journal/journal-internal.h @@ -28,28 +28,46 @@ #include +#include "journal-def.h" #include "list.h" +#include "hashmap.h" +#include "journal-file.h" + +typedef enum MatchType MatchType; +typedef enum LocationType LocationType; typedef struct Match Match; typedef struct Location Location; typedef struct Directory Directory; -typedef enum location_type { - LOCATION_HEAD, - LOCATION_TAIL, - LOCATION_DISCRETE -} location_type_t; +typedef enum MatchType { + MATCH_DISCRETE, + MATCH_OR_TERM, + MATCH_AND_TERM +} MatchType; struct Match { + MatchType type; + Match *parent; + LIST_FIELDS(Match, matches); + + /* For concrete matches */ char *data; size_t size; le64_t le_hash; - LIST_FIELDS(Match, matches); + /* For terms */ + LIST_HEAD(Match, matches); }; +typedef enum LocationType { + LOCATION_HEAD, + LOCATION_TAIL, + LOCATION_DISCRETE +} LocationType; + struct Location { - location_type_t type; + LocationType type; uint64_t seqnum; sd_id128_t seqnum_id; @@ -78,6 +96,7 @@ struct sd_journal { Hashmap *files; Location current_location; + JournalFile *current_file; uint64_t current_field; @@ -86,10 +105,11 @@ struct sd_journal { int inotify_fd; - LIST_HEAD(Match, matches); - unsigned n_matches; + Match *level0, *level1; unsigned current_invalidate_counter, last_invalidate_counter; }; +char *journal_make_match_string(sd_journal *j); + #endif diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 43cd2a3fef..65b3bd5a86 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -225,7 +225,9 @@ static int add_matches(sd_journal *j, char **args) { STRV_FOREACH(i, args) { - if (path_is_absolute(*i)) { + if (streq(*i, "+")) + r = sd_journal_add_disjunction(j); + else if (path_is_absolute(*i)) { char *p; const char *path; struct stat st; @@ -249,7 +251,7 @@ static int add_matches(sd_journal *j, char **args) { return -ENOMEM; } - r = sd_journal_add_match(j, t, strlen(t)); + r = sd_journal_add_match(j, t, 0); free(t); } else { free(p); @@ -259,10 +261,10 @@ static int add_matches(sd_journal *j, char **args) { free(p); } else - r = sd_journal_add_match(j, *i, strlen(*i)); + r = sd_journal_add_match(j, *i, 0); if (r < 0) { - log_error("Failed to add match: %s", strerror(-r)); + log_error("Failed to add match '%s': %s", *i, strerror(-r)); return r; } } diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index f4ef1aea7c..4bcc65c5c7 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -87,102 +87,272 @@ static void set_location(sd_journal *j, JournalFile *f, Object *o, uint64_t offs f->current_offset = offset; } -static int same_field(const void *_a, size_t s, const void *_b, size_t t) { +static int match_is_valid(const void *data, size_t size) { + const char *b, *p; + + assert(data); + + if (size < 2) + return false; + + if (startswith(data, "__")) + return false; + + b = data; + for (p = b; p < b + size; p++) { + + if (*p == '=') + return p > b; + + if (*p == '_') + continue; + + if (*p >= 'A' && *p <= 'Z') + continue; + + if (*p >= '0' && *p <= '9') + continue; + + return false; + } + + return false; +} + +static bool same_field(const void *_a, size_t s, const void *_b, size_t t) { const uint8_t *a = _a, *b = _b; size_t j; - bool a_good = false, b_good = false, different = false; for (j = 0; j < s && j < t; j++) { - if (a[j] == '=') - a_good = true; - if (b[j] == '=') - b_good = true; if (a[j] != b[j]) - different = true; + return false; + + if (a[j] == '=') + return true; + } + + return true; +} + +static Match *match_new(Match *p, MatchType t) { + Match *m; - if (a_good && b_good) - return different ? 0 : 1; + m = new0(Match, 1); + if (!m) + return NULL; + + m->type = t; + + if (p) { + m->parent = p; + LIST_PREPEND(Match, matches, p->matches, m); } - return -EINVAL; + return m; +} + +static void match_free(Match *m) { + assert(m); + + while (m->matches) + match_free(m->matches); + + if (m->parent) + LIST_REMOVE(Match, matches, m->parent->matches, m); + + free(m->data); + free(m); +} + +static void match_free_if_empty(Match *m) { + assert(m); + + if (m->matches) + return; + + match_free(m); } _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) { - Match *m, *after = NULL; + Match *l2, *l3, *add_here = NULL, *m; le64_t le_hash; if (!j) return -EINVAL; + if (!data) return -EINVAL; - if (size <= 1) - return -EINVAL; - if (!memchr(data, '=', size)) - return -EINVAL; - if (*(char*) data == '=') + + if (size == 0) + size = strlen(data); + + if (!match_is_valid(data, size)) return -EINVAL; - /* FIXME: iterating with multiple matches is currently - * broken */ - if (j->matches) - return -ENOTSUP; + /* level 0: OR term + * level 1: AND terms + * level 2: OR terms + * level 3: concrete matches */ + + if (!j->level0) { + j->level0 = match_new(NULL, MATCH_OR_TERM); + if (!j->level0) + return -ENOMEM; + } + + if (!j->level1) { + j->level1 = match_new(j->level0, MATCH_AND_TERM); + if (!j->level1) + return -ENOMEM; + } + + assert(j->level0->type == MATCH_OR_TERM); + assert(j->level1->type == MATCH_AND_TERM); le_hash = htole64(hash64(data, size)); - LIST_FOREACH(matches, m, j->matches) { - int r; + LIST_FOREACH(matches, l2, j->level1->matches) { + assert(l2->type == MATCH_OR_TERM); - if (m->le_hash == le_hash && - m->size == size && - memcmp(m->data, data, size) == 0) - return 0; + LIST_FOREACH(matches, l3, l2->matches) { + assert(l3->type == MATCH_DISCRETE); - r = same_field(data, size, m->data, m->size); - if (r < 0) - return r; - else if (r > 0) - after = m; + /* Exactly the same match already? Then ignore + * this addition */ + if (l3->le_hash == le_hash && + l3->size == size && + memcmp(l3->data, data, size) == 0) + return 0; + + /* Same field? Then let's add this to this OR term */ + if (same_field(data, size, l3->data, l3->size)) { + add_here = l2; + break; + } + } + + if (add_here) + break; } - m = new0(Match, 1); + if (!add_here) { + add_here = match_new(j->level1, MATCH_OR_TERM); + if (!add_here) + goto fail; + } + + m = match_new(add_here, MATCH_DISCRETE); if (!m) - return -ENOMEM; + goto fail; + m->le_hash = le_hash; m->size = size; + m->data = memdup(data, size); + if (!m->data) + goto fail; + + detach_location(j); + + return 0; + +fail: + if (add_here) + match_free_if_empty(add_here); + + if (j->level1) + match_free_if_empty(j->level1); + + if (j->level0) + match_free_if_empty(j->level0); + + return -ENOMEM; +} + +_public_ int sd_journal_add_disjunction(sd_journal *j) { + Match *m; + + assert(j); + + if (!j->level0) + return 0; + + if (!j->level1) + return 0; - m->data = malloc(m->size); - if (!m->data) { - free(m); + if (!j->level1->matches) + return 0; + + m = match_new(j->level0, MATCH_AND_TERM); + if (!m) return -ENOMEM; + + j->level1 = m; + return 0; +} + +static char *match_make_string(Match *m) { + char *p, *r; + Match *i; + bool enclose = false; + + if (!m) + return strdup(""); + + if (m->type == MATCH_DISCRETE) + return strndup(m->data, m->size); + + p = NULL; + LIST_FOREACH(matches, i, m->matches) { + char *t, *k; + + t = match_make_string(i); + if (!t) { + free(p); + return NULL; + } + + if (p) { + k = join(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t, NULL); + free(p); + free(t); + + if (!k) + return NULL; + + p = k; + + enclose = true; + } else { + free(p); + p = t; + } } - memcpy(m->data, data, size); - m->le_hash = le_hash; + if (enclose) { + r = join("(", p, ")", NULL); + free(p); + return r; + } - /* Matches for the same fields we order adjacent to each - * other */ - LIST_INSERT_AFTER(Match, matches, j->matches, after, m); - j->n_matches ++; + return p; +} - detach_location(j); +char *journal_make_match_string(sd_journal *j) { + assert(j); - return 0; + return match_make_string(j->level0); } _public_ void sd_journal_flush_matches(sd_journal *j) { + if (!j) return; - while (j->matches) { - Match *m = j->matches; + if (j->level0) + match_free(j->level0); - LIST_REMOVE(Match, matches, j->matches, m); - free(m->data); - free(m); - } - - j->n_matches = 0; + j->level0 = j->level1 = NULL; detach_location(j); } @@ -319,290 +489,270 @@ static int compare_with_location(JournalFile *af, Object *ao, Location *l) { return 0; } -static int find_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) { - Object *o = NULL; - uint64_t p = 0; +static int next_for_match( + sd_journal *j, + Match *m, + JournalFile *f, + uint64_t after_offset, + direction_t direction, + Object **ret, + uint64_t *offset) { + int r; + uint64_t np = 0; + Object *n; assert(j); + assert(m); + assert(f); - if (!j->matches) { - /* No matches is simple */ - - if (j->current_location.type == LOCATION_HEAD) - r = journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, &o, &p); - else if (j->current_location.type == LOCATION_TAIL) - r = journal_file_next_entry(f, NULL, 0, DIRECTION_UP, &o, &p); - else if (j->current_location.seqnum_set && - sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id)) - r = journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, &o, &p); - else if (j->current_location.monotonic_set) { - r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, &o, &p); - - if (r == -ENOENT) { - /* boot id unknown in this file */ - if (j->current_location.realtime_set) - r = journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, &o, &p); - else - r = journal_file_next_entry(f, NULL, 0, direction, &o, &p); - } - } else if (j->current_location.realtime_set) - r = journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, &o, &p); - else - r = journal_file_next_entry(f, NULL, 0, direction, &o, &p); + if (m->type == MATCH_DISCRETE) { + uint64_t dp; + r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp); if (r <= 0) return r; - } else { - Match *m, *term_match = NULL; - Object *to = NULL; - uint64_t tp = 0; + return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset); - /* We have matches, first, let's jump to the monotonic - * position if we have any, since it implies a - * match. */ + } else if (m->type == MATCH_OR_TERM) { + Match *i; - if (j->current_location.type == LOCATION_DISCRETE && - j->current_location.monotonic_set) { + /* Find the earliest match beyond after_offset */ - r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, &o, &p); - if (r <= 0) - return r == -ENOENT ? 0 : r; - } - - LIST_FOREACH(matches, m, j->matches) { - Object *c, *d; - uint64_t cp, dp; - - r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), &d, &dp); - if (r <= 0) - return r; - - if (j->current_location.type == LOCATION_HEAD) - r = journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, &c, &cp); - else if (j->current_location.type == LOCATION_TAIL) - r = journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, &c, &cp); - else if (j->current_location.seqnum_set && - sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id)) - r = journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, &c, &cp); - else if (j->current_location.realtime_set) - r = journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, &c, &cp); - else - r = journal_file_next_entry_for_data(f, NULL, 0, dp, direction, &c, &cp); + LIST_FOREACH(matches, i, m->matches) { + uint64_t cp; + r = next_for_match(j, i, f, after_offset, direction, NULL, &cp); if (r < 0) return r; + else if (r > 0) { + if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp)) + np = cp; + } + } - if (!term_match) { - term_match = m; - - if (r > 0) { - to = c; - tp = cp; - } - } else if (same_field(term_match->data, term_match->size, m->data, m->size)) { - - /* Same field as previous match... */ - if (r > 0) { - - /* Find the earliest of the OR matches */ + } else if (m->type == MATCH_AND_TERM) { + Match *i; + bool continue_looking; - if (!to || - (direction == DIRECTION_DOWN && cp < tp) || - (direction == DIRECTION_UP && cp > tp)) { - to = c; - tp = cp; - } + /* Always jump to the next matching entry and repeat + * this until we fine and offset that matches for all + * matches. */ - } + if (!m->matches) + return 0; - } else { + np = 0; + do { + continue_looking = false; - /* Previous term is finished, did anything match? */ - if (!to) - return 0; + LIST_FOREACH(matches, i, m->matches) { + uint64_t cp, limit; - /* Find the last of the AND matches */ - if (!o || - (direction == DIRECTION_DOWN && tp > p) || - (direction == DIRECTION_UP && tp < p)) { - o = to; - p = tp; - } + if (np == 0) + limit = after_offset; + else if (direction == DIRECTION_DOWN) + limit = MAX(np, after_offset); + else + limit = MIN(np, after_offset); - term_match = m; + r = next_for_match(j, i, f, limit, direction, NULL, &cp); + if (r <= 0) + return r; - if (r > 0) { - to = c; - tp = cp; - } else { - to = NULL; - tp = 0; + if ((direction == DIRECTION_DOWN ? cp >= after_offset : cp <= after_offset) && + (np == 0 || (direction == DIRECTION_DOWN ? cp > np : np < cp))) { + np = cp; + continue_looking = true; } } - } - /* Last term is finished, did anything match? */ - if (!to) - return 0; + } while (continue_looking); + } - if (!o || - (direction == DIRECTION_DOWN && tp > p) || - (direction == DIRECTION_UP && tp < p)) { - o = to; - p = tp; - } + if (np == 0) + return 0; - if (!o) - return 0; - } + r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n); + if (r < 0) + return r; if (ret) - *ret = o; - + *ret = n; if (offset) - *offset = p; + *offset = np; return 1; } -static int next_with_matches(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) { +static int find_location_for_match( + sd_journal *j, + Match *m, + JournalFile *f, + direction_t direction, + Object **ret, + uint64_t *offset) { + int r; - uint64_t cp; - Object *c; assert(j); + assert(m); assert(f); - assert(ret); - assert(offset); - c = *ret; - cp = *offset; + if (m->type == MATCH_DISCRETE) { + uint64_t dp; - if (!j->matches) { - /* No matches is easy */ - - r = journal_file_next_entry(f, c, cp, direction, &c, &cp); + r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp); if (r <= 0) return r; - if (ret) - *ret = c; - if (offset) - *offset = cp; - return 1; - } + /* FIXME: missing: find by monotonic */ + + if (j->current_location.type == LOCATION_HEAD) + return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_DOWN, ret, offset); + if (j->current_location.type == LOCATION_TAIL) + return journal_file_next_entry_for_data(f, NULL, 0, dp, DIRECTION_UP, ret, offset); + if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id)) + return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset); + if (j->current_location.monotonic_set) { + r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset); + if (r != -ENOENT) + return r; + } + if (j->current_location.realtime_set) + return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset); - /* So there are matches we have to adhere to, let's find the - * first entry that matches all of them */ + return journal_file_next_entry_for_data(f, NULL, 0, dp, direction, ret, offset); - for (;;) { - uint64_t np, n; - bool found, term_result = false; - Match *m, *term_match = NULL; - Object *npo = NULL; + } else if (m->type == MATCH_OR_TERM) { + uint64_t np = 0; + Object *n; + Match *i; - n = journal_file_entry_n_items(c); + /* Find the earliest match */ - /* Make sure we don't match the entry we are starting - * from. */ - found = cp != *offset; + LIST_FOREACH(matches, i, m->matches) { + uint64_t cp; - np = 0; - LIST_FOREACH(matches, m, j->matches) { - uint64_t q, k; - Object *qo = NULL; - - /* Let's check if this is the beginning of a - * new term, i.e. has a different field prefix - * as the preceeding match. */ - if (!term_match) { - term_match = m; - term_result = false; - } else if (!same_field(term_match->data, term_match->size, m->data, m->size)) { - if (!term_result) - found = false; - - term_match = m; - term_result = false; + r = find_location_for_match(j, i, f, direction, NULL, &cp); + if (r < 0) + return r; + else if (r > 0) { + if (np == 0 || (direction == DIRECTION_DOWN ? np > cp : np < cp)) + np = cp; } + } - for (k = 0; k < n; k++) - if (c->entry.items[k].hash == m->le_hash) - break; + if (np == 0) + return 0; - if (k >= n) { - /* Hmm, didn't find any field that - * matched this rule, so ignore this - * match. Go on with next match */ - continue; - } + r = journal_file_move_to_object(f, OBJECT_ENTRY, np, &n); + if (r < 0) + return r; + + if (ret) + *ret = n; + if (offset) + *offset = np; - term_result = true; + return 1; + + } else { + Match *i; + uint64_t np = 0; - /* Hmm, so, this field matched, let's remember - * where we'd have to try next, in case the other - * matches are not OK */ + assert(m->type == MATCH_AND_TERM); - r = journal_file_next_entry_for_data(f, c, cp, le64toh(c->entry.items[k].object_offset), direction, &qo, &q); - /* This pointer is invalidated if the window was - * remapped. May need to re-fetch it later */ - c = NULL; - if (r < 0) + /* First jump to the last match, and then find the + * next one where all matches match */ + + if (!m->matches) + return 0; + + LIST_FOREACH(matches, i, m->matches) { + uint64_t cp; + + r = find_location_for_match(j, i, f, direction, NULL, &cp); + if (r <= 0) return r; - if (r > 0) { - - if (direction == DIRECTION_DOWN) { - if (q > np) { - np = q; - npo = qo; - } - } else { - if (np == 0 || q < np) { - np = q; - npo = qo; - } - } - } + if (np == 0 || (direction == DIRECTION_DOWN ? np < cp : np > cp)) + np = cp; } - /* Check the last term */ - if (term_match && !term_result) - found = false; + return next_for_match(j, m, f, np, direction, ret, offset); + } +} - /* Did this entry match against all matches? */ - if (found) { - if (ret) { - if (c == NULL) { - /* Re-fetch the entry */ - r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c); - if (r < 0) - return r; - } - *ret = c; - } - if (offset) - *offset = cp; - return 1; +static int find_location_with_matches( + sd_journal *j, + JournalFile *f, + direction_t direction, + Object **ret, + uint64_t *offset) { + + int r; + + assert(j); + assert(f); + assert(ret); + assert(offset); + + if (!j->level0) { + /* No matches is simple */ + + if (j->current_location.type == LOCATION_HEAD) + return journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, ret, offset); + if (j->current_location.type == LOCATION_TAIL) + return journal_file_next_entry(f, NULL, 0, DIRECTION_UP, ret, offset); + if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id)) + return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset); + if (j->current_location.monotonic_set) { + r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset); + if (r != -ENOENT) + return r; } + if (j->current_location.realtime_set) + return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset); - /* Did we find a subsequent entry? */ - if (np == 0) - return 0; + return journal_file_next_entry(f, NULL, 0, direction, ret, offset); + } else + return find_location_for_match(j, j->level0, f, direction, ret, offset); +} - /* Hmm, ok, this entry only matched partially, so - * let's try another one */ - cp = np; - c = npo; - } +static int next_with_matches( + sd_journal *j, + JournalFile *f, + direction_t direction, + Object **ret, + uint64_t *offset) { + + Object *c; + uint64_t cp; + + assert(j); + assert(f); + assert(ret); + assert(offset); + + c = *ret; + cp = *offset; + + /* No matches is easy. We simple advance the file + * pointer by one. */ + if (!j->level0) + return journal_file_next_entry(f, c, cp, direction, ret, offset); + + /* If we have a match then we look for the next matching entry + * wiht an offset at least one step larger */ + return next_for_match(j, j->level0, f, direction == DIRECTION_DOWN ? cp+1 : cp-1, direction, ret, offset); } static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) { Object *c; uint64_t cp; - int compare_value, r; + int r; assert(j); assert(f); @@ -617,16 +767,18 @@ static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direc r = next_with_matches(j, f, direction, &c, &cp); if (r <= 0) return r; - - compare_value = 1; } else { - r = find_location(j, f, direction, &c, &cp); + r = find_location_with_matches(j, f, direction, &c, &cp); if (r <= 0) return r; - - compare_value = 0; } + /* OK, we found the spot, now let's advance until to an entry + * that is actually different from what we were previously + * looking at. This is necessary to handle entries which exist + * in two (or more) journal files, and which shall all be + * suppressed but one. */ + for (;;) { bool found; @@ -635,9 +787,9 @@ static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direc k = compare_with_location(f, c, &j->current_location); if (direction == DIRECTION_DOWN) - found = k >= compare_value; + found = k > 0; else - found = k <= -compare_value; + found = k < 0; } else found = true; diff --git a/src/journal/test-journal-match.c b/src/journal/test-journal-match.c new file mode 100644 index 0000000000..fa228144f5 --- /dev/null +++ b/src/journal/test-journal-match.c @@ -0,0 +1,67 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include + +#include "journal-internal.h" +#include "util.h" +#include "log.h" + +int main(int argc, char *argv[]) { + sd_journal *j; + char *t; + + log_set_max_level(LOG_DEBUG); + + assert_se(sd_journal_open(&j, 0) >= 0); + + assert_se(sd_journal_add_match(j, "foobar", 0) < 0); + assert_se(sd_journal_add_match(j, "foobar=waldo", 0) < 0); + assert_se(sd_journal_add_match(j, "", 0) < 0); + assert_se(sd_journal_add_match(j, "=", 0) < 0); + assert_se(sd_journal_add_match(j, "=xxxxx", 0) < 0); + assert_se(sd_journal_add_match(j, "HALLO=WALDO", 0) >= 0); + assert_se(sd_journal_add_match(j, "QUUX=mmmm", 0) >= 0); + assert_se(sd_journal_add_match(j, "QUUX=xxxxx", 0) >= 0); + assert_se(sd_journal_add_match(j, "HALLO=", 0) >= 0); + assert_se(sd_journal_add_match(j, "QUUX=xxxxx", 0) >= 0); + assert_se(sd_journal_add_match(j, "QUUX=yyyyy", 0) >= 0); + assert_se(sd_journal_add_match(j, "PIFF=paff", 0) >= 0); + + assert_se(sd_journal_add_disjunction(j) >= 0); + + assert_se(sd_journal_add_match(j, "ONE=one", 0) >= 0); + assert_se(sd_journal_add_match(j, "ONE=two", 0) >= 0); + assert_se(sd_journal_add_match(j, "TWO=two", 0) >= 0); + + assert_se(t = journal_make_match_string(j)); + + assert_se(streq(t, "((TWO=two AND (ONE=two OR ONE=one)) OR (PIFF=paff AND (QUUX=yyyyy OR QUUX=xxxxx OR QUUX=mmmm) AND (HALLO= OR HALLO=WALDO)))")); + + printf("resulting match expression is: %s\n", t); + free(t); + + sd_journal_close(j); + + return 0; +} diff --git a/src/journal/test-journal-send.c b/src/journal/test-journal-send.c index d682abbf01..9d376d1e56 100644 --- a/src/journal/test-journal-send.c +++ b/src/journal/test-journal-send.c @@ -21,7 +21,11 @@ #include +#include "log.h" + int main(int argc, char *argv[]) { + log_set_max_level(LOG_DEBUG); + sd_journal_print(LOG_INFO, "piepapo"); sd_journal_send("MESSAGE=foobar", diff --git a/src/journal/test-journal-stream.c b/src/journal/test-journal-stream.c new file mode 100644 index 0000000000..313606f9be --- /dev/null +++ b/src/journal/test-journal-stream.c @@ -0,0 +1,169 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include + +#include "journal-file.h" +#include "journal-internal.h" +#include "util.h" +#include "log.h" + +#define N_ENTRIES 200 + +static void verify_contents(sd_journal *j, unsigned skip) { + unsigned i; + + assert(j); + + i = 0; + SD_JOURNAL_FOREACH(j) { + const void *d; + char *k; + size_t l; + unsigned u; + + assert_se(sd_journal_get_cursor(j, &k) >= 0); + printf("cursor: %s\n", k); + free(k); + + assert_se(sd_journal_get_data(j, "MAGIC", &d, &l) >= 0); + printf("\t%.*s\n", (int) l, (const char*) d); + + assert_se(sd_journal_get_data(j, "NUMBER", &d, &l) >= 0); + assert_se(k = strndup(d, l)); + printf("\t%s\n", k); + + if (skip > 0) { + assert_se(safe_atou(k + 7, &u) >= 0); + assert_se(i == u); + i += skip; + } + + free(k); + } + + if (skip > 0) + assert_se(i == N_ENTRIES); +} + +int main(int argc, char *argv[]) { + JournalFile *one, *two, *three; + char t[] = "/tmp/journal-stream-XXXXXX"; + unsigned i; + sd_journal *j; + char *z; + + log_set_max_level(LOG_DEBUG); + + assert_se(mkdtemp(t)); + assert_se(chdir(t) >= 0); + + assert_se(journal_file_open("one.journal", O_RDWR|O_CREAT, 0666, NULL, &one) == 0); + assert_se(journal_file_open("two.journal", O_RDWR|O_CREAT, 0666, NULL, &two) == 0); + assert_se(journal_file_open("three.journal", O_RDWR|O_CREAT, 0666, NULL, &three) == 0); + + for (i = 0; i < N_ENTRIES; i++) { + char *p, *q; + dual_timestamp ts; + struct iovec iovec[2]; + + dual_timestamp_get(&ts); + + assert_se(asprintf(&p, "NUMBER=%u", i) >= 0); + iovec[0].iov_base = p; + iovec[0].iov_len = strlen(p); + + assert_se(asprintf(&q, "MAGIC=%s", i % 5 == 0 ? "quux" : "waldo") >= 0); + + iovec[1].iov_base = q; + iovec[1].iov_len = strlen(q); + + if (i % 10 == 0) + assert_se(journal_file_append_entry(three, &ts, iovec, 2, NULL, NULL, NULL) == 0); + else { + if (i % 3 == 0) + assert_se(journal_file_append_entry(two, &ts, iovec, 2, NULL, NULL, NULL) == 0); + + assert_se(journal_file_append_entry(one, &ts, iovec, 2, NULL, NULL, NULL) == 0); + } + + free(p); + free(q); + } + + journal_file_close(one); + journal_file_close(two); + journal_file_close(three); + + assert_se(sd_journal_open_directory(&j, t, 0) >= 0); + + assert_se(sd_journal_add_match(j, "MAGIC=quux", 0) >= 0); + SD_JOURNAL_FOREACH_BACKWARDS(j) { + const void *d; + size_t l; + + assert_se(sd_journal_get_data(j, "NUMBER", &d, &l) >= 0); + printf("\t%.*s\n", (int) l, (const char*) d); + } + + SD_JOURNAL_FOREACH(j) { + const void *d; + size_t l; + + assert_se(sd_journal_get_data(j, "NUMBER", &d, &l) >= 0); + printf("\t%.*s\n", (int) l, (const char*) d); + } + + sd_journal_flush_matches(j); + + verify_contents(j, 1); + + printf("NEXT TEST\n"); + assert_se(sd_journal_add_match(j, "MAGIC=quux", 0) >= 0); + + assert_se(z = journal_make_match_string(j)); + printf("resulting match expression is: %s\n", z); + free(z); + + verify_contents(j, 5); + + printf("NEXT TEST\n"); + sd_journal_flush_matches(j); + assert_se(sd_journal_add_match(j, "MAGIC=waldo", 0) >= 0); + assert_se(sd_journal_add_match(j, "NUMBER=10", 0) >= 0); + assert_se(sd_journal_add_match(j, "NUMBER=11", 0) >= 0); + assert_se(sd_journal_add_match(j, "NUMBER=12", 0) >= 0); + + assert_se(z = journal_make_match_string(j)); + printf("resulting match expression is: %s\n", z); + free(z); + + verify_contents(j, 0); + + sd_journal_close(j); + + assert_se(rm_rf(t, false, true, false) >= 0); + + return 0; +} diff --git a/src/systemd/sd-journal.h b/src/systemd/sd-journal.h index ab968b9496..74e57f48e0 100644 --- a/src/systemd/sd-journal.h +++ b/src/systemd/sd-journal.h @@ -88,6 +88,7 @@ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *l); void sd_journal_restart_data(sd_journal *j); int sd_journal_add_match(sd_journal *j, const void *data, size_t size); +int sd_journal_add_disjunction(sd_journal *j); void sd_journal_flush_matches(sd_journal *j); int sd_journal_seek_head(sd_journal *j); -- cgit v1.2.3-54-g00ecf From 69b7fedfc075f67b0c78f6fe0a1ef3a0ddb0a921 Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Tue, 17 Jul 2012 08:30:58 +0200 Subject: gitignore: tags files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index c4a7b0ddec..8928071449 100644 --- a/.gitignore +++ b/.gitignore @@ -125,3 +125,5 @@ stamp-* /v4l_id /test-libudev /test-udev +/TAGS +/tags -- cgit v1.2.3-54-g00ecf From 0284adc6a60ce0af1107cb0b50041a65d731f39e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 16 Aug 2012 01:51:54 +0200 Subject: journal: split up journal-file.c --- .gitignore | 1 + Makefile.am | 6 + src/journal/journal-authenticate.c | 434 ++++++++++ src/journal/journal-authenticate.h | 35 + src/journal/journal-file.c | 1521 +++++------------------------------- src/journal/journal-file.h | 13 +- src/journal/journal-vacuum.c | 230 ++++++ src/journal/journal-vacuum.h | 26 + src/journal/journal-verify.c | 558 +++++++++++++ src/journal/journal-verify.h | 26 + src/journal/journalctl.c | 1 + src/journal/journald.c | 1 + src/journal/test-journal-verify.c | 78 ++ src/journal/test-journal.c | 4 +- 14 files changed, 1598 insertions(+), 1336 deletions(-) create mode 100644 src/journal/journal-authenticate.c create mode 100644 src/journal/journal-authenticate.h create mode 100644 src/journal/journal-vacuum.c create mode 100644 src/journal/journal-vacuum.h create mode 100644 src/journal/journal-verify.c create mode 100644 src/journal/journal-verify.h create mode 100644 src/journal/test-journal-verify.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 8928071449..4c8bba8871 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/test-journal-verify /test-journal-match /test-journal-stream /test-unit-name diff --git a/Makefile.am b/Makefile.am index f220b59c9c..895dcfa334 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2397,6 +2397,12 @@ libsystemd_journal_la_SOURCES = \ src/systemd/sd-journal.h \ src/journal/journal-file.c \ src/journal/journal-file.h \ + src/journal/journal-vacuum.c \ + src/journal/journal-vacuum.h \ + src/journal/journal-verify.c \ + src/journal/journal-verify.h \ + src/journal/journal-authenticate.c \ + src/journal/journal-authenticate.h \ src/journal/lookup3.c \ src/journal/lookup3.h \ src/journal/journal-send.c \ diff --git a/src/journal/journal-authenticate.c b/src/journal/journal-authenticate.c new file mode 100644 index 0000000000..827e4e4fb9 --- /dev/null +++ b/src/journal/journal-authenticate.c @@ -0,0 +1,434 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "journal-def.h" +#include "journal-file.h" +#include "journal-authenticate.h" +#include "fsprg.h" + +static void *fsprg_state(JournalFile *f) { + uint64_t a, b; + assert(f); + + if (!f->authenticate) + return NULL; + + a = le64toh(f->fsprg_header->header_size); + b = le64toh(f->fsprg_header->state_size); + + if (a + b > f->fsprg_size) + return NULL; + + return (uint8_t*) f->fsprg_header + a; +} + +static uint64_t journal_file_tag_seqnum(JournalFile *f) { + uint64_t r; + + assert(f); + + r = le64toh(f->header->n_tags) + 1; + f->header->n_tags = htole64(r); + + return r; +} + +int journal_file_append_tag(JournalFile *f) { + Object *o; + uint64_t p; + int r; + + assert(f); + + if (!f->authenticate) + return 0; + + if (!f->hmac_running) + return 0; + + log_debug("Writing tag for epoch %llu\n", (unsigned long long) FSPRG_GetEpoch(fsprg_state(f))); + + assert(f->hmac); + + r = journal_file_append_object(f, OBJECT_TAG, sizeof(struct TagObject), &o, &p); + if (r < 0) + return r; + + o->tag.seqnum = htole64(journal_file_tag_seqnum(f)); + + /* Add the tag object itself, so that we can protect its + * header. This will exclude the actual hash value in it */ + r = journal_file_hmac_put_object(f, OBJECT_TAG, p); + if (r < 0) + return r; + + /* Get the HMAC tag and store it in the object */ + memcpy(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH); + f->hmac_running = false; + + return 0; +} + +static int journal_file_hmac_start(JournalFile *f) { + uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */ + + assert(f); + + if (!f->authenticate) + return 0; + + if (f->hmac_running) + return 0; + + /* Prepare HMAC for next cycle */ + gcry_md_reset(f->hmac); + FSPRG_GetKey(fsprg_state(f), key, sizeof(key), 0); + gcry_md_setkey(f->hmac, key, sizeof(key)); + + f->hmac_running = true; + + return 0; +} + +static int journal_file_get_epoch(JournalFile *f, uint64_t realtime, uint64_t *epoch) { + uint64_t t; + + assert(f); + assert(epoch); + assert(f->authenticate); + + if (le64toh(f->fsprg_header->fsprg_start_usec) == 0 || + le64toh(f->fsprg_header->fsprg_interval_usec) == 0) + return -ENOTSUP; + + if (realtime < le64toh(f->fsprg_header->fsprg_start_usec)) + return -ESTALE; + + t = realtime - le64toh(f->fsprg_header->fsprg_start_usec); + t = t / le64toh(f->fsprg_header->fsprg_interval_usec); + + *epoch = t; + return 0; +} + +static int journal_file_need_evolve(JournalFile *f, uint64_t realtime) { + uint64_t goal, epoch; + int r; + assert(f); + + if (!f->authenticate) + return 0; + + r = journal_file_get_epoch(f, realtime, &goal); + if (r < 0) + return r; + + epoch = FSPRG_GetEpoch(fsprg_state(f)); + if (epoch > goal) + return -ESTALE; + + return epoch != goal; +} + +static int journal_file_evolve(JournalFile *f, uint64_t realtime) { + uint64_t goal, epoch; + int r; + + assert(f); + + if (!f->authenticate) + return 0; + + r = journal_file_get_epoch(f, realtime, &goal); + if (r < 0) + return r; + + epoch = FSPRG_GetEpoch(fsprg_state(f)); + if (epoch < goal) + log_debug("Evolving FSPRG key from epoch %llu to %llu.", (unsigned long long) epoch, (unsigned long long) goal); + + for (;;) { + if (epoch > goal) + return -ESTALE; + if (epoch == goal) + return 0; + + FSPRG_Evolve(fsprg_state(f)); + epoch = FSPRG_GetEpoch(fsprg_state(f)); + } +} + +int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) { + int r; + + assert(f); + + if (!f->authenticate) + return 0; + + r = journal_file_need_evolve(f, realtime); + if (r <= 0) + return 0; + + r = journal_file_append_tag(f); + if (r < 0) + return r; + + r = journal_file_evolve(f, realtime); + if (r < 0) + return r; + + r = journal_file_hmac_start(f); + if (r < 0) + return r; + + return 0; +} + +int journal_file_hmac_put_object(JournalFile *f, int type, uint64_t p) { + int r; + Object *o; + + assert(f); + + if (!f->authenticate) + return 0; + + r = journal_file_hmac_start(f); + if (r < 0) + return r; + + r = journal_file_move_to_object(f, type, p, &o); + if (r < 0) + return r; + + gcry_md_write(f->hmac, o, offsetof(ObjectHeader, payload)); + + switch (o->object.type) { + + case OBJECT_DATA: + /* All but: hash and payload are mutable */ + gcry_md_write(f->hmac, &o->data.hash, sizeof(o->data.hash)); + gcry_md_write(f->hmac, o->data.payload, le64toh(o->object.size) - offsetof(DataObject, payload)); + break; + + case OBJECT_ENTRY: + /* All */ + gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(EntryObject, seqnum)); + break; + + case OBJECT_FIELD_HASH_TABLE: + case OBJECT_DATA_HASH_TABLE: + case OBJECT_ENTRY_ARRAY: + /* Nothing: everything is mutable */ + break; + + case OBJECT_TAG: + /* All but the tag itself */ + gcry_md_write(f->hmac, &o->tag.seqnum, sizeof(o->tag.seqnum)); + break; + default: + return -EINVAL; + } + + return 0; +} + +int journal_file_hmac_put_header(JournalFile *f) { + int r; + + assert(f); + + if (!f->authenticate) + return 0; + + r = journal_file_hmac_start(f); + if (r < 0) + return r; + + /* All but state+reserved, boot_id, arena_size, + * tail_object_offset, n_objects, n_entries, tail_seqnum, + * head_entry_realtime, tail_entry_realtime, + * tail_entry_monotonic, n_data, n_fields, header_tag */ + + gcry_md_write(f->hmac, f->header->signature, offsetof(Header, state) - offsetof(Header, signature)); + gcry_md_write(f->hmac, &f->header->file_id, offsetof(Header, boot_id) - offsetof(Header, file_id)); + gcry_md_write(f->hmac, &f->header->seqnum_id, offsetof(Header, arena_size) - offsetof(Header, seqnum_id)); + gcry_md_write(f->hmac, &f->header->data_hash_table_offset, offsetof(Header, tail_object_offset) - offsetof(Header, data_hash_table_offset)); + gcry_md_write(f->hmac, &f->header->head_entry_seqnum, offsetof(Header, head_entry_realtime) - offsetof(Header, head_entry_seqnum)); + + return 0; +} + +int journal_file_load_fsprg(JournalFile *f) { + int r, fd = -1; + char *p = NULL; + struct stat st; + FSPRGHeader *m = NULL; + sd_id128_t machine; + + assert(f); + + if (!f->authenticate) + return 0; + + r = sd_id128_get_machine(&machine); + if (r < 0) + return r; + + if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fsprg", + SD_ID128_FORMAT_VAL(machine)) < 0) + return -ENOMEM; + + fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY, 0600); + if (fd < 0) { + log_error("Failed to open %s: %m", p); + r = -errno; + goto finish; + } + + if (fstat(fd, &st) < 0) { + r = -errno; + goto finish; + } + + if (st.st_size < (off_t) sizeof(FSPRGHeader)) { + r = -ENODATA; + goto finish; + } + + m = mmap(NULL, PAGE_ALIGN(sizeof(FSPRGHeader)), PROT_READ, MAP_SHARED, fd, 0); + if (m == MAP_FAILED) { + m = NULL; + r = -errno; + goto finish; + } + + if (memcmp(m->signature, FSPRG_HEADER_SIGNATURE, 8) != 0) { + r = -EBADMSG; + goto finish; + } + + if (m->incompatible_flags != 0) { + r = -EPROTONOSUPPORT; + goto finish; + } + + if (le64toh(m->header_size) < sizeof(FSPRGHeader)) { + r = -EBADMSG; + goto finish; + } + + if (le64toh(m->state_size) != FSPRG_stateinbytes(m->secpar)) { + r = -EBADMSG; + goto finish; + } + + f->fsprg_size = le64toh(m->header_size) + le64toh(m->state_size); + if ((uint64_t) st.st_size < f->fsprg_size) { + r = -ENODATA; + goto finish; + } + + if (!sd_id128_equal(machine, m->machine_id)) { + r = -EHOSTDOWN; + goto finish; + } + + if (le64toh(m->fsprg_start_usec) <= 0 || + le64toh(m->fsprg_interval_usec) <= 0) { + r = -EBADMSG; + goto finish; + } + + f->fsprg_header = mmap(NULL, PAGE_ALIGN(f->fsprg_size), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (f->fsprg_header == MAP_FAILED) { + f->fsprg_header = NULL; + r = -errno; + goto finish; + } + + r = 0; + +finish: + if (m) + munmap(m, PAGE_ALIGN(sizeof(FSPRGHeader))); + + if (fd >= 0) + close_nointr_nofail(fd); + + free(p); + return r; +} + +int journal_file_setup_hmac(JournalFile *f) { + gcry_error_t e; + + if (!f->authenticate) + return 0; + + e = gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC); + if (e != 0) + return -ENOTSUP; + + return 0; +} + +int journal_file_append_first_tag(JournalFile *f) { + int r; + uint64_t p; + + if (!f->authenticate) + return 0; + + log_debug("Calculating first tag..."); + + r = journal_file_hmac_put_header(f); + if (r < 0) + return r; + + p = le64toh(f->header->field_hash_table_offset); + if (p < offsetof(Object, hash_table.items)) + return -EINVAL; + p -= offsetof(Object, hash_table.items); + + r = journal_file_hmac_put_object(f, OBJECT_FIELD_HASH_TABLE, p); + if (r < 0) + return r; + + p = le64toh(f->header->data_hash_table_offset); + if (p < offsetof(Object, hash_table.items)) + return -EINVAL; + p -= offsetof(Object, hash_table.items); + + r = journal_file_hmac_put_object(f, OBJECT_DATA_HASH_TABLE, p); + if (r < 0) + return r; + + r = journal_file_append_tag(f); + if (r < 0) + return r; + + return 0; +} diff --git a/src/journal/journal-authenticate.h b/src/journal/journal-authenticate.h new file mode 100644 index 0000000000..c991b22e4a --- /dev/null +++ b/src/journal/journal-authenticate.h @@ -0,0 +1,35 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "journal-file.h" + +int journal_file_append_tag(JournalFile *f); +int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime); +int journal_file_append_first_tag(JournalFile *f); + +int journal_file_hmac_put_header(JournalFile *f); +int journal_file_hmac_put_object(JournalFile *f, int type, uint64_t p); + +int journal_file_load_fsprg(JournalFile *f); + +int journal_file_setup_hmac(JournalFile *f); diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index 7beedb4a25..ff439f2474 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -29,6 +29,7 @@ #include "journal-def.h" #include "journal-file.h" +#include "journal-authenticate.h" #include "lookup3.h" #include "compress.h" #include "fsprg.h" @@ -60,14 +61,6 @@ /* n_data was the first entry we added after the initial file format design */ #define HEADER_SIZE_MIN ALIGN64(offsetof(Header, n_data)) -#define ALIGN64(x) (((x) + 7ULL) & ~7ULL) - -#define JOURNAL_HEADER_CONTAINS(h, field) \ - (le64toh((h)->header_size) >= offsetof(Header, field) + sizeof((h)->field)) - -static int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime); -static int journal_file_hmac_put_object(JournalFile *f, int type, uint64_t p); - void journal_file_close(JournalFile *f) { assert(f); @@ -434,7 +427,7 @@ static uint64_t journal_file_entry_seqnum(JournalFile *f, uint64_t *seqnum) { return r; } -static int journal_file_append_object(JournalFile *f, int type, uint64_t size, Object **ret, uint64_t *offset) { +int journal_file_append_object(JournalFile *f, int type, uint64_t size, Object **ret, uint64_t *offset) { int r; uint64_t p; Object *tail, *o; @@ -796,7 +789,7 @@ uint64_t journal_file_entry_n_items(Object *o) { return (le64toh(o->object.size) - offsetof(Object, entry.items)) / sizeof(EntryItem); } -static uint64_t journal_file_entry_array_n_items(Object *o) { +uint64_t journal_file_entry_array_n_items(Object *o) { assert(o); assert(o->object.type == OBJECT_ENTRY_ARRAY); @@ -1823,1174 +1816,241 @@ int journal_file_move_to_entry_by_realtime_for_data( ret, offset, NULL); } -static void *fsprg_state(JournalFile *f) { - uint64_t a, b; - assert(f); - - if (!f->authenticate) - return NULL; - - a = le64toh(f->fsprg_header->header_size); - b = le64toh(f->fsprg_header->state_size); - - if (a + b > f->fsprg_size) - return NULL; - - return (uint8_t*) f->fsprg_header + a; -} - -static uint64_t journal_file_tag_seqnum(JournalFile *f) { - uint64_t r; - - assert(f); - - r = le64toh(f->header->n_tags) + 1; - f->header->n_tags = htole64(r); - - return r; -} - -int journal_file_append_tag(JournalFile *f) { +void journal_file_dump(JournalFile *f) { Object *o; - uint64_t p; int r; + uint64_t p; assert(f); - if (!f->authenticate) - return 0; - - if (!f->hmac_running) - return 0; - - log_debug("Writing tag for epoch %llu\n", (unsigned long long) FSPRG_GetEpoch(fsprg_state(f))); - - assert(f->hmac); + journal_file_print_header(f); - r = journal_file_append_object(f, OBJECT_TAG, sizeof(struct TagObject), &o, &p); - if (r < 0) - return r; + p = le64toh(f->header->header_size); + while (p != 0) { + r = journal_file_move_to_object(f, -1, p, &o); + if (r < 0) + goto fail; - o->tag.seqnum = htole64(journal_file_tag_seqnum(f)); + switch (o->object.type) { - /* Add the tag object itself, so that we can protect its - * header. This will exclude the actual hash value in it */ - r = journal_file_hmac_put_object(f, OBJECT_TAG, p); - if (r < 0) - return r; + case OBJECT_UNUSED: + printf("Type: OBJECT_UNUSED\n"); + break; - /* Get the HMAC tag and store it in the object */ - memcpy(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH); - f->hmac_running = false; + case OBJECT_DATA: + printf("Type: OBJECT_DATA\n"); + break; - return 0; -} + case OBJECT_ENTRY: + printf("Type: OBJECT_ENTRY %llu %llu %llu\n", + (unsigned long long) le64toh(o->entry.seqnum), + (unsigned long long) le64toh(o->entry.monotonic), + (unsigned long long) le64toh(o->entry.realtime)); + break; -static int journal_file_hmac_start(JournalFile *f) { - uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */ + case OBJECT_FIELD_HASH_TABLE: + printf("Type: OBJECT_FIELD_HASH_TABLE\n"); + break; - assert(f); + case OBJECT_DATA_HASH_TABLE: + printf("Type: OBJECT_DATA_HASH_TABLE\n"); + break; - if (!f->authenticate) - return 0; + case OBJECT_ENTRY_ARRAY: + printf("Type: OBJECT_ENTRY_ARRAY\n"); + break; - if (f->hmac_running) - return 0; + case OBJECT_TAG: + printf("Type: OBJECT_TAG %llu\n", + (unsigned long long) le64toh(o->tag.seqnum)); + break; + } - /* Prepare HMAC for next cycle */ - gcry_md_reset(f->hmac); - FSPRG_GetKey(fsprg_state(f), key, sizeof(key), 0); - gcry_md_setkey(f->hmac, key, sizeof(key)); + if (o->object.flags & OBJECT_COMPRESSED) + printf("Flags: COMPRESSED\n"); - f->hmac_running = true; + if (p == le64toh(f->header->tail_object_offset)) + p = 0; + else + p = p + ALIGN64(le64toh(o->object.size)); + } - return 0; + return; +fail: + log_error("File corrupt"); } -static int journal_file_get_epoch(JournalFile *f, uint64_t realtime, uint64_t *epoch) { - uint64_t t; +void journal_file_print_header(JournalFile *f) { + char a[33], b[33], c[33]; + char x[FORMAT_TIMESTAMP_MAX], y[FORMAT_TIMESTAMP_MAX]; assert(f); - assert(epoch); - assert(f->authenticate); - if (le64toh(f->fsprg_header->fsprg_start_usec) == 0 || - le64toh(f->fsprg_header->fsprg_interval_usec) == 0) - return -ENOTSUP; - - if (realtime < le64toh(f->fsprg_header->fsprg_start_usec)) - return -ESTALE; + printf("File Path: %s\n" + "File ID: %s\n" + "Machine ID: %s\n" + "Boot ID: %s\n" + "Sequential Number ID: %s\n" + "State: %s\n" + "Compatible Flags:%s%s\n" + "Incompatible Flags:%s%s\n" + "Header size: %llu\n" + "Arena size: %llu\n" + "Data Hash Table Size: %llu\n" + "Field Hash Table Size: %llu\n" + "Objects: %llu\n" + "Entry Objects: %llu\n" + "Rotate Suggested: %s\n" + "Head Sequential Number: %llu\n" + "Tail Sequential Number: %llu\n" + "Head Realtime Timestamp: %s\n" + "Tail Realtime Timestamp: %s\n", + f->path, + sd_id128_to_string(f->header->file_id, a), + sd_id128_to_string(f->header->machine_id, b), + sd_id128_to_string(f->header->boot_id, c), + sd_id128_to_string(f->header->seqnum_id, c), + f->header->state == STATE_OFFLINE ? "offline" : + f->header->state == STATE_ONLINE ? "online" : + f->header->state == STATE_ARCHIVED ? "archived" : "unknown", + (f->header->compatible_flags & HEADER_COMPATIBLE_AUTHENTICATED) ? " AUTHENTICATED" : "", + (f->header->compatible_flags & ~HEADER_COMPATIBLE_AUTHENTICATED) ? " ???" : "", + (f->header->incompatible_flags & HEADER_INCOMPATIBLE_COMPRESSED) ? " COMPRESSED" : "", + (f->header->incompatible_flags & ~HEADER_INCOMPATIBLE_COMPRESSED) ? " ???" : "", + (unsigned long long) le64toh(f->header->header_size), + (unsigned long long) le64toh(f->header->arena_size), + (unsigned long long) le64toh(f->header->data_hash_table_size) / sizeof(HashItem), + (unsigned long long) le64toh(f->header->field_hash_table_size) / sizeof(HashItem), + (unsigned long long) le64toh(f->header->n_objects), + (unsigned long long) le64toh(f->header->n_entries), + yes_no(journal_file_rotate_suggested(f)), + (unsigned long long) le64toh(f->header->head_entry_seqnum), + (unsigned long long) le64toh(f->header->tail_entry_seqnum), + format_timestamp(x, sizeof(x), le64toh(f->header->head_entry_realtime)), + format_timestamp(y, sizeof(y), le64toh(f->header->tail_entry_realtime))); - t = realtime - le64toh(f->fsprg_header->fsprg_start_usec); - t = t / le64toh(f->fsprg_header->fsprg_interval_usec); + if (JOURNAL_HEADER_CONTAINS(f->header, n_data)) + printf("Data Objects: %llu\n" + "Data Hash Table Fill: %.1f%%\n", + (unsigned long long) le64toh(f->header->n_data), + 100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)))); - *epoch = t; - return 0; + if (JOURNAL_HEADER_CONTAINS(f->header, n_fields)) + printf("Field Objects: %llu\n" + "Field Hash Table Fill: %.1f%%\n", + (unsigned long long) le64toh(f->header->n_fields), + 100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)))); } -static int journal_file_need_evolve(JournalFile *f, uint64_t realtime) { - uint64_t goal, epoch; - int r; - assert(f); - - if (!f->authenticate) - return 0; +int journal_file_open( + const char *fname, + int flags, + mode_t mode, + bool compress, + bool authenticate, + JournalMetrics *metrics, + MMapCache *mmap_cache, + JournalFile *template, + JournalFile **ret) { - r = journal_file_get_epoch(f, realtime, &goal); - if (r < 0) - return r; + JournalFile *f; + int r; + bool newly_created = false; - epoch = FSPRG_GetEpoch(fsprg_state(f)); - if (epoch > goal) - return -ESTALE; + assert(fname); - return epoch != goal; -} + if ((flags & O_ACCMODE) != O_RDONLY && + (flags & O_ACCMODE) != O_RDWR) + return -EINVAL; -static int journal_file_evolve(JournalFile *f, uint64_t realtime) { - uint64_t goal, epoch; - int r; + if (!endswith(fname, ".journal")) + return -EINVAL; - assert(f); + f = new0(JournalFile, 1); + if (!f) + return -ENOMEM; - if (!f->authenticate) - return 0; + f->fd = -1; + f->mode = mode; - r = journal_file_get_epoch(f, realtime, &goal); - if (r < 0) - return r; + f->flags = flags; + f->prot = prot_from_flags(flags); + f->writable = (flags & O_ACCMODE) != O_RDONLY; + f->compress = compress; + f->authenticate = authenticate; - epoch = FSPRG_GetEpoch(fsprg_state(f)); - if (epoch < goal) - log_debug("Evolving FSPRG key from epoch %llu to %llu.", (unsigned long long) epoch, (unsigned long long) goal); + if (mmap_cache) + f->mmap = mmap_cache_ref(mmap_cache); + else { + /* One context for each type, plus the zeroth catchall + * context. One fd for the file plus one for each type + * (which we need during verification */ + f->mmap = mmap_cache_new(_OBJECT_TYPE_MAX, 1 + _OBJECT_TYPE_MAX); + if (!f->mmap) { + r = -ENOMEM; + goto fail; + } + } - for (;;) { - if (epoch > goal) - return -ESTALE; - if (epoch == goal) - return 0; + f->path = strdup(fname); + if (!f->path) { + r = -ENOMEM; + goto fail; + } - FSPRG_Evolve(fsprg_state(f)); - epoch = FSPRG_GetEpoch(fsprg_state(f)); + f->fd = open(f->path, f->flags|O_CLOEXEC, f->mode); + if (f->fd < 0) { + r = -errno; + goto fail; } -} -static int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) { - int r; + if (fstat(f->fd, &f->last_stat) < 0) { + r = -errno; + goto fail; + } - assert(f); + if (f->last_stat.st_size == 0 && f->writable) { + newly_created = true; - if (!f->authenticate) - return 0; + /* Try to load the FSPRG state, and if we can't, then + * just don't do authentication */ + r = journal_file_load_fsprg(f); + if (r < 0) + f->authenticate = false; - r = journal_file_need_evolve(f, realtime); - if (r <= 0) - return 0; + r = journal_file_init_header(f, template); + if (r < 0) + goto fail; - r = journal_file_append_tag(f); - if (r < 0) - return r; + if (fstat(f->fd, &f->last_stat) < 0) { + r = -errno; + goto fail; + } + } - r = journal_file_evolve(f, realtime); - if (r < 0) - return r; + if (f->last_stat.st_size < (off_t) HEADER_SIZE_MIN) { + r = -EIO; + goto fail; + } - r = journal_file_hmac_start(f); - if (r < 0) - return r; + f->header = mmap(NULL, PAGE_ALIGN(sizeof(Header)), prot_from_flags(flags), MAP_SHARED, f->fd, 0); + if (f->header == MAP_FAILED) { + f->header = NULL; + r = -errno; + goto fail; + } - return 0; -} + if (!newly_created) { + r = journal_file_verify_header(f); + if (r < 0) + goto fail; + } -static int journal_file_hmac_put_object(JournalFile *f, int type, uint64_t p) { - int r; - Object *o; - - assert(f); - - if (!f->authenticate) - return 0; - - r = journal_file_hmac_start(f); - if (r < 0) - return r; - - r = journal_file_move_to_object(f, type, p, &o); - if (r < 0) - return r; - - gcry_md_write(f->hmac, o, offsetof(ObjectHeader, payload)); - - switch (o->object.type) { - - case OBJECT_DATA: - /* All but: hash and payload are mutable */ - gcry_md_write(f->hmac, &o->data.hash, sizeof(o->data.hash)); - gcry_md_write(f->hmac, o->data.payload, le64toh(o->object.size) - offsetof(DataObject, payload)); - break; - - case OBJECT_ENTRY: - /* All */ - gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(EntryObject, seqnum)); - break; - - case OBJECT_FIELD_HASH_TABLE: - case OBJECT_DATA_HASH_TABLE: - case OBJECT_ENTRY_ARRAY: - /* Nothing: everything is mutable */ - break; - - case OBJECT_TAG: - /* All but the tag itself */ - gcry_md_write(f->hmac, &o->tag.seqnum, sizeof(o->tag.seqnum)); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int journal_file_hmac_put_header(JournalFile *f) { - int r; - - assert(f); - - if (!f->authenticate) - return 0; - - r = journal_file_hmac_start(f); - if (r < 0) - return r; - - /* All but state+reserved, boot_id, arena_size, - * tail_object_offset, n_objects, n_entries, tail_seqnum, - * head_entry_realtime, tail_entry_realtime, - * tail_entry_monotonic, n_data, n_fields, header_tag */ - - gcry_md_write(f->hmac, f->header->signature, offsetof(Header, state) - offsetof(Header, signature)); - gcry_md_write(f->hmac, &f->header->file_id, offsetof(Header, boot_id) - offsetof(Header, file_id)); - gcry_md_write(f->hmac, &f->header->seqnum_id, offsetof(Header, arena_size) - offsetof(Header, seqnum_id)); - gcry_md_write(f->hmac, &f->header->data_hash_table_offset, offsetof(Header, tail_object_offset) - offsetof(Header, data_hash_table_offset)); - gcry_md_write(f->hmac, &f->header->head_entry_seqnum, offsetof(Header, head_entry_realtime) - offsetof(Header, head_entry_seqnum)); - - return 0; -} - -static int journal_file_load_fsprg(JournalFile *f) { - int r, fd = -1; - char *p = NULL; - struct stat st; - FSPRGHeader *m = NULL; - sd_id128_t machine; - - assert(f); - - if (!f->authenticate) - return 0; - - r = sd_id128_get_machine(&machine); - if (r < 0) - return r; - - if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fsprg", - SD_ID128_FORMAT_VAL(machine)) < 0) - return -ENOMEM; - - fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY, 0600); - if (fd < 0) { - log_error("Failed to open %s: %m", p); - r = -errno; - goto finish; - } - - if (fstat(fd, &st) < 0) { - r = -errno; - goto finish; - } - - if (st.st_size < (off_t) sizeof(FSPRGHeader)) { - r = -ENODATA; - goto finish; - } - - m = mmap(NULL, PAGE_ALIGN(sizeof(FSPRGHeader)), PROT_READ, MAP_SHARED, fd, 0); - if (m == MAP_FAILED) { - m = NULL; - r = -errno; - goto finish; - } - - if (memcmp(m->signature, FSPRG_HEADER_SIGNATURE, 8) != 0) { - r = -EBADMSG; - goto finish; - } - - if (m->incompatible_flags != 0) { - r = -EPROTONOSUPPORT; - goto finish; - } - - if (le64toh(m->header_size) < sizeof(FSPRGHeader)) { - r = -EBADMSG; - goto finish; - } - - if (le64toh(m->state_size) != FSPRG_stateinbytes(m->secpar)) { - r = -EBADMSG; - goto finish; - } - - f->fsprg_size = le64toh(m->header_size) + le64toh(m->state_size); - if ((uint64_t) st.st_size < f->fsprg_size) { - r = -ENODATA; - goto finish; - } - - if (!sd_id128_equal(machine, m->machine_id)) { - r = -EHOSTDOWN; - goto finish; - } - - if (le64toh(m->fsprg_start_usec) <= 0 || - le64toh(m->fsprg_interval_usec) <= 0) { - r = -EBADMSG; - goto finish; - } - - f->fsprg_header = mmap(NULL, PAGE_ALIGN(f->fsprg_size), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); - if (f->fsprg_header == MAP_FAILED) { - f->fsprg_header = NULL; - r = -errno; - goto finish; - } - - r = 0; - -finish: - if (m) - munmap(m, PAGE_ALIGN(sizeof(FSPRGHeader))); - - if (fd >= 0) - close_nointr_nofail(fd); - - free(p); - return r; -} - -static int journal_file_setup_hmac(JournalFile *f) { - gcry_error_t e; - - if (!f->authenticate) - return 0; - - e = gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC); - if (e != 0) - return -ENOTSUP; - - return 0; -} - -static int journal_file_append_first_tag(JournalFile *f) { - int r; - uint64_t p; - - if (!f->authenticate) - return 0; - - log_debug("Calculating first tag..."); - - r = journal_file_hmac_put_header(f); - if (r < 0) - return r; - - p = le64toh(f->header->field_hash_table_offset); - if (p < offsetof(Object, hash_table.items)) - return -EINVAL; - p -= offsetof(Object, hash_table.items); - - r = journal_file_hmac_put_object(f, OBJECT_FIELD_HASH_TABLE, p); - if (r < 0) - return r; - - p = le64toh(f->header->data_hash_table_offset); - if (p < offsetof(Object, hash_table.items)) - return -EINVAL; - p -= offsetof(Object, hash_table.items); - - r = journal_file_hmac_put_object(f, OBJECT_DATA_HASH_TABLE, p); - if (r < 0) - return r; - - r = journal_file_append_tag(f); - if (r < 0) - return r; - - return 0; -} - -static int journal_file_object_verify(JournalFile *f, Object *o) { - assert(f); - assert(o); - - /* This does various superficial tests about the length an - * possible field values. It does not follow any references to - * other objects. */ - - switch (o->object.type) { - case OBJECT_DATA: - if (le64toh(o->data.entry_offset) <= 0 || - le64toh(o->data.n_entries) <= 0) - return -EBADMSG; - - if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0) - return -EBADMSG; - break; - - case OBJECT_FIELD: - if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0) - return -EBADMSG; - break; - - case OBJECT_ENTRY: - if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0) - return -EBADMSG; - - if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0) - return -EBADMSG; - - if (le64toh(o->entry.seqnum) <= 0 || - le64toh(o->entry.realtime) <= 0) - return -EBADMSG; - - break; - - case OBJECT_DATA_HASH_TABLE: - case OBJECT_FIELD_HASH_TABLE: - if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0) - return -EBADMSG; - - break; - - case OBJECT_ENTRY_ARRAY: - if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0) - return -EBADMSG; - - break; - - case OBJECT_TAG: - if (le64toh(o->object.size) != sizeof(TagObject)) - return -EBADMSG; - break; - } - - return 0; -} - -static void draw_progress(uint64_t p, usec_t *last_usec) { - unsigned n, i, j, k; - usec_t z, x; - - if (!isatty(STDOUT_FILENO)) - return; - - z = now(CLOCK_MONOTONIC); - x = *last_usec; - - if (x != 0 && x + 40 * USEC_PER_MSEC > z) - return; - - *last_usec = z; - - n = (3 * columns()) / 4; - j = (n * (unsigned) p) / 65535ULL; - k = n - j; - - fputs("\r\x1B[?25l", stdout); - - for (i = 0; i < j; i++) - fputs("\xe2\x96\x88", stdout); - - for (i = 0; i < k; i++) - fputs("\xe2\x96\x91", stdout); - - printf(" %3lu%%", 100LU * (unsigned long) p / 65535LU); - - fputs("\r\x1B[?25h", stdout); - fflush(stdout); -} - -static void flush_progress(void) { - unsigned n, i; - - if (!isatty(STDOUT_FILENO)) - return; - - n = (3 * columns()) / 4; - - putchar('\r'); - - for (i = 0; i < n + 5; i++) - putchar(' '); - - putchar('\r'); - fflush(stdout); -} - -static int write_uint64(int fd, uint64_t p) { - ssize_t k; - - k = write(fd, &p, sizeof(p)); - if (k < 0) - return -errno; - if (k != sizeof(p)) - return -EIO; - - return 0; -} - -static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) { - uint64_t a, b; - int r; - - assert(m); - assert(fd >= 0); - - /* Bisection ... */ - - a = 0; b = n; - while (a < b) { - uint64_t c, *z; - - c = (a + b) / 2; - - r = mmap_cache_get(m, fd, PROT_READ, 0, c * sizeof(uint64_t), sizeof(uint64_t), (void **) &z); - if (r < 0) - return r; - - if (*z == p) - return 1; - - if (p < *z) - b = c; - else - a = c; - } - - return 0; -} - -int journal_file_verify(JournalFile *f, const char *key) { - int r; - Object *o; - uint64_t p = 0; - uint64_t tag_seqnum = 0, entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0; - sd_id128_t entry_boot_id; - bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false; - uint64_t n_weird = 0, n_objects = 0, n_entries = 0, n_data = 0, n_fields = 0, n_data_hash_tables = 0, n_field_hash_tables = 0, n_entry_arrays = 0; - usec_t last_usec = 0; - int data_fd = -1, entry_fd = -1, entry_array_fd = -1; - char data_path[] = "/var/tmp/journal-data-XXXXXX", - entry_path[] = "/var/tmp/journal-entry-XXXXXX", - entry_array_path[] = "/var/tmp/journal-entry-array-XXXXXX"; - - assert(f); - - data_fd = mkostemp(data_path, O_CLOEXEC); - if (data_fd < 0) { - log_error("Failed to create data file: %m"); - goto fail; - } - unlink(data_path); - - entry_fd = mkostemp(entry_path, O_CLOEXEC); - if (entry_fd < 0) { - log_error("Failed to create entry file: %m"); - goto fail; - } - unlink(entry_path); - - entry_array_fd = mkostemp(entry_array_path, O_CLOEXEC); - if (entry_array_fd < 0) { - log_error("Failed to create entry array file: %m"); - goto fail; - } - unlink(entry_array_path); - - /* First iteration: we go through all objects, verify the - * superficial structure, headers, hashes. */ - - r = journal_file_hmac_put_header(f); - if (r < 0) { - log_error("Failed to calculate HMAC of header."); - goto fail; - } - - p = le64toh(f->header->header_size); - while (p != 0) { - draw_progress((0x7FFF * p) / le64toh(f->header->tail_object_offset), &last_usec); - - r = journal_file_move_to_object(f, -1, p, &o); - if (r < 0) { - log_error("Invalid object at %llu", (unsigned long long) p); - goto fail; - } - - if (le64toh(f->header->tail_object_offset) < p) { - log_error("Invalid tail object pointer."); - r = -EBADMSG; - goto fail; - } - - n_objects ++; - - r = journal_file_object_verify(f, o); - if (r < 0) { - log_error("Invalid object contents at %llu", (unsigned long long) p); - goto fail; - } - - r = journal_file_hmac_put_object(f, -1, p); - if (r < 0) { - log_error("Failed to calculate HMAC at %llu", (unsigned long long) p); - goto fail; - } - - if (o->object.flags & OBJECT_COMPRESSED && - !(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED)) { - log_error("Compressed object without compression at %llu", (unsigned long long) p); - r = -EBADMSG; - goto fail; - } - - if (o->object.flags & OBJECT_COMPRESSED && - o->object.type != OBJECT_DATA) { - log_error("Compressed non-data object at %llu", (unsigned long long) p); - r = -EBADMSG; - goto fail; - } - - if (o->object.type == OBJECT_TAG) { - - if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED)) { - log_error("Tag object without authentication at %llu", (unsigned long long) p); - r = -EBADMSG; - goto fail; - } - - if (le64toh(o->tag.seqnum) != tag_seqnum) { - log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p); - r = -EBADMSG; - goto fail; - } - - } else if (o->object.type == OBJECT_ENTRY) { - - r = write_uint64(entry_fd, p); - if (r < 0) - goto fail; - - if (!entry_seqnum_set && - le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) { - log_error("Head entry sequence number incorrect"); - r = -EBADMSG; - goto fail; - } - - if (entry_seqnum_set && - entry_seqnum >= le64toh(o->entry.seqnum)) { - log_error("Entry sequence number out of synchronization at %llu", (unsigned long long) p); - r = -EBADMSG; - goto fail; - } - - entry_seqnum = le64toh(o->entry.seqnum); - entry_seqnum_set = true; - - if (entry_monotonic_set && - sd_id128_equal(entry_boot_id, o->entry.boot_id) && - entry_monotonic > le64toh(o->entry.monotonic)) { - log_error("Entry timestamp out of synchronization at %llu", (unsigned long long) p); - r = -EBADMSG; - goto fail; - } - - entry_monotonic = le64toh(o->entry.monotonic); - entry_boot_id = o->entry.boot_id; - entry_monotonic_set = true; - - if (!entry_realtime_set && - le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) { - log_error("Head entry realtime timestamp incorrect"); - r = -EBADMSG; - goto fail; - } - - entry_realtime = le64toh(o->entry.realtime); - entry_realtime_set = true; - - n_entries ++; - } else if (o->object.type == OBJECT_ENTRY_ARRAY) { - - r = write_uint64(entry_array_fd, p); - if (r < 0) - goto fail; - - if (p == le64toh(f->header->entry_array_offset)) { - if (found_main_entry_array) { - log_error("More than one main entry array at %llu", (unsigned long long) p); - r = -EBADMSG; - goto fail; - } - - found_main_entry_array = true; - } - - n_entry_arrays++; - - } else if (o->object.type == OBJECT_DATA) { - - r = write_uint64(data_fd, p); - if (r < 0) - goto fail; - - n_data++; - - } else if (o->object.type == OBJECT_FIELD) - n_fields++; - else if (o->object.type == OBJECT_DATA_HASH_TABLE) { - n_data_hash_tables++; - - if (n_data_hash_tables > 1) { - log_error("More than one data hash table at %llu", (unsigned long long) p); - r = -EBADMSG; - goto fail; - } - - if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) || - le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) { - log_error("Header fields for data hash table invalid."); - r = -EBADMSG; - goto fail; - } - } else if (o->object.type == OBJECT_FIELD_HASH_TABLE) { - n_field_hash_tables++; - - if (n_field_hash_tables > 1) { - log_error("More than one field hash table at %llu", (unsigned long long) p); - r = -EBADMSG; - goto fail; - } - - if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) || - le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) { - log_error("Header fields for field hash table invalid."); - r = -EBADMSG; - goto fail; - } - } else if (o->object.type >= _OBJECT_TYPE_MAX) - n_weird ++; - - if (p == le64toh(f->header->tail_object_offset)) - p = 0; - else - p = p + ALIGN64(le64toh(o->object.size)); - } - - if (n_objects != le64toh(f->header->n_objects)) { - log_error("Object number mismatch"); - r = -EBADMSG; - goto fail; - } - - if (n_entries != le64toh(f->header->n_entries)) { - log_error("Entry number mismatch"); - r = -EBADMSG; - goto fail; - } - - if (JOURNAL_HEADER_CONTAINS(f->header, n_data) && - n_data != le64toh(f->header->n_data)) { - log_error("Data number mismatch"); - r = -EBADMSG; - goto fail; - } - - if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) && - n_fields != le64toh(f->header->n_fields)) { - log_error("Field number mismatch"); - r = -EBADMSG; - goto fail; - } - - if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) && - tag_seqnum != le64toh(f->header->n_tags)) { - log_error("Tag number mismatch"); - r = -EBADMSG; - goto fail; - } - - if (n_data_hash_tables != 1) { - log_error("Missing data hash table"); - r = -EBADMSG; - goto fail; - } - - if (n_field_hash_tables != 1) { - log_error("Missing field hash table"); - r = -EBADMSG; - goto fail; - } - - if (!found_main_entry_array) { - log_error("Missing entry array"); - r = -EBADMSG; - goto fail; - } - - if (entry_seqnum_set && - entry_seqnum != le64toh(f->header->tail_entry_seqnum)) { - log_error("Invalid tail seqnum"); - r = -EBADMSG; - goto fail; - } - - if (entry_monotonic_set && - (!sd_id128_equal(entry_boot_id, f->header->boot_id) || - entry_monotonic != le64toh(f->header->tail_entry_monotonic))) { - log_error("Invalid tail monotonic timestamp"); - r = -EBADMSG; - goto fail; - } - - if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) { - log_error("Invalid tail realtime timestamp"); - r = -EBADMSG; - goto fail; - } - - /* Second iteration: we go through all objects again, this - * time verify all pointers. */ - - p = le64toh(f->header->header_size); - while (p != 0) { - draw_progress(0x8000 + (0x7FFF * p) / le64toh(f->header->tail_object_offset), &last_usec); - - r = journal_file_move_to_object(f, -1, p, &o); - if (r < 0) { - log_error("Invalid object at %llu", (unsigned long long) p); - goto fail; - } - - if (o->object.type == OBJECT_ENTRY_ARRAY) { - uint64_t i = 0, n; - - if (le64toh(o->entry_array.next_entry_array_offset) != 0 && - !contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, le64toh(o->entry_array.next_entry_array_offset))) { - log_error("Entry array chains up to invalid next array at %llu", (unsigned long long) p); - r = -EBADMSG; - goto fail; - } - - n = journal_file_entry_array_n_items(o); - for (i = 0; i < n; i++) { - if (le64toh(o->entry_array.items[i]) != 0 && - !contains_uint64(f->mmap, entry_fd, n_entries, le64toh(o->entry_array.items[i]))) { - - log_error("Entry array points to invalid next array at %llu", (unsigned long long) p); - r = -EBADMSG; - goto fail; - } - } - - } - - r = journal_file_move_to_object(f, -1, p, &o); - if (r < 0) { - log_error("Invalid object at %llu", (unsigned long long) p); - goto fail; - } - - if (p == le64toh(f->header->tail_object_offset)) - p = 0; - else - p = p + ALIGN64(le64toh(o->object.size)); - } - - flush_progress(); - - mmap_cache_close_fd(f->mmap, data_fd); - mmap_cache_close_fd(f->mmap, entry_fd); - mmap_cache_close_fd(f->mmap, entry_array_fd); - - close_nointr_nofail(data_fd); - close_nointr_nofail(entry_fd); - close_nointr_nofail(entry_array_fd); - - return 0; - -fail: - flush_progress(); - - log_error("File corruption detected at %s:%llu (of %llu, %llu%%).", - f->path, - (unsigned long long) p, - (unsigned long long) f->last_stat.st_size, - (unsigned long long) (100 * p / f->last_stat.st_size)); - - if (data_fd >= 0) { - mmap_cache_close_fd(f->mmap, data_fd); - close_nointr_nofail(data_fd); - } - - if (entry_fd >= 0) { - mmap_cache_close_fd(f->mmap, entry_fd); - close_nointr_nofail(entry_fd); - } - - if (entry_array_fd >= 0) { - mmap_cache_close_fd(f->mmap, entry_array_fd); - close_nointr_nofail(entry_array_fd); - } - - return r; -} - -void journal_file_dump(JournalFile *f) { - Object *o; - int r; - uint64_t p; - - assert(f); - - journal_file_print_header(f); - - p = le64toh(f->header->header_size); - while (p != 0) { - r = journal_file_move_to_object(f, -1, p, &o); - if (r < 0) - goto fail; - - switch (o->object.type) { - - case OBJECT_UNUSED: - printf("Type: OBJECT_UNUSED\n"); - break; - - case OBJECT_DATA: - printf("Type: OBJECT_DATA\n"); - break; - - case OBJECT_ENTRY: - printf("Type: OBJECT_ENTRY %llu %llu %llu\n", - (unsigned long long) le64toh(o->entry.seqnum), - (unsigned long long) le64toh(o->entry.monotonic), - (unsigned long long) le64toh(o->entry.realtime)); - break; - - case OBJECT_FIELD_HASH_TABLE: - printf("Type: OBJECT_FIELD_HASH_TABLE\n"); - break; - - case OBJECT_DATA_HASH_TABLE: - printf("Type: OBJECT_DATA_HASH_TABLE\n"); - break; - - case OBJECT_ENTRY_ARRAY: - printf("Type: OBJECT_ENTRY_ARRAY\n"); - break; - - case OBJECT_TAG: - printf("Type: OBJECT_TAG %llu\n", - (unsigned long long) le64toh(o->tag.seqnum)); - break; - } - - if (o->object.flags & OBJECT_COMPRESSED) - printf("Flags: COMPRESSED\n"); - - if (p == le64toh(f->header->tail_object_offset)) - p = 0; - else - p = p + ALIGN64(le64toh(o->object.size)); - } - - return; -fail: - log_error("File corrupt"); -} - -void journal_file_print_header(JournalFile *f) { - char a[33], b[33], c[33]; - char x[FORMAT_TIMESTAMP_MAX], y[FORMAT_TIMESTAMP_MAX]; - - assert(f); - - printf("File Path: %s\n" - "File ID: %s\n" - "Machine ID: %s\n" - "Boot ID: %s\n" - "Sequential Number ID: %s\n" - "State: %s\n" - "Compatible Flags:%s%s\n" - "Incompatible Flags:%s%s\n" - "Header size: %llu\n" - "Arena size: %llu\n" - "Data Hash Table Size: %llu\n" - "Field Hash Table Size: %llu\n" - "Objects: %llu\n" - "Entry Objects: %llu\n" - "Rotate Suggested: %s\n" - "Head Sequential Number: %llu\n" - "Tail Sequential Number: %llu\n" - "Head Realtime Timestamp: %s\n" - "Tail Realtime Timestamp: %s\n", - f->path, - sd_id128_to_string(f->header->file_id, a), - sd_id128_to_string(f->header->machine_id, b), - sd_id128_to_string(f->header->boot_id, c), - sd_id128_to_string(f->header->seqnum_id, c), - f->header->state == STATE_OFFLINE ? "offline" : - f->header->state == STATE_ONLINE ? "online" : - f->header->state == STATE_ARCHIVED ? "archived" : "unknown", - (f->header->compatible_flags & HEADER_COMPATIBLE_AUTHENTICATED) ? " AUTHENTICATED" : "", - (f->header->compatible_flags & ~HEADER_COMPATIBLE_AUTHENTICATED) ? " ???" : "", - (f->header->incompatible_flags & HEADER_INCOMPATIBLE_COMPRESSED) ? " COMPRESSED" : "", - (f->header->incompatible_flags & ~HEADER_INCOMPATIBLE_COMPRESSED) ? " ???" : "", - (unsigned long long) le64toh(f->header->header_size), - (unsigned long long) le64toh(f->header->arena_size), - (unsigned long long) le64toh(f->header->data_hash_table_size) / sizeof(HashItem), - (unsigned long long) le64toh(f->header->field_hash_table_size) / sizeof(HashItem), - (unsigned long long) le64toh(f->header->n_objects), - (unsigned long long) le64toh(f->header->n_entries), - yes_no(journal_file_rotate_suggested(f)), - (unsigned long long) le64toh(f->header->head_entry_seqnum), - (unsigned long long) le64toh(f->header->tail_entry_seqnum), - format_timestamp(x, sizeof(x), le64toh(f->header->head_entry_realtime)), - format_timestamp(y, sizeof(y), le64toh(f->header->tail_entry_realtime))); - - if (JOURNAL_HEADER_CONTAINS(f->header, n_data)) - printf("Data Objects: %llu\n" - "Data Hash Table Fill: %.1f%%\n", - (unsigned long long) le64toh(f->header->n_data), - 100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)))); - - if (JOURNAL_HEADER_CONTAINS(f->header, n_fields)) - printf("Field Objects: %llu\n" - "Field Hash Table Fill: %.1f%%\n", - (unsigned long long) le64toh(f->header->n_fields), - 100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)))); -} - -int journal_file_open( - const char *fname, - int flags, - mode_t mode, - bool compress, - bool authenticate, - JournalMetrics *metrics, - MMapCache *mmap_cache, - JournalFile *template, - JournalFile **ret) { - - JournalFile *f; - int r; - bool newly_created = false; - - assert(fname); - - if ((flags & O_ACCMODE) != O_RDONLY && - (flags & O_ACCMODE) != O_RDWR) - return -EINVAL; - - if (!endswith(fname, ".journal")) - return -EINVAL; - - f = new0(JournalFile, 1); - if (!f) - return -ENOMEM; - - f->fd = -1; - f->mode = mode; - - f->flags = flags; - f->prot = prot_from_flags(flags); - f->writable = (flags & O_ACCMODE) != O_RDONLY; - f->compress = compress; - f->authenticate = authenticate; - - if (mmap_cache) - f->mmap = mmap_cache_ref(mmap_cache); - else { - /* One context for each type, plus the zeroth catchall - * context. One fd for the file plus one for each type - * (which we need during verification */ - f->mmap = mmap_cache_new(_OBJECT_TYPE_MAX, 1 + _OBJECT_TYPE_MAX); - if (!f->mmap) { - r = -ENOMEM; - goto fail; - } - } - - f->path = strdup(fname); - if (!f->path) { - r = -ENOMEM; - goto fail; - } - - f->fd = open(f->path, f->flags|O_CLOEXEC, f->mode); - if (f->fd < 0) { - r = -errno; - goto fail; - } - - if (fstat(f->fd, &f->last_stat) < 0) { - r = -errno; - goto fail; - } - - if (f->last_stat.st_size == 0 && f->writable) { - newly_created = true; - - /* Try to load the FSPRG state, and if we can't, then - * just don't do authentication */ - r = journal_file_load_fsprg(f); - if (r < 0) - f->authenticate = false; - - r = journal_file_init_header(f, template); - if (r < 0) - goto fail; - - if (fstat(f->fd, &f->last_stat) < 0) { - r = -errno; - goto fail; - } - } - - if (f->last_stat.st_size < (off_t) HEADER_SIZE_MIN) { - r = -EIO; - goto fail; - } - - f->header = mmap(NULL, PAGE_ALIGN(sizeof(Header)), prot_from_flags(flags), MAP_SHARED, f->fd, 0); - if (f->header == MAP_FAILED) { - f->header = NULL; - r = -errno; - goto fail; - } - - if (!newly_created) { - r = journal_file_verify_header(f); - if (r < 0) - goto fail; - } - - if (!newly_created && f->writable) { - r = journal_file_load_fsprg(f); - if (r < 0) - goto fail; - } + if (!newly_created && f->writable) { + r = journal_file_load_fsprg(f); + if (r < 0) + goto fail; + } if (f->writable) { if (metrics) { @@ -3139,203 +2199,6 @@ int journal_file_open_reliably( return journal_file_open(fname, flags, mode, compress, authenticate, metrics, mmap, template, ret); } -struct vacuum_info { - off_t usage; - char *filename; - - uint64_t realtime; - sd_id128_t seqnum_id; - uint64_t seqnum; - - bool have_seqnum; -}; - -static int vacuum_compare(const void *_a, const void *_b) { - const struct vacuum_info *a, *b; - - a = _a; - b = _b; - - if (a->have_seqnum && b->have_seqnum && - sd_id128_equal(a->seqnum_id, b->seqnum_id)) { - if (a->seqnum < b->seqnum) - return -1; - else if (a->seqnum > b->seqnum) - return 1; - else - return 0; - } - - if (a->realtime < b->realtime) - return -1; - else if (a->realtime > b->realtime) - return 1; - else if (a->have_seqnum && b->have_seqnum) - return memcmp(&a->seqnum_id, &b->seqnum_id, 16); - else - return strcmp(a->filename, b->filename); -} - -int journal_directory_vacuum(const char *directory, uint64_t max_use, uint64_t min_free) { - DIR *d; - int r = 0; - struct vacuum_info *list = NULL; - unsigned n_list = 0, n_allocated = 0, i; - uint64_t sum = 0; - - assert(directory); - - if (max_use <= 0) - return 0; - - d = opendir(directory); - if (!d) - return -errno; - - for (;;) { - int k; - struct dirent buf, *de; - size_t q; - struct stat st; - char *p; - unsigned long long seqnum = 0, realtime; - sd_id128_t seqnum_id; - bool have_seqnum; - - k = readdir_r(d, &buf, &de); - if (k != 0) { - r = -k; - goto finish; - } - - if (!de) - break; - - if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) - continue; - - if (!S_ISREG(st.st_mode)) - continue; - - q = strlen(de->d_name); - - if (endswith(de->d_name, ".journal")) { - - /* Vacuum archived files */ - - if (q < 1 + 32 + 1 + 16 + 1 + 16 + 8) - continue; - - if (de->d_name[q-8-16-1] != '-' || - de->d_name[q-8-16-1-16-1] != '-' || - de->d_name[q-8-16-1-16-1-32-1] != '@') - continue; - - p = strdup(de->d_name); - if (!p) { - r = -ENOMEM; - goto finish; - } - - de->d_name[q-8-16-1-16-1] = 0; - if (sd_id128_from_string(de->d_name + q-8-16-1-16-1-32, &seqnum_id) < 0) { - free(p); - continue; - } - - if (sscanf(de->d_name + q-8-16-1-16, "%16llx-%16llx.journal", &seqnum, &realtime) != 2) { - free(p); - continue; - } - - have_seqnum = true; - - } else if (endswith(de->d_name, ".journal~")) { - unsigned long long tmp; - - /* Vacuum corrupted files */ - - if (q < 1 + 16 + 1 + 16 + 8 + 1) - continue; - - if (de->d_name[q-1-8-16-1] != '-' || - de->d_name[q-1-8-16-1-16-1] != '@') - continue; - - p = strdup(de->d_name); - if (!p) { - r = -ENOMEM; - goto finish; - } - - if (sscanf(de->d_name + q-1-8-16-1-16, "%16llx-%16llx.journal~", &realtime, &tmp) != 2) { - free(p); - continue; - } - - have_seqnum = false; - } else - continue; - - if (n_list >= n_allocated) { - struct vacuum_info *j; - - n_allocated = MAX(n_allocated * 2U, 8U); - j = realloc(list, n_allocated * sizeof(struct vacuum_info)); - if (!j) { - free(p); - r = -ENOMEM; - goto finish; - } - - list = j; - } - - list[n_list].filename = p; - list[n_list].usage = 512UL * (uint64_t) st.st_blocks; - list[n_list].seqnum = seqnum; - list[n_list].realtime = realtime; - list[n_list].seqnum_id = seqnum_id; - list[n_list].have_seqnum = have_seqnum; - - sum += list[n_list].usage; - - n_list ++; - } - - if (n_list > 0) - qsort(list, n_list, sizeof(struct vacuum_info), vacuum_compare); - - for(i = 0; i < n_list; i++) { - struct statvfs ss; - - if (fstatvfs(dirfd(d), &ss) < 0) { - r = -errno; - goto finish; - } - - if (sum <= max_use && - (uint64_t) ss.f_bavail * (uint64_t) ss.f_bsize >= min_free) - break; - - if (unlinkat(dirfd(d), list[i].filename, 0) >= 0) { - log_info("Deleted archived journal %s/%s.", directory, list[i].filename); - sum -= list[i].usage; - } else if (errno != ENOENT) - log_warning("Failed to delete %s/%s: %m", directory, list[i].filename); - } - -finish: - for (i = 0; i < n_list; i++) - free(list[i].filename); - - free(list); - - if (d) - closedir(d); - - return r; -} int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p, uint64_t *seqnum, Object **ret, uint64_t *offset) { uint64_t i, n; diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h index 0305c97134..aba3d9a9bd 100644 --- a/src/journal/journal-file.h +++ b/src/journal/journal-file.h @@ -107,10 +107,17 @@ int journal_file_open_reliably( JournalFile *template, JournalFile **ret); +#define ALIGN64(x) (((x) + 7ULL) & ~7ULL) + +#define JOURNAL_HEADER_CONTAINS(h, field) \ + (le64toh((h)->header_size) >= offsetof(Header, field) + sizeof((h)->field)) + int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Object **ret); uint64_t journal_file_entry_n_items(Object *o); +uint64_t journal_file_entry_array_n_items(Object *o); +int journal_file_append_object(JournalFile *f, int type, uint64_t size, Object **ret, uint64_t *offset); int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const struct iovec iovec[], unsigned n_iovec, uint64_t *seqno, Object **ret, uint64_t *offset); int journal_file_find_data_object(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset); @@ -138,8 +145,6 @@ void journal_file_print_header(JournalFile *f); int journal_file_rotate(JournalFile **f, bool compress, bool authenticate); -int journal_directory_vacuum(const char *directory, uint64_t max_use, uint64_t min_free); - void journal_file_post_change(JournalFile *f); void journal_default_metrics(JournalMetrics *m, int fd); @@ -148,7 +153,3 @@ int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t * int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot, usec_t *from, usec_t *to); bool journal_file_rotate_suggested(JournalFile *f); - -int journal_file_append_tag(JournalFile *f); - -int journal_file_verify(JournalFile *f, const char *key); diff --git a/src/journal/journal-vacuum.c b/src/journal/journal-vacuum.c new file mode 100644 index 0000000000..ff2cd3306d --- /dev/null +++ b/src/journal/journal-vacuum.c @@ -0,0 +1,230 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "journal-def.h" +#include "journal-file.h" +#include "journal-vacuum.h" +#include "sd-id128.h" +#include "util.h" + +struct vacuum_info { + off_t usage; + char *filename; + + uint64_t realtime; + sd_id128_t seqnum_id; + uint64_t seqnum; + + bool have_seqnum; +}; + +static int vacuum_compare(const void *_a, const void *_b) { + const struct vacuum_info *a, *b; + + a = _a; + b = _b; + + if (a->have_seqnum && b->have_seqnum && + sd_id128_equal(a->seqnum_id, b->seqnum_id)) { + if (a->seqnum < b->seqnum) + return -1; + else if (a->seqnum > b->seqnum) + return 1; + else + return 0; + } + + if (a->realtime < b->realtime) + return -1; + else if (a->realtime > b->realtime) + return 1; + else if (a->have_seqnum && b->have_seqnum) + return memcmp(&a->seqnum_id, &b->seqnum_id, 16); + else + return strcmp(a->filename, b->filename); +} + +int journal_directory_vacuum(const char *directory, uint64_t max_use, uint64_t min_free) { + DIR *d; + int r = 0; + struct vacuum_info *list = NULL; + unsigned n_list = 0, n_allocated = 0, i; + uint64_t sum = 0; + + assert(directory); + + if (max_use <= 0) + return 0; + + d = opendir(directory); + if (!d) + return -errno; + + for (;;) { + int k; + struct dirent buf, *de; + size_t q; + struct stat st; + char *p; + unsigned long long seqnum = 0, realtime; + sd_id128_t seqnum_id; + bool have_seqnum; + + k = readdir_r(d, &buf, &de); + if (k != 0) { + r = -k; + goto finish; + } + + if (!de) + break; + + if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) + continue; + + if (!S_ISREG(st.st_mode)) + continue; + + q = strlen(de->d_name); + + if (endswith(de->d_name, ".journal")) { + + /* Vacuum archived files */ + + if (q < 1 + 32 + 1 + 16 + 1 + 16 + 8) + continue; + + if (de->d_name[q-8-16-1] != '-' || + de->d_name[q-8-16-1-16-1] != '-' || + de->d_name[q-8-16-1-16-1-32-1] != '@') + continue; + + p = strdup(de->d_name); + if (!p) { + r = -ENOMEM; + goto finish; + } + + de->d_name[q-8-16-1-16-1] = 0; + if (sd_id128_from_string(de->d_name + q-8-16-1-16-1-32, &seqnum_id) < 0) { + free(p); + continue; + } + + if (sscanf(de->d_name + q-8-16-1-16, "%16llx-%16llx.journal", &seqnum, &realtime) != 2) { + free(p); + continue; + } + + have_seqnum = true; + + } else if (endswith(de->d_name, ".journal~")) { + unsigned long long tmp; + + /* Vacuum corrupted files */ + + if (q < 1 + 16 + 1 + 16 + 8 + 1) + continue; + + if (de->d_name[q-1-8-16-1] != '-' || + de->d_name[q-1-8-16-1-16-1] != '@') + continue; + + p = strdup(de->d_name); + if (!p) { + r = -ENOMEM; + goto finish; + } + + if (sscanf(de->d_name + q-1-8-16-1-16, "%16llx-%16llx.journal~", &realtime, &tmp) != 2) { + free(p); + continue; + } + + have_seqnum = false; + } else + continue; + + if (n_list >= n_allocated) { + struct vacuum_info *j; + + n_allocated = MAX(n_allocated * 2U, 8U); + j = realloc(list, n_allocated * sizeof(struct vacuum_info)); + if (!j) { + free(p); + r = -ENOMEM; + goto finish; + } + + list = j; + } + + list[n_list].filename = p; + list[n_list].usage = 512UL * (uint64_t) st.st_blocks; + list[n_list].seqnum = seqnum; + list[n_list].realtime = realtime; + list[n_list].seqnum_id = seqnum_id; + list[n_list].have_seqnum = have_seqnum; + + sum += list[n_list].usage; + + n_list ++; + } + + if (n_list > 0) + qsort(list, n_list, sizeof(struct vacuum_info), vacuum_compare); + + for(i = 0; i < n_list; i++) { + struct statvfs ss; + + if (fstatvfs(dirfd(d), &ss) < 0) { + r = -errno; + goto finish; + } + + if (sum <= max_use && + (uint64_t) ss.f_bavail * (uint64_t) ss.f_bsize >= min_free) + break; + + if (unlinkat(dirfd(d), list[i].filename, 0) >= 0) { + log_info("Deleted archived journal %s/%s.", directory, list[i].filename); + sum -= list[i].usage; + } else if (errno != ENOENT) + log_warning("Failed to delete %s/%s: %m", directory, list[i].filename); + } + +finish: + for (i = 0; i < n_list; i++) + free(list[i].filename); + + free(list); + + if (d) + closedir(d); + + return r; +} diff --git a/src/journal/journal-vacuum.h b/src/journal/journal-vacuum.h new file mode 100644 index 0000000000..9841d72de8 --- /dev/null +++ b/src/journal/journal-vacuum.h @@ -0,0 +1,26 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +int journal_directory_vacuum(const char *directory, uint64_t max_use, uint64_t min_free); diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c new file mode 100644 index 0000000000..f3182e876e --- /dev/null +++ b/src/journal/journal-verify.c @@ -0,0 +1,558 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "util.h" +#include "macro.h" +#include "journal-def.h" +#include "journal-file.h" +#include "journal-authenticate.h" +#include "journal-verify.h" + +static int journal_file_object_verify(JournalFile *f, Object *o) { + assert(f); + assert(o); + + /* This does various superficial tests about the length an + * possible field values. It does not follow any references to + * other objects. */ + + switch (o->object.type) { + case OBJECT_DATA: + if (le64toh(o->data.entry_offset) <= 0 || + le64toh(o->data.n_entries) <= 0) + return -EBADMSG; + + if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0) + return -EBADMSG; + break; + + case OBJECT_FIELD: + if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0) + return -EBADMSG; + break; + + case OBJECT_ENTRY: + if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0) + return -EBADMSG; + + if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0) + return -EBADMSG; + + if (le64toh(o->entry.seqnum) <= 0 || + le64toh(o->entry.realtime) <= 0) + return -EBADMSG; + + break; + + case OBJECT_DATA_HASH_TABLE: + case OBJECT_FIELD_HASH_TABLE: + if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0) + return -EBADMSG; + + break; + + case OBJECT_ENTRY_ARRAY: + if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0) + return -EBADMSG; + + break; + + case OBJECT_TAG: + if (le64toh(o->object.size) != sizeof(TagObject)) + return -EBADMSG; + break; + } + + return 0; +} + +static void draw_progress(uint64_t p, usec_t *last_usec) { + unsigned n, i, j, k; + usec_t z, x; + + if (!isatty(STDOUT_FILENO)) + return; + + z = now(CLOCK_MONOTONIC); + x = *last_usec; + + if (x != 0 && x + 40 * USEC_PER_MSEC > z) + return; + + *last_usec = z; + + n = (3 * columns()) / 4; + j = (n * (unsigned) p) / 65535ULL; + k = n - j; + + fputs("\r\x1B[?25l", stdout); + + for (i = 0; i < j; i++) + fputs("\xe2\x96\x88", stdout); + + for (i = 0; i < k; i++) + fputs("\xe2\x96\x91", stdout); + + printf(" %3lu%%", 100LU * (unsigned long) p / 65535LU); + + fputs("\r\x1B[?25h", stdout); + fflush(stdout); +} + +static void flush_progress(void) { + unsigned n, i; + + if (!isatty(STDOUT_FILENO)) + return; + + n = (3 * columns()) / 4; + + putchar('\r'); + + for (i = 0; i < n + 5; i++) + putchar(' '); + + putchar('\r'); + fflush(stdout); +} + +static int write_uint64(int fd, uint64_t p) { + ssize_t k; + + k = write(fd, &p, sizeof(p)); + if (k < 0) + return -errno; + if (k != sizeof(p)) + return -EIO; + + return 0; +} + +static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) { + uint64_t a, b; + int r; + + assert(m); + assert(fd >= 0); + + /* Bisection ... */ + + a = 0; b = n; + while (a < b) { + uint64_t c, *z; + + c = (a + b) / 2; + + r = mmap_cache_get(m, fd, PROT_READ, 0, c * sizeof(uint64_t), sizeof(uint64_t), (void **) &z); + if (r < 0) + return r; + + if (*z == p) + return 1; + + if (p < *z) + b = c; + else + a = c; + } + + return 0; +} + +int journal_file_verify(JournalFile *f, const char *key) { + int r; + Object *o; + uint64_t p = 0; + uint64_t tag_seqnum = 0, entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0; + sd_id128_t entry_boot_id; + bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false; + uint64_t n_weird = 0, n_objects = 0, n_entries = 0, n_data = 0, n_fields = 0, n_data_hash_tables = 0, n_field_hash_tables = 0, n_entry_arrays = 0; + usec_t last_usec = 0; + int data_fd = -1, entry_fd = -1, entry_array_fd = -1; + char data_path[] = "/var/tmp/journal-data-XXXXXX", + entry_path[] = "/var/tmp/journal-entry-XXXXXX", + entry_array_path[] = "/var/tmp/journal-entry-array-XXXXXX"; + + assert(f); + + data_fd = mkostemp(data_path, O_CLOEXEC); + if (data_fd < 0) { + log_error("Failed to create data file: %m"); + goto fail; + } + unlink(data_path); + + entry_fd = mkostemp(entry_path, O_CLOEXEC); + if (entry_fd < 0) { + log_error("Failed to create entry file: %m"); + goto fail; + } + unlink(entry_path); + + entry_array_fd = mkostemp(entry_array_path, O_CLOEXEC); + if (entry_array_fd < 0) { + log_error("Failed to create entry array file: %m"); + goto fail; + } + unlink(entry_array_path); + + /* First iteration: we go through all objects, verify the + * superficial structure, headers, hashes. */ + + r = journal_file_hmac_put_header(f); + if (r < 0) { + log_error("Failed to calculate HMAC of header."); + goto fail; + } + + p = le64toh(f->header->header_size); + while (p != 0) { + draw_progress((0x7FFF * p) / le64toh(f->header->tail_object_offset), &last_usec); + + r = journal_file_move_to_object(f, -1, p, &o); + if (r < 0) { + log_error("Invalid object at %llu", (unsigned long long) p); + goto fail; + } + + if (le64toh(f->header->tail_object_offset) < p) { + log_error("Invalid tail object pointer."); + r = -EBADMSG; + goto fail; + } + + n_objects ++; + + r = journal_file_object_verify(f, o); + if (r < 0) { + log_error("Invalid object contents at %llu", (unsigned long long) p); + goto fail; + } + + r = journal_file_hmac_put_object(f, -1, p); + if (r < 0) { + log_error("Failed to calculate HMAC at %llu", (unsigned long long) p); + goto fail; + } + + if (o->object.flags & OBJECT_COMPRESSED && + !(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED)) { + log_error("Compressed object without compression at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + + if (o->object.flags & OBJECT_COMPRESSED && + o->object.type != OBJECT_DATA) { + log_error("Compressed non-data object at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + + if (o->object.type == OBJECT_TAG) { + + if (!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED)) { + log_error("Tag object without authentication at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + + if (le64toh(o->tag.seqnum) != tag_seqnum) { + log_error("Tag sequence number out of synchronization at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + + } else if (o->object.type == OBJECT_ENTRY) { + + r = write_uint64(entry_fd, p); + if (r < 0) + goto fail; + + if (!entry_seqnum_set && + le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) { + log_error("Head entry sequence number incorrect"); + r = -EBADMSG; + goto fail; + } + + if (entry_seqnum_set && + entry_seqnum >= le64toh(o->entry.seqnum)) { + log_error("Entry sequence number out of synchronization at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + + entry_seqnum = le64toh(o->entry.seqnum); + entry_seqnum_set = true; + + if (entry_monotonic_set && + sd_id128_equal(entry_boot_id, o->entry.boot_id) && + entry_monotonic > le64toh(o->entry.monotonic)) { + log_error("Entry timestamp out of synchronization at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + + entry_monotonic = le64toh(o->entry.monotonic); + entry_boot_id = o->entry.boot_id; + entry_monotonic_set = true; + + if (!entry_realtime_set && + le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) { + log_error("Head entry realtime timestamp incorrect"); + r = -EBADMSG; + goto fail; + } + + entry_realtime = le64toh(o->entry.realtime); + entry_realtime_set = true; + + n_entries ++; + } else if (o->object.type == OBJECT_ENTRY_ARRAY) { + + r = write_uint64(entry_array_fd, p); + if (r < 0) + goto fail; + + if (p == le64toh(f->header->entry_array_offset)) { + if (found_main_entry_array) { + log_error("More than one main entry array at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + + found_main_entry_array = true; + } + + n_entry_arrays++; + + } else if (o->object.type == OBJECT_DATA) { + + r = write_uint64(data_fd, p); + if (r < 0) + goto fail; + + n_data++; + + } else if (o->object.type == OBJECT_FIELD) + n_fields++; + else if (o->object.type == OBJECT_DATA_HASH_TABLE) { + n_data_hash_tables++; + + if (n_data_hash_tables > 1) { + log_error("More than one data hash table at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + + if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) || + le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) { + log_error("Header fields for data hash table invalid."); + r = -EBADMSG; + goto fail; + } + } else if (o->object.type == OBJECT_FIELD_HASH_TABLE) { + n_field_hash_tables++; + + if (n_field_hash_tables > 1) { + log_error("More than one field hash table at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + + if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) || + le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) { + log_error("Header fields for field hash table invalid."); + r = -EBADMSG; + goto fail; + } + } else if (o->object.type >= _OBJECT_TYPE_MAX) + n_weird ++; + + if (p == le64toh(f->header->tail_object_offset)) + p = 0; + else + p = p + ALIGN64(le64toh(o->object.size)); + } + + if (n_objects != le64toh(f->header->n_objects)) { + log_error("Object number mismatch"); + r = -EBADMSG; + goto fail; + } + + if (n_entries != le64toh(f->header->n_entries)) { + log_error("Entry number mismatch"); + r = -EBADMSG; + goto fail; + } + + if (JOURNAL_HEADER_CONTAINS(f->header, n_data) && + n_data != le64toh(f->header->n_data)) { + log_error("Data number mismatch"); + r = -EBADMSG; + goto fail; + } + + if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) && + n_fields != le64toh(f->header->n_fields)) { + log_error("Field number mismatch"); + r = -EBADMSG; + goto fail; + } + + if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) && + tag_seqnum != le64toh(f->header->n_tags)) { + log_error("Tag number mismatch"); + r = -EBADMSG; + goto fail; + } + + if (n_data_hash_tables != 1) { + log_error("Missing data hash table"); + r = -EBADMSG; + goto fail; + } + + if (n_field_hash_tables != 1) { + log_error("Missing field hash table"); + r = -EBADMSG; + goto fail; + } + + if (!found_main_entry_array) { + log_error("Missing entry array"); + r = -EBADMSG; + goto fail; + } + + if (entry_seqnum_set && + entry_seqnum != le64toh(f->header->tail_entry_seqnum)) { + log_error("Invalid tail seqnum"); + r = -EBADMSG; + goto fail; + } + + if (entry_monotonic_set && + (!sd_id128_equal(entry_boot_id, f->header->boot_id) || + entry_monotonic != le64toh(f->header->tail_entry_monotonic))) { + log_error("Invalid tail monotonic timestamp"); + r = -EBADMSG; + goto fail; + } + + if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) { + log_error("Invalid tail realtime timestamp"); + r = -EBADMSG; + goto fail; + } + + /* Second iteration: we go through all objects again, this + * time verify all pointers. */ + + p = le64toh(f->header->header_size); + while (p != 0) { + draw_progress(0x8000 + (0x7FFF * p) / le64toh(f->header->tail_object_offset), &last_usec); + + r = journal_file_move_to_object(f, -1, p, &o); + if (r < 0) { + log_error("Invalid object at %llu", (unsigned long long) p); + goto fail; + } + + if (o->object.type == OBJECT_ENTRY_ARRAY) { + uint64_t i = 0, n; + + if (le64toh(o->entry_array.next_entry_array_offset) != 0 && + !contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, le64toh(o->entry_array.next_entry_array_offset))) { + log_error("Entry array chains up to invalid next array at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + + n = journal_file_entry_array_n_items(o); + for (i = 0; i < n; i++) { + if (le64toh(o->entry_array.items[i]) != 0 && + !contains_uint64(f->mmap, entry_fd, n_entries, le64toh(o->entry_array.items[i]))) { + + log_error("Entry array points to invalid next array at %llu", (unsigned long long) p); + r = -EBADMSG; + goto fail; + } + } + + } + + r = journal_file_move_to_object(f, -1, p, &o); + if (r < 0) { + log_error("Invalid object at %llu", (unsigned long long) p); + goto fail; + } + + if (p == le64toh(f->header->tail_object_offset)) + p = 0; + else + p = p + ALIGN64(le64toh(o->object.size)); + } + + flush_progress(); + + mmap_cache_close_fd(f->mmap, data_fd); + mmap_cache_close_fd(f->mmap, entry_fd); + mmap_cache_close_fd(f->mmap, entry_array_fd); + + close_nointr_nofail(data_fd); + close_nointr_nofail(entry_fd); + close_nointr_nofail(entry_array_fd); + + return 0; + +fail: + flush_progress(); + + log_error("File corruption detected at %s:%llu (of %llu, %llu%%).", + f->path, + (unsigned long long) p, + (unsigned long long) f->last_stat.st_size, + (unsigned long long) (100 * p / f->last_stat.st_size)); + + if (data_fd >= 0) { + mmap_cache_close_fd(f->mmap, data_fd); + close_nointr_nofail(data_fd); + } + + if (entry_fd >= 0) { + mmap_cache_close_fd(f->mmap, entry_fd); + close_nointr_nofail(entry_fd); + } + + if (entry_array_fd >= 0) { + mmap_cache_close_fd(f->mmap, entry_array_fd); + close_nointr_nofail(entry_array_fd); + } + + return r; +} diff --git a/src/journal/journal-verify.h b/src/journal/journal-verify.h new file mode 100644 index 0000000000..3ebdd5e7f2 --- /dev/null +++ b/src/journal/journal-verify.h @@ -0,0 +1,26 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "journal-file.h" + +int journal_file_verify(JournalFile *f, const char *key); diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 8e09ff115a..a70de0684e 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -43,6 +43,7 @@ #include "journal-internal.h" #include "fsprg.h" #include "journal-def.h" +#include "journal-verify.h" #define DEFAULT_FSPRG_INTERVAL_USEC (15*USEC_PER_MINUTE) diff --git a/src/journal/journald.c b/src/journal/journald.c index 384ed90568..d431953f07 100644 --- a/src/journal/journald.c +++ b/src/journal/journald.c @@ -47,6 +47,7 @@ #include "list.h" #include "journal-rate-limit.h" #include "journal-internal.h" +#include "journal-vacuum.h" #include "conf-parser.h" #include "journald.h" #include "virt.h" diff --git a/src/journal/test-journal-verify.c b/src/journal/test-journal-verify.c new file mode 100644 index 0000000000..bada498fab --- /dev/null +++ b/src/journal/test-journal-verify.c @@ -0,0 +1,78 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "util.h" +#include "log.h" +#include "journal-file.h" +#include "journal-verify.h" + +#define N_ENTRIES 6000 +#define RANDOM_RANGE 77 + +int main(int argc, char *argv[]) { + char t[] = "/tmp/journal-XXXXXX"; + unsigned n; + JournalFile *f; + + log_set_max_level(LOG_DEBUG); + + assert_se(mkdtemp(t)); + assert_se(chdir(t) >= 0); + + log_info("Generating..."); + + assert_se(journal_file_open("test.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, &f) == 0); + + for (n = 0; n < N_ENTRIES; n++) { + struct iovec iovec; + struct dual_timestamp ts; + char *test; + + dual_timestamp_get(&ts); + + assert_se(asprintf(&test, "RANDOM=%lu", random() % RANDOM_RANGE)); + + iovec.iov_base = (void*) test; + iovec.iov_len = strlen(test); + + assert_se(journal_file_append_entry(f, &ts, &iovec, 1, NULL, NULL, NULL) == 0); + + free(test); + } + + journal_file_close(f); + + log_info("Verifying..."); + + assert_se(journal_file_open("test.journal", O_RDONLY, 0666, false, false, NULL, NULL, NULL, &f) == 0); + assert_se(journal_file_verify(f, NULL) >= 0); + journal_file_close(f); + + log_info("Exiting..."); + + assert_se(rm_rf_dangerous(t, false, true, false) >= 0); + + return 0; +} diff --git a/src/journal/test-journal.c b/src/journal/test-journal.c index 2fd19a755b..05bb2ea8ed 100644 --- a/src/journal/test-journal.c +++ b/src/journal/test-journal.c @@ -24,8 +24,10 @@ #include -#include "journal-file.h" #include "log.h" +#include "journal-file.h" +#include "journal-authenticate.h" +#include "journal-vacuum.h" int main(int argc, char *argv[]) { dual_timestamp ts; -- cgit v1.2.3-54-g00ecf From 877d54e9b09e093c2102f519a84e2a52637ae035 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 24 Aug 2012 22:21:20 +0200 Subject: journal: generate structured journal messages for a number of events --- .gitignore | 1 + Makefile.am | 9 +++- TODO | 4 ++ src/core/job.c | 118 ++++++++++++++++++++++++++++++++++++++++++--- src/core/manager.c | 47 ++++++++++++------ src/core/unit.c | 90 +++++++++++++++++++++++++++++++--- src/login/logind-dbus.c | 45 ++++++++++++++++- src/login/logind-seat.c | 14 +++++- src/login/logind-session.c | 20 ++++++-- src/shared/log.c | 118 ++++++++++++++++++++++++++++++++++++++++++++- src/shared/log.h | 64 ++++++++++++++++-------- src/sleep/sleep.c | 26 ++++++++-- src/systemd/sd-id128.h | 1 + src/systemd/sd-messages.h | 33 +++++++++++-- src/test/test-log.c | 46 ++++++++++++++++++ src/timedate/timedated.c | 14 +++++- 16 files changed, 582 insertions(+), 68 deletions(-) create mode 100644 src/test/test-log.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 4c8bba8871..ca95d7afba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/test-log /test-journal-verify /test-journal-match /test-journal-stream diff --git a/Makefile.am b/Makefile.am index e5ace9b971..927d25f085 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1117,7 +1117,8 @@ noinst_PROGRAMS += \ test-strv \ test-install \ test-watchdog \ - test-unit-name + test-unit-name \ + test-log TESTS += \ test-job-type \ @@ -1173,6 +1174,12 @@ test_unit_name_SOURCES = \ test_unit_name_LDADD = \ libsystemd-core.la +test_log_SOURCES = \ + src/test/test-log.c + +test_log_LDADD = \ + libsystemd-core.la + test_daemon_SOURCES = \ src/test/test-daemon.c diff --git a/TODO b/TODO index a4643d7b83..b1d92dafad 100644 --- a/TODO +++ b/TODO @@ -49,6 +49,10 @@ Bugfixes: Features: +* wall messages for shutdown should move to logind + +* remove wants from journald.service + * allow writing multiple conditions in unit files on one line * journal: json output needs to be able to deal with multiple assignments of the same field diff --git a/src/core/job.c b/src/core/job.c index 8d51aa3fdd..9614642f56 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -24,6 +24,8 @@ #include #include +#include "systemd/sd-id128.h" +#include "systemd/sd-messages.h" #include "set.h" #include "unit.h" #include "macro.h" @@ -549,17 +551,74 @@ int job_run_and_invalidate(Job *j) { return r; } -static void job_print_status_message(Unit *u, JobType t, JobResult result) { +static const char *job_get_status_message_format(Unit *u, JobType t, JobResult result) { const UnitStatusMessageFormats *format_table; - const char *format; + + assert(u); + assert(t >= 0); + assert(t < _JOB_TYPE_MAX); format_table = &UNIT_VTABLE(u)->status_message_formats; if (!format_table) - return; + return NULL; + + if (t == JOB_START) + return format_table->finished_start_job[result]; + else if (t == JOB_STOP || t == JOB_RESTART) + return format_table->finished_stop_job[result]; + + return NULL; +} +static const char *job_get_status_message_format_try_harder(Unit *u, JobType t, JobResult result) { + const char *format; + + assert(u); + assert(t >= 0); + assert(t < _JOB_TYPE_MAX); + + format = job_get_status_message_format(u, t, result); + if (format) + return format; + + /* Return generic strings */ if (t == JOB_START) { + if (result == JOB_DONE) + return "Started %s."; + else if (result == JOB_FAILED) + return "Failed to start %s."; + else if (result == JOB_DEPENDENCY) + return "Dependency failed for %s."; + else if (result == JOB_TIMEOUT) + return "Timed out starting %s."; + } else if (t == JOB_STOP || t == JOB_RESTART) { + if (result == JOB_DONE) + return "Stopped %s."; + else if (result == JOB_FAILED) + return "Stopped (with error) %s."; + else if (result == JOB_TIMEOUT) + return "Timed out stoppping %s."; + } else if (t == JOB_RELOAD) { + if (result == JOB_DONE) + return "Reloaded %s."; + else if (result == JOB_FAILED) + return "Reload failed for %s."; + else if (result == JOB_TIMEOUT) + return "Timed out reloading %s."; + } + + return NULL; +} + +static void job_print_status_message(Unit *u, JobType t, JobResult result) { + const char *format; - format = format_table->finished_start_job[result]; + assert(u); + assert(t >= 0); + assert(t < _JOB_TYPE_MAX); + + if (t == JOB_START) { + format = job_get_status_message_format(u, t, result); if (!format) return; @@ -572,7 +631,7 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) { case JOB_FAILED: unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, format, unit_description(u)); - unit_status_printf(u, "", "See 'systemctl status %s' for details.", u->id); + unit_status_printf(u, NULL, "See 'systemctl status %s' for details.", u->id); break; case JOB_DEPENDENCY: @@ -589,7 +648,7 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) { } else if (t == JOB_STOP || t == JOB_RESTART) { - format = format_table->finished_stop_job[result]; + format = job_get_status_message_format(u, t, result); if (!format) return; @@ -618,6 +677,52 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) { } } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +static void job_log_status_message(Unit *u, JobType t, JobResult result) { + const char *format; + char buf[LINE_MAX]; + + assert(u); + assert(t >= 0); + assert(t < _JOB_TYPE_MAX); + + format = job_get_status_message_format_try_harder(u, t, result); + if (!format) + return; + + snprintf(buf, sizeof(buf), format, unit_description(u)); + char_array_0(buf); + + if (t == JOB_START) { + sd_id128_t mid; + + mid = result == JOB_DONE ? SD_MESSAGE_UNIT_STARTED : SD_MESSAGE_UNIT_FAILED; + log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR, + "MESSSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(mid), + "UNIT=%s", u->id, + "RESULT=%s", job_result_to_string(result), + "MESSAGE=%s", buf, + NULL); + + } else if (t == JOB_STOP) + log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR, + "MESSSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(SD_MESSAGE_UNIT_STOPPED), + "UNIT=%s", u->id, + "RESULT=%s", job_result_to_string(result), + "MESSAGE=%s", buf, + NULL); + + else if (t == JOB_RELOAD) + log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR, + "MESSSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(SD_MESSAGE_UNIT_RELOADED), + "UNIT=%s", u->id, + "RESULT=%s", job_result_to_string(result), + "MESSAGE=%s", buf, + NULL); +} +#pragma GCC diagnostic pop + int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) { Unit *u; Unit *other; @@ -636,6 +741,7 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) { log_debug("Job %s/%s finished, result=%s", u->id, job_type_to_string(t), job_result_to_string(result)); job_print_status_message(u, t, result); + job_log_status_message(u, t, result); job_add_to_dbus_queue(j); diff --git a/src/core/manager.c b/src/core/manager.c index bcaf913b5a..da766e6d58 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -41,7 +41,9 @@ #include #endif -#include +#include "systemd/sd-daemon.h" +#include "systemd/sd-id128.h" +#include "systemd/sd-messages.h" #include "manager.h" #include "transaction.h" @@ -2030,26 +2032,41 @@ void manager_check_finished(Manager *m) { kernel_usec = m->initrd_timestamp.monotonic; initrd_usec = m->startup_timestamp.monotonic - m->initrd_timestamp.monotonic; - log_info("Startup finished in %s (kernel) + %s (initrd) + %s (userspace) = %s.", - format_timespan(kernel, sizeof(kernel), kernel_usec), - format_timespan(initrd, sizeof(initrd), initrd_usec), - format_timespan(userspace, sizeof(userspace), userspace_usec), - format_timespan(sum, sizeof(sum), total_usec)); + log_struct(LOG_INFO, + "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(SD_MESSAGE_STARTUP_FINISHED), + "KERNEL_USEC=%llu", (unsigned long long) kernel_usec, + "INITRD_USEC=%llu", (unsigned long long) initrd_usec, + "USERSPACE_USEC=%llu", (unsigned long long) userspace_usec, + "MESSAGE=Startup finished in %s (kernel) + %s (initrd) + %s (userspace) = %s.", + format_timespan(kernel, sizeof(kernel), kernel_usec), + format_timespan(initrd, sizeof(initrd), initrd_usec), + format_timespan(userspace, sizeof(userspace), userspace_usec), + format_timespan(sum, sizeof(sum), total_usec), + NULL); } else { kernel_usec = m->startup_timestamp.monotonic; initrd_usec = 0; - log_info("Startup finished in %s (kernel) + %s (userspace) = %s.", - format_timespan(kernel, sizeof(kernel), kernel_usec), - format_timespan(userspace, sizeof(userspace), userspace_usec), - format_timespan(sum, sizeof(sum), total_usec)); + log_struct(LOG_INFO, + "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(SD_MESSAGE_STARTUP_FINISHED), + "KERNEL_USEC=%llu", (unsigned long long) kernel_usec, + "USERSPACE_USEC=%llu", (unsigned long long) userspace_usec, + "MESSAGE=Startup finished in %s (kernel) + %s (userspace) = %s.", + format_timespan(kernel, sizeof(kernel), kernel_usec), + format_timespan(userspace, sizeof(userspace), userspace_usec), + format_timespan(sum, sizeof(sum), total_usec), + NULL); } } else { - userspace_usec = initrd_usec = kernel_usec = 0; - total_usec = m->finish_timestamp.monotonic - m->startup_timestamp.monotonic; - - log_debug("Startup finished in %s.", - format_timespan(sum, sizeof(sum), total_usec)); + initrd_usec = kernel_usec = 0; + total_usec = userspace_usec = m->finish_timestamp.monotonic - m->startup_timestamp.monotonic; + + log_struct(LOG_INFO, + "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(SD_MESSAGE_STARTUP_FINISHED), + "USERSPACE_USEC=%llu", (unsigned long long) userspace_usec, + "MESSAGE=Startup finished in %s.", + format_timespan(sum, sizeof(sum), total_usec), + NULL); } bus_broadcast_finished(m, kernel_usec, initrd_usec, userspace_usec, total_usec); diff --git a/src/core/unit.c b/src/core/unit.c index 0d5d15ef90..8246837658 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -29,6 +29,8 @@ #include #include +#include "systemd/sd-id128.h" +#include "systemd/sd-messages.h" #include "set.h" #include "unit.h" #include "macro.h" @@ -936,21 +938,93 @@ bool unit_condition_test(Unit *u) { return u->condition_result; } -static void unit_status_print_starting_stopping(Unit *u, bool stopping) { +static const char* unit_get_status_message_format(Unit *u, JobType t) { const UnitStatusMessageFormats *format_table; - const char *format; + + assert(u); + assert(t >= 0); + assert(t < _JOB_TYPE_MAX); + + if (t != JOB_START && t != JOB_STOP) + return NULL; format_table = &UNIT_VTABLE(u)->status_message_formats; if (!format_table) - return; + return NULL; + + return format_table->starting_stopping[t == JOB_STOP]; +} + +static const char *unit_get_status_message_format_try_harder(Unit *u, JobType t) { + const char *format; + + assert(u); + assert(t >= 0); + assert(t < _JOB_TYPE_MAX); + + format = unit_get_status_message_format(u, t); + if (format) + return format; + + /* Return generic strings */ + if (t == JOB_START) + return "Starting %s."; + else if (t == JOB_STOP) + return "Stopping %s."; + else if (t == JOB_RELOAD) + return "Reloading %s."; + + return NULL; +} + +static void unit_status_print_starting_stopping(Unit *u, JobType t) { + const char *format; + + assert(u); - format = format_table->starting_stopping[stopping]; + /* We only print status messages for selected units on + * selected operations. */ + + format = unit_get_status_message_format(u, t); if (!format) return; unit_status_printf(u, "", format, unit_description(u)); } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) { + const char *format; + char buf[LINE_MAX]; + sd_id128_t mid; + + assert(u); + + if (t != JOB_START && t != JOB_STOP && t != JOB_RELOAD) + return; + + /* We log status messages for all units and all operations. */ + + format = unit_get_status_message_format_try_harder(u, t); + if (!format) + return; + + snprintf(buf, sizeof(buf), format, unit_description(u)); + char_array_0(buf); + + mid = t == JOB_START ? SD_MESSAGE_UNIT_STARTING : + t == JOB_STOP ? SD_MESSAGE_UNIT_STOPPING : + SD_MESSAGE_UNIT_RELOADING; + + log_struct(LOG_INFO, + "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(mid), + "UNIT=%s", u->id, + "MESSAGE=%s", buf, + NULL); +} +#pragma GCC diagnostic pop + /* Errors: * -EBADR: This unit type does not support starting. * -EALREADY: Unit is already started. @@ -990,7 +1064,8 @@ int unit_start(Unit *u) { return unit_start(following); } - unit_status_print_starting_stopping(u, false); + unit_status_log_starting_stopping_reloading(u, JOB_START); + unit_status_print_starting_stopping(u, JOB_START); /* If it is stopped, but we cannot start it, then fail */ if (!UNIT_VTABLE(u)->start) @@ -1040,7 +1115,8 @@ int unit_stop(Unit *u) { return unit_stop(following); } - unit_status_print_starting_stopping(u, true); + unit_status_log_starting_stopping_reloading(u, JOB_STOP); + unit_status_print_starting_stopping(u, JOB_STOP); if (!UNIT_VTABLE(u)->stop) return -EBADR; @@ -1079,6 +1155,8 @@ int unit_reload(Unit *u) { return unit_reload(following); } + unit_status_log_starting_stopping_reloading(u, JOB_RELOAD); + unit_add_to_dbus_queue(u); return UNIT_VTABLE(u)->reload(u); } diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index ae9671bb1e..af62d87821 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -31,6 +31,8 @@ #include "path-util.h" #include "polkit.h" #include "special.h" +#include "systemd/sd-id128.h" +#include "systemd/sd-messages.h" #define BUS_MANAGER_INTERFACE \ " \n" \ @@ -1138,6 +1140,42 @@ finish: return 0; } +static int bus_manager_log_shutdown( + Manager *m, + InhibitWhat w, + const char *unit_name) { + + const char *p, *q; + + assert(m); + assert(unit_name); + + if (w != INHIBIT_SHUTDOWN) + return 0; + + if (streq(unit_name, SPECIAL_POWEROFF_TARGET)) { + p = "MESSAGE=System is powering down."; + q = "SHUTDOWN=power-off"; + } else if (streq(unit_name, SPECIAL_HALT_TARGET)) { + p = "MESSAGE=System is halting."; + q = "SHUTDOWN=halt"; + } else if (streq(unit_name, SPECIAL_REBOOT_TARGET)) { + p = "MESSAGE=System is rebooting."; + q = "SHUTDOWN=reboot"; + } else if (streq(unit_name, SPECIAL_KEXEC_TARGET)) { + p = "MESSAGE=System is rebooting with kexec."; + q = "SHUTDOWN=kexec"; + } else { + p = "MESSAGE=System is shutting down."; + q = NULL; + } + + return log_struct(LOG_NOTICE, + "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(SD_MESSAGE_SHUTDOWN), + p, + q, NULL); +} + int bus_manager_shutdown_or_sleep_now_or_later( Manager *m, const char *unit_name, @@ -1160,10 +1198,13 @@ int bus_manager_shutdown_or_sleep_now_or_later( /* Shutdown is delayed, keep in mind what we * want to do, and start a timeout */ r = delay_shutdown_or_sleep(m, w, unit_name); - else + else { + bus_manager_log_shutdown(m, w, unit_name); + /* Shutdown is not delayed, execute it * immediately */ r = send_start_unit(m->bus, unit_name, error); + } return r; } @@ -2256,6 +2297,8 @@ int manager_dispatch_delayed(Manager *manager) { if (delayed) return 0; + bus_manager_log_shutdown(manager, manager->delayed_what, manager->delayed_unit); + /* Reset delay data */ unit_name = manager->delayed_unit; manager->delayed_unit = NULL; diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c index 045712192a..937315ebf1 100644 --- a/src/login/logind-seat.c +++ b/src/login/logind-seat.c @@ -27,6 +27,8 @@ #include #include +#include "systemd/sd-id128.h" +#include "systemd/sd-messages.h" #include "logind-seat.h" #include "logind-acl.h" #include "util.h" @@ -337,7 +339,11 @@ int seat_start(Seat *s) { if (s->started) return 0; - log_info("New seat %s.", s->id); + log_struct(LOG_INFO, + "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(SD_MESSAGE_SEAT_START), + "SEAT_ID=%s", s->id, + "MESSAGE=New seat %s.", s->id, + NULL); /* Initialize VT magic stuff */ seat_preallocate_vts(s); @@ -361,7 +367,11 @@ int seat_stop(Seat *s) { assert(s); if (s->started) - log_info("Removed seat %s.", s->id); + log_struct(LOG_INFO, + "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(SD_MESSAGE_SEAT_STOP), + "SEAT_ID=%s", s->id, + "MESSAGE=Removed seat %s.", s->id, + NULL); seat_stop_sessions(s); diff --git a/src/login/logind-session.c b/src/login/logind-session.c index 16d4955d5d..77462a8d15 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -25,6 +25,8 @@ #include #include +#include "systemd/sd-id128.h" +#include "systemd/sd-messages.h" #include "strv.h" #include "util.h" #include "mkdir.h" @@ -542,8 +544,13 @@ int session_start(Session *s) { if (r < 0) return r; - log_full(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG, - "New session %s of user %s.", s->id, s->user->name); + log_struct(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG, + "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(SD_MESSAGE_SESSION_START), + "SESSION_ID=%s", s->id, + "USER_ID=%s", s->user->name, + "LEADER=%lu", (unsigned long) s->leader, + "MESSAGE=New session %s of user %s.", s->id, s->user->name, + NULL); /* Create cgroup */ r = session_create_cgroup(s); @@ -679,8 +686,13 @@ int session_stop(Session *s) { assert(s); if (s->started) - log_full(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG, - "Removed session %s.", s->id); + log_struct(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG, + "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(SD_MESSAGE_SESSION_STOP), + "SESSION_ID=%s", s->id, + "USER_ID=%s", s->user->name, + "LEADER=%lu", (unsigned long) s->leader, + "MESSAGE=Removed session %s.", s->id, + NULL); /* Kill cgroup */ k = session_terminate_cgroup(s); diff --git a/src/shared/log.c b/src/shared/log.c index 4fc430eed1..67a3e1b843 100644 --- a/src/shared/log.c +++ b/src/shared/log.c @@ -658,11 +658,127 @@ _noreturn_ void log_assert_failed_unreachable(const char *text, const char *file log_assert(text, file, line, func, "Code should not be reached '%s' at %s:%u, function %s(). Aborting."); } -int __log_oom(const char *file, int line, const char *func) { +int log_oom_internal(const char *file, int line, const char *func) { log_meta(LOG_ERR, file, line, func, "Out of memory."); return -ENOMEM; } +int log_struct_internal( + int level, + const char *file, + int line, + const char *func, + const char *format, ...) { + + int saved_errno; + va_list ap; + int r; + + if (_likely_(LOG_PRI(level) > log_max_level)) + return 0; + + if (log_target == LOG_TARGET_NULL) + return 0; + + if ((level & LOG_FACMASK) == 0) + level = log_facility | LOG_PRI(level); + + saved_errno = errno; + + if ((log_target == LOG_TARGET_AUTO || + log_target == LOG_TARGET_JOURNAL_OR_KMSG || + log_target == LOG_TARGET_JOURNAL) && + journal_fd >= 0) { + + char header[LINE_MAX]; + struct iovec iovec[17]; + unsigned n = 0, i; + struct msghdr mh; + const char nl = '\n'; + + /* If the journal is available do structured logging */ + + snprintf(header, sizeof(header), + "PRIORITY=%i\n" + "SYSLOG_FACILITY=%i\n" + "CODE_FILE=%s\n" + "CODE_LINE=%i\n" + "CODE_FUNCTION=%s\n" + "SYSLOG_IDENTIFIER=%s\n", + LOG_PRI(level), + LOG_FAC(level), + file, + line, + func, + program_invocation_short_name); + char_array_0(header); + + zero(iovec); + IOVEC_SET_STRING(iovec[n++], header); + + va_start(ap, format); + while (format && n + 1 < ELEMENTSOF(iovec)) { + char *buf; + + if (vasprintf(&buf, format, ap) < 0) { + r = -ENOMEM; + goto finish; + } + + IOVEC_SET_STRING(iovec[n++], buf); + + iovec[n].iov_base = (char*) &nl; + iovec[n].iov_len = 1; + n++; + + format = va_arg(ap, char *); + } + va_end(ap); + + zero(mh); + mh.msg_iov = iovec; + mh.msg_iovlen = n; + + if (sendmsg(journal_fd, &mh, MSG_NOSIGNAL) < 0) + r = -errno; + else + r = 1; + + finish: + for (i = 1; i < n; i += 2) + free(iovec[i].iov_base); + + } else { + char buf[LINE_MAX]; + bool found = false; + + /* Fallback if journal logging is not available */ + + va_start(ap, format); + while (format) { + + vsnprintf(buf, sizeof(buf), format, ap); + char_array_0(buf); + + if (startswith(buf, "MESSAGE=")) { + found = true; + break; + } + + format = va_arg(ap, char *); + } + va_end(ap); + + if (found) + r = log_dispatch(level, file, line, func, buf + 8); + else + r = -EINVAL; + } + + errno = saved_errno; + return r; +} + int log_set_target_from_string(const char *e) { LogTarget t; diff --git a/src/shared/log.h b/src/shared/log.h index ab894df172..0c60b7671b 100644 --- a/src/shared/log.h +++ b/src/shared/log.h @@ -70,30 +70,51 @@ void log_close_console(void); void log_parse_environment(void); int log_meta( - int level, - const char*file, - int line, - const char *func, - const char *format, ...) _printf_attr_(5,6); + int level, + const char*file, + int line, + const char *func, + const char *format, ...) _printf_attr_(5,6); int log_metav( - int level, - const char*file, - int line, - const char *func, - const char *format, - va_list ap); - -_noreturn_ void log_assert_failed(const char *text, const char *file, int line, const char *func); -_noreturn_ void log_assert_failed_unreachable(const char *text, const char *file, int line, const char *func); + int level, + const char*file, + int line, + const char *func, + const char *format, + va_list ap); + +int log_struct_internal( + int level, + const char *file, + int line, + const char *func, + const char *format, ...) _sentinel_; + +int log_oom_internal( + const char *file, + int line, + const char *func); /* This modifies the buffer passed! */ int log_dump_internal( - int level, - const char*file, - int line, - const char *func, - char *buffer); + int level, + const char*file, + int line, + const char *func, + char *buffer); + +_noreturn_ void log_assert_failed( + const char *text, + const char *file, + int line, + const char *func); + +_noreturn_ void log_assert_failed_unreachable( + const char *text, + const char *file, + int line, + const char *func); #define log_full(level, ...) log_meta(level, __FILE__, __LINE__, __func__, __VA_ARGS__) @@ -103,8 +124,9 @@ int log_dump_internal( #define log_warning(...) log_meta(LOG_WARNING, __FILE__, __LINE__, __func__, __VA_ARGS__) #define log_error(...) log_meta(LOG_ERR, __FILE__, __LINE__, __func__, __VA_ARGS__) -int __log_oom(const char *file, int line, const char *func); -#define log_oom() __log_oom(__FILE__, __LINE__, __func__) +#define log_struct(level, ...) log_struct_internal(level, __FILE__, __LINE__, __func__, __VA_ARGS__) + +#define log_oom() log_oom_internal(__FILE__, __LINE__, __func__) /* This modifies the buffer passed! */ #define log_dump(level, buffer) log_dump_internal(level, __FILE__, __LINE__, __func__, buffer) diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c index c86f69c4aa..71c7518a3b 100644 --- a/src/sleep/sleep.c +++ b/src/sleep/sleep.c @@ -25,6 +25,8 @@ #include "log.h" #include "util.h" +#include "systemd/sd-id128.h" +#include "systemd/sd-messages.h" int main(int argc, char *argv[]) { const char *verb; @@ -66,9 +68,17 @@ int main(int argc, char *argv[]) { execute_directory(SYSTEM_SLEEP_PATH, NULL, arguments); if (streq(argv[1], "suspend")) - log_info("Suspending system..."); + log_struct(LOG_INFO, + "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(SD_MESSAGE_SLEEP_START), + "MESSAGE=Suspending system...", + "SLEEP=suspend", + NULL); else - log_info("Hibernating system..."); + log_struct(LOG_INFO, + "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(SD_MESSAGE_SLEEP_START), + "MESSAGE=Hibernating system...", + "SLEEP=hibernate", + NULL); fputs(verb, f); fputc('\n', f); @@ -77,9 +87,17 @@ int main(int argc, char *argv[]) { r = ferror(f) ? -errno : 0; if (streq(argv[1], "suspend")) - log_info("System resumed."); + log_struct(LOG_INFO, + "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(SD_MESSAGE_SLEEP_STOP), + "MESSAGE=System resumed.", + "SLEEP=suspend", + NULL); else - log_info("System thawed."); + log_struct(LOG_INFO, + "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(SD_MESSAGE_SLEEP_STOP), + "MESSAGE=System thawed.", + "SLEEP=hibernate", + NULL); arguments[1] = (char*) "post"; execute_directory(SYSTEM_SLEEP_PATH, NULL, arguments); diff --git a/src/systemd/sd-id128.h b/src/systemd/sd-id128.h index 27fa479427..126d83ce8a 100644 --- a/src/systemd/sd-id128.h +++ b/src/systemd/sd-id128.h @@ -56,6 +56,7 @@ int sd_id128_get_boot(sd_id128_t *ret); * times. It is hence not a good idea to call this macro with an * expensive function as paramater or an expression with side * effects */ + #define SD_ID128_FORMAT_STR "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" #define SD_ID128_FORMAT_VAL(x) (x).bytes[0], (x).bytes[1], (x).bytes[2], (x).bytes[3], (x).bytes[4], (x).bytes[5], (x).bytes[6], (x).bytes[7], (x).bytes[8], (x).bytes[9], (x).bytes[10], (x).bytes[11], (x).bytes[12], (x).bytes[13], (x).bytes[14], (x).bytes[15] diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h index 9b485b9a8b..01616c8f01 100644 --- a/src/systemd/sd-messages.h +++ b/src/systemd/sd-messages.h @@ -28,12 +28,35 @@ extern "C" { #endif -#define SD_MESSAGE_JOURNAL_START SD_ID128_MAKE(f7,73,79,a8,49,0b,40,8b,be,5f,69,40,50,5a,77,7b) -#define SD_MESSAGE_JOURNAL_STOP SD_ID128_MAKE(d9,3f,b3,c9,c2,4d,45,1a,97,ce,a6,15,ce,59,c0,0b) -#define SD_MESSAGE_JOURNAL_DROPPED SD_ID128_MAKE(a5,96,d6,fe,7b,fa,49,94,82,8e,72,30,9e,95,d6,1e) -#define SD_MESSAGE_JOURNAL_MISSED SD_ID128_MAKE(e9,bf,28,e6,e8,34,48,1b,b6,f4,8f,54,8a,d1,36,06) +#define SD_MESSAGE_JOURNAL_START SD_ID128_MAKE(f7,73,79,a8,49,0b,40,8b,be,5f,69,40,50,5a,77,7b) +#define SD_MESSAGE_JOURNAL_STOP SD_ID128_MAKE(d9,3f,b3,c9,c2,4d,45,1a,97,ce,a6,15,ce,59,c0,0b) +#define SD_MESSAGE_JOURNAL_DROPPED SD_ID128_MAKE(a5,96,d6,fe,7b,fa,49,94,82,8e,72,30,9e,95,d6,1e) +#define SD_MESSAGE_JOURNAL_MISSED SD_ID128_MAKE(e9,bf,28,e6,e8,34,48,1b,b6,f4,8f,54,8a,d1,36,06) -#define SD_MESSAGE_COREDUMP SD_ID128_MAKE(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1) +#define SD_MESSAGE_COREDUMP SD_ID128_MAKE(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1) + +#define SD_MESSAGE_SESSION_START SD_ID128_MAKE(8d,45,62,0c,1a,43,48,db,b1,74,10,da,57,c6,0c,66) +#define SD_MESSAGE_SESSION_STOP SD_ID128_MAKE(33,54,93,94,24,b4,45,6d,98,02,ca,83,33,ed,42,4a) +#define SD_MESSAGE_SEAT_START SD_ID128_MAKE(fc,be,fc,5d,a2,3d,42,80,93,f9,7c,82,a9,29,0f,7b) +#define SD_MESSAGE_SEAT_STOP SD_ID128_MAKE(e7,85,2b,fe,46,78,4e,d0,ac,cd,e0,4b,c8,64,c2,d5) + +#define SD_MESSAGE_TIME_CHANGE SD_ID128_MAKE(c7,a7,87,07,9b,35,4e,aa,a9,e7,7b,37,18,93,cd,27) +#define SD_MESSAGE_TIMEZONE_CHANGE SD_ID128_MAKE(45,f8,2f,4a,ef,7a,4b,bf,94,2c,e8,61,d1,f2,09,90) + +#define SD_MESSAGE_STARTUP_FINISHED SD_ID128_MAKE(b0,7a,24,9c,d0,24,41,4a,82,dd,00,cd,18,13,78,ff) + +#define SD_MESSAGE_SLEEP_START SD_ID128_MAKE(6b,bd,95,ee,97,79,41,e4,97,c4,8b,e2,7c,25,41,28) +#define SD_MESSAGE_SLEEP_STOP SD_ID128_MAKE(88,11,e6,df,2a,8e,40,f5,8a,94,ce,a2,6f,8e,bf,14) + +#define SD_MESSAGE_SHUTDOWN SD_ID128_MAKE(98,26,88,66,d1,d5,4a,49,9c,4e,98,92,1d,93,bc,40) + +#define SD_MESSAGE_UNIT_STARTING SD_ID128_MAKE(7d,49,58,e8,42,da,4a,75,8f,6c,1c,dc,7b,36,dc,c5) +#define SD_MESSAGE_UNIT_STARTED SD_ID128_MAKE(39,f5,34,79,d3,a0,45,ac,8e,11,78,62,48,23,1f,bf) +#define SD_MESSAGE_UNIT_STOPPING SD_ID128_MAKE(de,5b,42,6a,63,be,47,a7,b6,ac,3e,aa,c8,2e,2f,6f) +#define SD_MESSAGE_UNIT_STOPPED SD_ID128_MAKE(9d,1a,aa,27,d6,01,40,bd,96,36,54,38,aa,d2,02,86) +#define SD_MESSAGE_UNIT_FAILED SD_ID128_MAKE(be,02,cf,68,55,d2,42,8b,a4,0d,f7,e9,d0,22,f0,3d) +#define SD_MESSAGE_UNIT_RELOADING SD_ID128_MAKE(be,02,cf,68,55,d2,42,8b,a4,0d,f7,e9,d0,22,f0,3d) +#define SD_MESSAGE_UNIT_RELOADED SD_ID128_MAKE(be,02,cf,68,55,d2,42,8b,a4,0d,f7,e9,d0,22,f0,3d) #ifdef __cplusplus } diff --git a/src/test/test-log.c b/src/test/test-log.c new file mode 100644 index 0000000000..cc924fa57b --- /dev/null +++ b/src/test/test-log.c @@ -0,0 +1,46 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "log.h" + +int main(int argc, char* argv[]) { + + log_set_target(LOG_TARGET_CONSOLE); + log_open(); + + log_struct(LOG_INFO, + "MESSAGE=Waldo PID=%lu", (unsigned long) getpid(), + "SERVICE=piepapo", + NULL); + + log_set_target(LOG_TARGET_JOURNAL); + log_open(); + + log_struct(LOG_INFO, + "MESSAGE=Foobar PID=%lu", (unsigned long) getpid(), + "SERVICE=foobar", + NULL); + + return 0; +} diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c index 09fd808332..34d287b2a1 100644 --- a/src/timedate/timedated.c +++ b/src/timedate/timedated.c @@ -25,6 +25,8 @@ #include #include +#include "systemd/sd-id128.h" +#include "systemd/sd-messages.h" #include "util.h" #include "strv.h" #include "dbus-common.h" @@ -701,7 +703,11 @@ static DBusHandlerResult timedate_message_handler( hwclock_set_time(tm); } - log_info("Changed timezone to '%s'.", tz.zone); + log_struct(LOG_INFO, + "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(SD_MESSAGE_TIMEZONE_CHANGE), + "TIMEZONE=%s", tz.zone, + "MESSAGE=Changed timezone to '%s'.", tz.zone, + NULL); changed = bus_properties_changed_new( "/org/freedesktop/timedate1", @@ -842,7 +848,11 @@ static DBusHandlerResult timedate_message_handler( hwclock_set_time(tm); - log_info("Changed local time to %s", ctime(&ts.tv_sec)); + log_struct(LOG_INFO, + "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(SD_MESSAGE_TIME_CHANGE), + "REALTIME=%llu", (unsigned long long) timespec_load(&ts), + "MESSAGE=Changed local time to %s", ctime(&ts.tv_sec), + NULL); } } else if (dbus_message_is_method_call(message, "org.freedesktop.timedate1", "SetNTP")) { dbus_bool_t ntp; -- cgit v1.2.3-54-g00ecf From b5b46d599524341ddd7407e5dff1021af8ff5089 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 11 Sep 2012 01:11:32 +0200 Subject: when determining unit file list, include invalid unit names in an "invalid" state --- .gitignore | 1 + Makefile.am | 12 +++++++++-- TODO | 2 ++ src/shared/install.c | 24 +++++++++++----------- src/shared/install.h | 1 + src/systemctl/systemctl.c | 3 ++- src/test/test-unit-file.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 80 insertions(+), 15 deletions(-) create mode 100644 src/test/test-unit-file.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index ca95d7afba..cc904f19ed 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/test-unit-file /test-log /test-journal-verify /test-journal-match diff --git a/Makefile.am b/Makefile.am index df3d3cad9d..135d9f8ab1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1120,13 +1120,15 @@ noinst_PROGRAMS += \ test-install \ test-watchdog \ test-unit-name \ - test-log + test-log \ + test-unit-file TESTS += \ test-job-type \ test-env-replace \ test-strv \ - test-unit-name + test-unit-name \ + test-unit-file test_engine_SOURCES = \ src/test/test-engine.c @@ -1176,6 +1178,12 @@ test_unit_name_SOURCES = \ test_unit_name_LDADD = \ libsystemd-core.la +test_unit_file_SOURCES = \ + src/test/test-unit-file.c + +test_unit_file_LDADD = \ + libsystemd-core.la + test_log_SOURCES = \ src/test/test-log.c diff --git a/TODO b/TODO index ec6d5ea8e8..10de80f91b 100644 --- a/TODO +++ b/TODO @@ -49,6 +49,8 @@ Bugfixes: Features: +* Document word splitting syntax for ExecStart= and friends + * merge: github.com/systemd/python-systemd * when writing journal entries order field items by their address to improve speed on rotating media diff --git a/src/shared/install.c b/src/shared/install.c index ef1c3f584d..1a69337f5a 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -2003,25 +2003,24 @@ int unit_file_get_list( free(f->path); free(f); goto finish; - } else if (r > 0) + } else if (r > 0) { + f->state = UNIT_FILE_ENABLED; goto found; + } r = unit_file_can_install(&paths, root_dir, f->path, true); - if (r < 0) { + if (r == -EINVAL || /* Invalid setting? */ + r == -EBADMSG || /* Invalid format? */ + r == -ENOENT /* Included file not found? */) + f->state = UNIT_FILE_INVALID; + else if (r < 0) { free(f->path); free(f); goto finish; - } else if (r > 0) { + } else if (r > 0) f->state = UNIT_FILE_DISABLED; - goto found; - } else { + else f->state = UNIT_FILE_STATIC; - goto found; - } - - free(f->path); - free(f); - continue; found: r = hashmap_put(h, path_get_file_name(f->path), f); @@ -2051,7 +2050,8 @@ static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = { [UNIT_FILE_MASKED] = "masked", [UNIT_FILE_MASKED_RUNTIME] = "masked-runtime", [UNIT_FILE_STATIC] = "static", - [UNIT_FILE_DISABLED] = "disabled" + [UNIT_FILE_DISABLED] = "disabled", + [UNIT_FILE_INVALID] = "invalid", }; DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState); diff --git a/src/shared/install.h b/src/shared/install.h index f02fa3efbd..55249914b1 100644 --- a/src/shared/install.h +++ b/src/shared/install.h @@ -40,6 +40,7 @@ typedef enum UnitFileState { UNIT_FILE_MASKED_RUNTIME, UNIT_FILE_STATIC, UNIT_FILE_DISABLED, + UNIT_FILE_INVALID, _UNIT_FILE_STATE_MAX, _UNIT_FILE_STATE_INVALID = -1 } UnitFileState; diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 98d0fcb39e..5102c8ee58 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -585,7 +585,8 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) { if (u->state == UNIT_FILE_MASKED || u->state == UNIT_FILE_MASKED_RUNTIME || - u->state == UNIT_FILE_DISABLED) { + u->state == UNIT_FILE_DISABLED || + u->state == UNIT_FILE_INVALID) { on = ansi_highlight_red(true); off = ansi_highlight_red(false); } else if (u->state == UNIT_FILE_ENABLED) { diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c new file mode 100644 index 0000000000..b390c44b05 --- /dev/null +++ b/src/test/test-unit-file.c @@ -0,0 +1,52 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "install.h" +#include "util.h" +#include "macro.h" +#include "hashmap.h" + +int main(int argc, char *argv[]) { + + int r; + Hashmap *h; + Iterator i; + UnitFileList *p; + + h = hashmap_new(string_hash_func, string_compare_func); + assert(h); + + r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h); + log_info("%s", strerror(-r)); + assert(r >= 0); + + HASHMAP_FOREACH(p, h, i) + printf("%s = %s\n", p->path, unit_file_state_to_string(p->state)); + + unit_file_list_free(h); + + return 0; +} -- cgit v1.2.3-54-g00ecf From 636d30a0895f17eca8313d50f9b2fc1ec5e128da Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 21 Sep 2012 12:46:22 +0200 Subject: multi-seat: drop multi-seat-x wrapper, as upstream X can handle multi-seat graphics on its own now --- .gitignore | 1 - Makefile.am | 11 --- src/login/multi-seat-x.c | 196 ----------------------------------------------- 3 files changed, 208 deletions(-) delete mode 100644 src/login/multi-seat-x.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index cc904f19ed..c1459ae607 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,6 @@ /build-aux /test-watchdog /test-journal-send -/systemd-multi-seat-x /systemd-cgtop /systemd-coredump /systemd-cat diff --git a/Makefile.am b/Makefile.am index 36b33c2824..1828d7f11e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3347,17 +3347,6 @@ logind-install-data-hook: INSTALL_DATA_HOOKS += \ logind-install-data-hook -systemd_multi_seat_x_SOURCES = \ - src/login/multi-seat-x.c - -systemd_multi_seat_x_LDADD = \ - libsystemd-label.la \ - libsystemd-shared.la \ - libudev.la - -rootlibexec_PROGRAMS += \ - systemd-multi-seat-x - dist_udevrules_DATA += \ src/login/70-uaccess.rules \ src/login/70-power-switch.rules diff --git a/src/login/multi-seat-x.c b/src/login/multi-seat-x.c deleted file mode 100644 index 59f70882d4..0000000000 --- a/src/login/multi-seat-x.c +++ /dev/null @@ -1,196 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see . -***/ - -#include -#include - -#include - -#include "util.h" -#include "mkdir.h" - -int main(int argc, char *argv[]) { - - struct udev *udev = NULL; - struct udev_enumerate *enumerator = NULL; - struct udev_list_entry *first, *item; - int i; - const char *seat = NULL; - char **new_argv; - char *path = NULL, *device_node = NULL; - int r; - FILE *f = NULL; - - /* This binary will go away as soon as X natively supports - * display enumeration with udev in a way that covers both PCI - * and USB. */ - - /* This will simply determine the fb device id of the graphics - * device assigned to a seat and write a configuration file - * from it and then spawn the real X server. */ - - /* If this file is removed, don't forget to remove the code - * that invokes this in gdm and other display managers. */ - - for (i = 1; i < argc; i++) - if (streq(argv[i], "-seat")) - seat = argv[i+1]; - - if (isempty(seat) || streq(seat, "seat0")) { - argv[0] = (char*) X_SERVER; - execv(X_SERVER, argv); - log_error("Failed to execute real X server: %m"); - goto fail; - } - - udev = udev_new(); - if (!udev) { - log_error("Failed to allocate udev environment."); - goto fail; - } - - enumerator = udev_enumerate_new(udev); - if (!enumerator) { - log_error("Failed to allocate udev enumerator."); - goto fail; - } - - udev_enumerate_add_match_subsystem(enumerator, "graphics"); - udev_enumerate_add_match_tag(enumerator, seat); - - r = udev_enumerate_scan_devices(enumerator); - if (r < 0) { - log_error("Failed to enumerate devices."); - goto fail; - } - - first = udev_enumerate_get_list_entry(enumerator); - udev_list_entry_foreach(item, first) { - struct udev_device *d; - const char *dn; - - d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); - if (!d) - continue; - - dn = udev_device_get_devnode(d); - - if (dn) { - device_node = strdup(dn); - if (!device_node) { - udev_device_unref(d); - log_oom(); - goto fail; - } - } - - udev_device_unref(d); - - if (device_node) - break; - } - - if (!device_node) { - log_error("Failed to find device node for seat %s.", seat); - goto fail; - } - - r = mkdir_safe_label("/run/systemd/multi-session-x", 0755, 0, 0); - if (r < 0) { - log_error("Failed to create directory: %s", strerror(-r)); - goto fail; - } - - path = strappend("/run/systemd/multi-session-x/", seat); - if (!path) { - log_oom(); - goto fail; - } - - f = fopen(path, "we"); - if (!f) { - log_error("Failed to write configuration file: %m"); - goto fail; - } - - fprintf(f, - "Section \"Device\"\n" - " Identifier \"udev\"\n" - " Driver \"fbdev\"\n" - " Option \"fbdev\" \"%s\"\n" - "EndSection\n" - "Section \"ServerFlags\"\n" - " Option \"AutoAddDevices\" \"True\"\n" - " Option \"AllowEmptyInput\" \"True\"\n" - " Option \"DontVTSwitch\" \"True\"\n" - "EndSection\n" - "Section \"InputClass\"\n" - " Identifier \"Force Input Devices to Seat\"\n" - " Option \"GrabDevice\" \"True\"\n" - "EndSection\n", - device_node); - - fflush(f); - - if (ferror(f)) { - log_error("Failed to write configuration file: %m"); - goto fail; - } - - fclose(f); - f = NULL; - - new_argv = alloca(sizeof(char*) * (argc + 3 + 1)); - memcpy(new_argv, argv, sizeof(char*) * (argc + 2 + 1)); - - new_argv[0] = (char*) X_SERVER; - new_argv[argc+0] = (char*) "-config"; - new_argv[argc+1] = path; - new_argv[argc+2] = (char*) "-sharevts"; - new_argv[argc+3] = NULL; - - udev_enumerate_unref(enumerator); - enumerator = NULL; - - udev_unref(udev); - udev = NULL; - - free(device_node); - device_node = NULL; - - execv(X_SERVER, new_argv); - log_error("Failed to execute real X server: %m"); - -fail: - if (enumerator) - udev_enumerate_unref(enumerator); - - if (udev) - udev_unref(udev); - - free(path); - free(device_node); - - if (f) - fclose(f); - - return EXIT_FAILURE; -} -- cgit v1.2.3-54-g00ecf From ec29187225c0b404f6b9f59a416c4fa0c0afaaff Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 21 Sep 2012 15:51:04 +0200 Subject: Revert "multi-seat: drop multi-seat-x wrapper, as upstream X can handle multi-seat graphics on its own now" This reverts commit 636d30a0895f17eca8313d50f9b2fc1ec5e128da. Turns out we will need the multi-seat wrapper a bit longer, however without the fb-specific bits in it. --- .gitignore | 1 + Makefile.am | 11 +++ src/login/multi-seat-x.c | 196 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 208 insertions(+) create mode 100644 src/login/multi-seat-x.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index c1459ae607..cc904f19ed 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ /build-aux /test-watchdog /test-journal-send +/systemd-multi-seat-x /systemd-cgtop /systemd-coredump /systemd-cat diff --git a/Makefile.am b/Makefile.am index 1828d7f11e..36b33c2824 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3347,6 +3347,17 @@ logind-install-data-hook: INSTALL_DATA_HOOKS += \ logind-install-data-hook +systemd_multi_seat_x_SOURCES = \ + src/login/multi-seat-x.c + +systemd_multi_seat_x_LDADD = \ + libsystemd-label.la \ + libsystemd-shared.la \ + libudev.la + +rootlibexec_PROGRAMS += \ + systemd-multi-seat-x + dist_udevrules_DATA += \ src/login/70-uaccess.rules \ src/login/70-power-switch.rules diff --git a/src/login/multi-seat-x.c b/src/login/multi-seat-x.c new file mode 100644 index 0000000000..59f70882d4 --- /dev/null +++ b/src/login/multi-seat-x.c @@ -0,0 +1,196 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include + +#include "util.h" +#include "mkdir.h" + +int main(int argc, char *argv[]) { + + struct udev *udev = NULL; + struct udev_enumerate *enumerator = NULL; + struct udev_list_entry *first, *item; + int i; + const char *seat = NULL; + char **new_argv; + char *path = NULL, *device_node = NULL; + int r; + FILE *f = NULL; + + /* This binary will go away as soon as X natively supports + * display enumeration with udev in a way that covers both PCI + * and USB. */ + + /* This will simply determine the fb device id of the graphics + * device assigned to a seat and write a configuration file + * from it and then spawn the real X server. */ + + /* If this file is removed, don't forget to remove the code + * that invokes this in gdm and other display managers. */ + + for (i = 1; i < argc; i++) + if (streq(argv[i], "-seat")) + seat = argv[i+1]; + + if (isempty(seat) || streq(seat, "seat0")) { + argv[0] = (char*) X_SERVER; + execv(X_SERVER, argv); + log_error("Failed to execute real X server: %m"); + goto fail; + } + + udev = udev_new(); + if (!udev) { + log_error("Failed to allocate udev environment."); + goto fail; + } + + enumerator = udev_enumerate_new(udev); + if (!enumerator) { + log_error("Failed to allocate udev enumerator."); + goto fail; + } + + udev_enumerate_add_match_subsystem(enumerator, "graphics"); + udev_enumerate_add_match_tag(enumerator, seat); + + r = udev_enumerate_scan_devices(enumerator); + if (r < 0) { + log_error("Failed to enumerate devices."); + goto fail; + } + + first = udev_enumerate_get_list_entry(enumerator); + udev_list_entry_foreach(item, first) { + struct udev_device *d; + const char *dn; + + d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); + if (!d) + continue; + + dn = udev_device_get_devnode(d); + + if (dn) { + device_node = strdup(dn); + if (!device_node) { + udev_device_unref(d); + log_oom(); + goto fail; + } + } + + udev_device_unref(d); + + if (device_node) + break; + } + + if (!device_node) { + log_error("Failed to find device node for seat %s.", seat); + goto fail; + } + + r = mkdir_safe_label("/run/systemd/multi-session-x", 0755, 0, 0); + if (r < 0) { + log_error("Failed to create directory: %s", strerror(-r)); + goto fail; + } + + path = strappend("/run/systemd/multi-session-x/", seat); + if (!path) { + log_oom(); + goto fail; + } + + f = fopen(path, "we"); + if (!f) { + log_error("Failed to write configuration file: %m"); + goto fail; + } + + fprintf(f, + "Section \"Device\"\n" + " Identifier \"udev\"\n" + " Driver \"fbdev\"\n" + " Option \"fbdev\" \"%s\"\n" + "EndSection\n" + "Section \"ServerFlags\"\n" + " Option \"AutoAddDevices\" \"True\"\n" + " Option \"AllowEmptyInput\" \"True\"\n" + " Option \"DontVTSwitch\" \"True\"\n" + "EndSection\n" + "Section \"InputClass\"\n" + " Identifier \"Force Input Devices to Seat\"\n" + " Option \"GrabDevice\" \"True\"\n" + "EndSection\n", + device_node); + + fflush(f); + + if (ferror(f)) { + log_error("Failed to write configuration file: %m"); + goto fail; + } + + fclose(f); + f = NULL; + + new_argv = alloca(sizeof(char*) * (argc + 3 + 1)); + memcpy(new_argv, argv, sizeof(char*) * (argc + 2 + 1)); + + new_argv[0] = (char*) X_SERVER; + new_argv[argc+0] = (char*) "-config"; + new_argv[argc+1] = path; + new_argv[argc+2] = (char*) "-sharevts"; + new_argv[argc+3] = NULL; + + udev_enumerate_unref(enumerator); + enumerator = NULL; + + udev_unref(udev); + udev = NULL; + + free(device_node); + device_node = NULL; + + execv(X_SERVER, new_argv); + log_error("Failed to execute real X server: %m"); + +fail: + if (enumerator) + udev_enumerate_unref(enumerator); + + if (udev) + udev_unref(udev); + + free(path); + free(device_node); + + if (f) + fclose(f); + + return EXIT_FAILURE; +} -- cgit v1.2.3-54-g00ecf From 2a2507e60df45b69d69a026661f865b971ff4f29 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 24 Sep 2012 10:18:10 +0200 Subject: journal: add missing test file --- .gitignore | 1 + src/journal/test-mmap-cache.c | 79 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 src/journal/test-mmap-cache.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index cc904f19ed..db7fcdfcff 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/test-mmap-cache /test-unit-file /test-log /test-journal-verify diff --git a/src/journal/test-mmap-cache.c b/src/journal/test-mmap-cache.c new file mode 100644 index 0000000000..e2ffaf4723 --- /dev/null +++ b/src/journal/test-mmap-cache.c @@ -0,0 +1,79 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "log.h" +#include "macro.h" +#include "util.h" +#include "mmap-cache.h" + +int main(int argc, char *argv[]) { + int x, y, z, r; + char px[] = "/tmp/testmmapXXXXXXX", py[] = "/tmp/testmmapYXXXXXX", pz[] = "/tmp/testmmapZXXXXXX"; + MMapCache *m; + void *p, *q; + + assert_se(m = mmap_cache_new()); + + x = mkstemp(px); + assert(x >= 0); + unlink(px); + + y = mkstemp(py); + assert(y >= 0); + unlink(py); + + z = mkstemp(pz); + assert(z >= 0); + unlink(pz); + + r = mmap_cache_get(m, x, PROT_READ, 0, false, 1, 2, NULL, &p); + assert(r >= 0); + + r = mmap_cache_get(m, x, PROT_READ, 0, false, 2, 2, NULL, &q); + assert(r >= 0); + + assert((uint8_t*) p + 1 == (uint8_t*) q); + + r = mmap_cache_get(m, x, PROT_READ, 1, false, 3, 2, NULL, &q); + assert(r >= 0); + + assert((uint8_t*) p + 2 == (uint8_t*) q); + + r = mmap_cache_get(m, x, PROT_READ, 0, false, 16ULL*1024ULL*1024ULL, 2, NULL, &p); + assert(r >= 0); + + r = mmap_cache_get(m, x, PROT_READ, 1, false, 16ULL*1024ULL*1024ULL+1, 2, NULL, &q); + assert(r >= 0); + + assert((uint8_t*) p + 1 == (uint8_t*) q); + + mmap_cache_unref(m); + + close_nointr_nofail(x); + close_nointr_nofail(y); + close_nointr_nofail(z); + + return 0; +} -- cgit v1.2.3-54-g00ecf From 7b17a7d72f5ba5ad838b19803534c56a46f3bce9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 28 Sep 2012 00:46:32 +0200 Subject: journal: add minimal journal gateway daemon based on GNU libmicrohttpd This minimal HTTP server can serve journal data via HTTP. Its primary purpose is synchronization of journal data across the network. It serves journal data in three formats: text/plain: the text format known from /var/log/messages application/json: the journal entries formatted as JSON application/vnd.fdo.journal: the binary export format of the journal The HTTP server also serves a small HTML5 app that makes use of the JSON serialization to present the journal data to the user. Examples: This downloads the journal in text format: # systemctl start systemd-journal-gatewayd.service # wget http://localhost:19531/entries Same for JSON: # curl -H"Accept: application/json" http://localhost:19531/entries Access via web browser: $ firefox http://localhost:19531/ --- .gitignore | 2 + Makefile.am | 37 ++ README | 3 + configure.ac | 13 + src/journal/journal-gatewayd.c | 623 ++++++++++++++++++++++++++++++ units/.gitignore | 1 + units/systemd-journal-gatewayd.service.in | 16 + units/systemd-journal-gatewayd.socket | 15 + 8 files changed, 710 insertions(+) create mode 100644 src/journal/journal-gatewayd.c create mode 100644 units/systemd-journal-gatewayd.service.in create mode 100644 units/systemd-journal-gatewayd.socket (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index db7fcdfcff..13d2df4652 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +/install-tree +/systemd-journal-gatewayd /test-mmap-cache /test-unit-file /test-log diff --git a/Makefile.am b/Makefile.am index be92356f67..f7249987d1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2651,6 +2651,43 @@ EXTRA_DIST += \ CLEANFILES += \ src/journal/journald-gperf.c +if HAVE_MICROHTTPD + +gatewayddocumentrootdir=$(pkgdatadir)/gatewayd + +rootlibexec_PROGRAMS += \ + systemd-journal-gatewayd + +systemd_journal_gatewayd_SOURCES = \ + src/journal/journal-gatewayd.c + +systemd_journal_gatewayd_LDADD = \ + libsystemd-shared.la \ + libsystemd-logs.la \ + libsystemd-journal-internal.la \ + libsystemd-id128-internal.la \ + libsystemd-daemon.la \ + $(MICROHTTPD_LIBS) + +systemd_journal_gatewayd_CFLAGS = \ + -DDOCUMENT_ROOT=\"$(gatewayddocumentrootdir)\" \ + $(AM_CFLAGS) \ + $(MICROHTTPD_CFLAGS) + +EXTRA_DIST += \ + units/systemd-journal-gatewayd.service.in + +dist_systemunit_DATA += \ + units/systemd-journal-gatewayd.socket + +nodist_systemunit_DATA += \ + units/systemd-journal-gatewayd.service + +dist_gatewayddocumentroot_DATA = \ + src/journal/browse.html + +endif + # ------------------------------------------------------------------------------ if ENABLE_COREDUMP systemd_coredump_SOURCES = \ diff --git a/README b/README index 334c597213..84ca3c0aea 100644 --- a/README +++ b/README @@ -48,6 +48,9 @@ REQUIREMENTS: libselinux (optional) liblzma (optional) tcpwrappers (optional) + libgcrypt (optional) + libqrencode (optional) + libmicrohttpd (optional) When you build from git you need the following additional dependencies: diff --git a/configure.ac b/configure.ac index 34eb5a8892..79ce5944f3 100644 --- a/configure.ac +++ b/configure.ac @@ -424,6 +424,18 @@ if test "x$enable_qrencode" != "xno"; then fi AM_CONDITIONAL(HAVE_QRENCODE, [test "$have_qrencode" = "yes"]) +# ------------------------------------------------------------------------------ +have_microhttpd=no +AC_ARG_ENABLE(microhttpd, AS_HELP_STRING([--disable-microhttpd], [disable microhttpd support])) +if test "x$enable_microhttpd" != "xno"; then + PKG_CHECK_MODULES(MICROHTTPD, [ libmicrohttpd ], + [AC_DEFINE(HAVE_MICROHTTPD, 1, [Define if microhttpd is available]) have_microhttpd=yes], have_microhttpd=no) + if test "x$have_microhttpd" = xno -a "x$enable_microhttpd" = xyes; then + AC_MSG_ERROR([*** microhttpd support requested but libraries not found]) + fi +fi +AM_CONDITIONAL(HAVE_MICROHTTPD, [test "$have_microhttpd" = "yes"]) + # ------------------------------------------------------------------------------ have_binfmt=no AC_ARG_ENABLE(binfmt, AS_HELP_STRING([--disable-binfmt], [disable binfmt tool])) @@ -803,6 +815,7 @@ AC_MSG_RESULT([ ACL: ${have_acl} GCRYPT: ${have_gcrypt} QRENCODE: ${have_qrencode} + MICROHTTPD: ${have_microhttpd} binfmt: ${have_binfmt} vconsole: ${have_vconsole} readahead: ${have_readahead} diff --git a/src/journal/journal-gatewayd.c b/src/journal/journal-gatewayd.c new file mode 100644 index 0000000000..b7acfba99f --- /dev/null +++ b/src/journal/journal-gatewayd.c @@ -0,0 +1,623 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include + +#include "log.h" +#include "util.h" +#include "sd-journal.h" +#include "sd-daemon.h" +#include "logs-show.h" +#include "virt.h" + +typedef struct RequestMeta { + sd_journal *journal; + + OutputMode mode; + + char *cursor; + int64_t n_skip; + uint64_t n_entries; + bool n_entries_set; + + FILE *tmp; + uint64_t delta, size; +} RequestMeta; + +static const char* const mime_types[_OUTPUT_MODE_MAX] = { + [OUTPUT_SHORT] = "text/plain", + [OUTPUT_JSON] = "application/json", + [OUTPUT_EXPORT] = "application/vnd.fdo.journal" +}; + +static RequestMeta *request_meta(void **connection_cls) { + RequestMeta *m; + + if (*connection_cls) + return *connection_cls; + + m = new0(RequestMeta, 1); + if (!m) + return NULL; + + *connection_cls = m; + return m; +} + +static void request_meta_free( + void *cls, + struct MHD_Connection *connection, + void **connection_cls, + enum MHD_RequestTerminationCode toe) { + + RequestMeta *m = *connection_cls; + + if (!m) + return; + + if (m->journal) + sd_journal_close(m->journal); + + if (m->tmp) + fclose(m->tmp); + + free(m->cursor); + free(m); +} + +static int open_journal(RequestMeta *m) { + assert(m); + + if (m->journal) + return 0; + + return sd_journal_open(&m->journal, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM_ONLY); +} + + +static int respond_oom(struct MHD_Connection *connection) { + struct MHD_Response *response; + const char m[] = "Out of memory.\n"; + int ret; + + assert(connection); + + response = MHD_create_response_from_buffer(sizeof(m)-1, (char*) m, MHD_RESPMEM_PERSISTENT); + if (!response) + return MHD_NO; + + MHD_add_response_header(response, "Content-Type", "text/plain"); + ret = MHD_queue_response(connection, MHD_HTTP_SERVICE_UNAVAILABLE, response); + MHD_destroy_response(response); + + return ret; +} + +static int respond_error( + struct MHD_Connection *connection, + unsigned code, + const char *format, ...) { + + struct MHD_Response *response; + char *m; + int r; + va_list ap; + + assert(connection); + assert(format); + + va_start(ap, format); + r = vasprintf(&m, format, ap); + va_end(ap); + + if (r < 0) + return respond_oom(connection); + + response = MHD_create_response_from_buffer(strlen(m), m, MHD_RESPMEM_MUST_FREE); + if (!response) { + free(m); + return respond_oom(connection); + } + + MHD_add_response_header(response, "Content-Type", "text/plain"); + r = MHD_queue_response(connection, code, response); + MHD_destroy_response(response); + + return r; +} + +static ssize_t request_reader_entries( + void *cls, + uint64_t pos, + char *buf, + size_t max) { + + RequestMeta *m = cls; + int r; + size_t n, k; + + assert(m); + assert(buf); + assert(max > 0); + assert(pos >= m->delta); + + pos -= m->delta; + + while (pos >= m->size) { + off_t sz; + + /* End of this entry, so let's serialize the next + * one */ + + if (m->n_entries_set && + m->n_entries <= 0) + return MHD_CONTENT_READER_END_OF_STREAM; + + if (m->n_skip < 0) { + r = sd_journal_previous_skip(m->journal, (uint64_t) -m->n_skip); + + /* We couldn't seek this far backwards? Then + * let's try to look forward... */ + if (r == 0) + r = sd_journal_next(m->journal); + + } else if (m->n_skip > 0) + r = sd_journal_next_skip(m->journal, (uint64_t) m->n_skip + 1); + else + r = sd_journal_next(m->journal); + + if (r < 0) { + log_error("Failed to advance journal pointer: %s", strerror(-r)); + return MHD_CONTENT_READER_END_WITH_ERROR; + } else if (r == 0) + return MHD_CONTENT_READER_END_OF_STREAM; + + pos -= m->size; + m->delta += m->size; + + if (m->n_entries_set) + m->n_entries -= 1; + + m->n_skip = 0; + + if (m->tmp) + rewind(m->tmp); + else { + m->tmp = tmpfile(); + if (!m->tmp) { + log_error("Failed to create temporary file: %m"); + return MHD_CONTENT_READER_END_WITH_ERROR;; + } + } + + r = output_journal(m->tmp, m->journal, m->mode, 0, OUTPUT_FULL_WIDTH); + if (r < 0) { + log_error("Failed to serialize item: %s", strerror(-r)); + return MHD_CONTENT_READER_END_WITH_ERROR; + } + + sz = ftello(m->tmp); + if (sz == (off_t) -1) { + log_error("Failed to retrieve file position: %m"); + return MHD_CONTENT_READER_END_WITH_ERROR; + } + + m->size = (uint64_t) sz; + } + + if (fseeko(m->tmp, pos, SEEK_SET) < 0) { + log_error("Failed to seek to position: %m"); + return MHD_CONTENT_READER_END_WITH_ERROR; + } + + n = m->size - pos; + if (n > max) + n = max; + + errno = 0; + k = fread(buf, 1, n, m->tmp); + if (k != n) { + log_error("Failed to read from file: %s", errno ? strerror(errno) : "Premature EOF"); + return MHD_CONTENT_READER_END_WITH_ERROR; + } + + return (ssize_t) k; +} + +static int request_parse_accept( + RequestMeta *m, + struct MHD_Connection *connection) { + + const char *accept; + + assert(m); + assert(connection); + + accept = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Accept"); + if (!accept) + return 0; + + if (streq(accept, mime_types[OUTPUT_JSON])) + m->mode = OUTPUT_JSON; + else if (streq(accept, mime_types[OUTPUT_EXPORT])) + m->mode = OUTPUT_EXPORT; + else + m->mode = OUTPUT_SHORT; + + return 0; +} + +static int request_parse_range( + RequestMeta *m, + struct MHD_Connection *connection) { + + const char *range, *colon, *colon2; + int r; + + assert(m); + assert(connection); + + range = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Range"); + if (!range) + return 0; + + if (!startswith(range, "entries=")) + return 0; + + range += 8; + range += strspn(range, WHITESPACE); + + colon = strchr(range, ':'); + if (!colon) + m->cursor = strdup(range); + else { + const char *p; + + colon2 = strchr(colon + 1, ':'); + if (colon2) { + char *t; + + t = strndup(colon + 1, colon2 - colon - 1); + if (!t) + return -ENOMEM; + + r = safe_atoi64(t, &m->n_skip); + free(t); + if (r < 0) + return r; + } + + p = (colon2 ? colon2 : colon) + 1; + if (*p) { + r = safe_atou64(p, &m->n_entries); + if (r < 0) + return r; + + if (m->n_entries <= 0) + return -EINVAL; + + m->n_entries_set = true; + } + + m->cursor = strndup(range, colon - range); + } + + if (!m->cursor) + return -ENOMEM; + + m->cursor[strcspn(m->cursor, WHITESPACE)] = 0; + if (isempty(m->cursor)) { + free(m->cursor); + m->cursor = NULL; + } + + return 0; +} + +static int request_handler_entries( + struct MHD_Connection *connection, + void **connection_cls) { + + struct MHD_Response *response; + RequestMeta *m; + int r; + + assert(connection); + assert(connection_cls); + + m = request_meta(connection_cls); + if (!m) + return respond_oom(connection); + + r = open_journal(m); + if (r < 0) + return respond_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %s\n", strerror(-r)); + + if (request_parse_accept(m, connection) < 0) + return respond_error(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Accept header.\n"); + + if (request_parse_range(m, connection) < 0) + return respond_error(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Range header.\n"); + + /* log_info("cursor = %s", m->cursor); */ + /* log_info("skip = %lli", m->n_skip); */ + /* if (!m->n_entries_set) */ + /* log_info("n_entries not set!"); */ + /* else */ + /* log_info("n_entries = %llu", m->n_entries); */ + + if (m->cursor) + r = sd_journal_seek_cursor(m->journal, m->cursor); + else if (m->n_skip >= 0) + r = sd_journal_seek_head(m->journal); + else if (m->n_skip < 0) + r = sd_journal_seek_tail(m->journal); + if (r < 0) + return respond_error(connection, MHD_HTTP_BAD_REQUEST, "Failed to seek in journal.\n"); + + response = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 4*1024, request_reader_entries, m, NULL); + if (!response) + return respond_oom(connection); + + MHD_add_response_header(response, "Content-Type", mime_types[m->mode]); + + r = MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + + return r; +} + +static int request_handler_redirect( + struct MHD_Connection *connection, + const char *target) { + + char *page; + struct MHD_Response *response; + int ret; + + assert(connection); + assert(page); + + if (asprintf(&page, "Please continue to the journal browser.", target) < 0) + return respond_oom(connection); + + response = MHD_create_response_from_buffer(strlen(page), page, MHD_RESPMEM_MUST_FREE); + if (!response) { + free(page); + return respond_oom(connection); + } + + MHD_add_response_header(response, "Content-Type", "text/html"); + MHD_add_response_header(response, "Location", target); + + ret = MHD_queue_response(connection, MHD_HTTP_MOVED_PERMANENTLY, response); + MHD_destroy_response(response); + + return ret; +} + +static int request_handler_file( + struct MHD_Connection *connection, + const char *path, + const char *mime_type) { + + struct MHD_Response *response; + int ret; + _cleanup_close_ int fd = -1; + struct stat st; + + assert(connection); + assert(path); + assert(mime_type); + + fd = open(path, O_RDONLY|O_CLOEXEC); + if (fd < 0) + return respond_error(connection, MHD_HTTP_NOT_FOUND, "Failed to open file %s: %m\n", path); + + if (fstat(fd, &st) < 0) + return respond_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to stat file: %m\n"); + + response = MHD_create_response_from_fd_at_offset(st.st_size, fd, 0); + if (!response) + return respond_oom(connection); + + fd = -1; + + MHD_add_response_header(response, "Content-Type", mime_type); + + ret = MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + + return ret; +} + +static int request_handler_machine( + struct MHD_Connection *connection, + void **connection_cls) { + + struct MHD_Response *response; + RequestMeta *m; + int r; + _cleanup_free_ char* hostname = NULL, *os_name = NULL; + uint64_t cutoff_from, cutoff_to, usage; + char *json; + sd_id128_t mid, bid; + const char *v = "bare"; + + assert(connection); + + m = request_meta(connection_cls); + if (!m) + return respond_oom(connection); + + r = open_journal(m); + if (r < 0) + return respond_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %s\n", strerror(-r)); + + r = sd_id128_get_machine(&mid); + if (r < 0) + return respond_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine machine ID: %s\n", strerror(-r)); + + r = sd_id128_get_boot(&bid); + if (r < 0) + return respond_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine boot ID: %s\n", strerror(-r)); + + hostname = gethostname_malloc(); + if (!hostname) + return respond_oom(connection); + + r = sd_journal_get_usage(m->journal, &usage); + if (r < 0) + return respond_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine disk usage: %s\n", strerror(-r)); + + r = sd_journal_get_cutoff_realtime_usec(m->journal, &cutoff_from, &cutoff_to); + if (r < 0) + return respond_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine disk usage: %s\n", strerror(-r)); + + parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &os_name, NULL); + + detect_virtualization(&v); + + r = asprintf(&json, + "{ \"machine_id\" : \"" SD_ID128_FORMAT_STR "\"," + "\"boot_id\" : \"" SD_ID128_FORMAT_STR "\"," + "\"hostname\" : \"%s\"," + "\"os_pretty_name\" : \"%s\"," + "\"virtualization\" : \"%s\"," + "\"usage\" : \"%llu\"," + "\"cutoff_from_realtime\" : \"%llu\"," + "\"cutoff_to_realtime\" : \"%llu\" }\n", + SD_ID128_FORMAT_VAL(mid), + SD_ID128_FORMAT_VAL(bid), + hostname_cleanup(hostname), + os_name ? os_name : "Linux", + v, + (unsigned long long) usage, + (unsigned long long) cutoff_from, + (unsigned long long) cutoff_to); + + if (r < 0) + return respond_oom(connection); + + response = MHD_create_response_from_buffer(strlen(json), json, MHD_RESPMEM_MUST_FREE); + if (!response) { + free(json); + return respond_oom(connection); + } + + MHD_add_response_header(response, "Content-Type", "application/json"); + r = MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_destroy_response(response); + + return r; +} + +static int request_handler( + void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t *upload_data_size, + void **connection_cls) { + + assert(connection); + assert(url); + assert(method); + + if (!streq(method, "GET")) + return MHD_NO; + + if (streq(url, "/")) + return request_handler_redirect(connection, "/browse"); + + if (streq(url, "/entries")) + return request_handler_entries(connection, connection_cls); + + if (streq(url, "/browse")) + return request_handler_file(connection, DOCUMENT_ROOT "/browse.html", "text/html"); + + if (streq(url, "/machine")) + return request_handler_machine(connection, connection_cls); + + return respond_error(connection, MHD_HTTP_NOT_FOUND, "Not found.\n"); +} + +int main(int argc, char *argv[]) { + struct MHD_Daemon *daemon = NULL; + int r = EXIT_FAILURE, n; + + if (argc > 1) { + log_error("This program does not take arguments."); + goto finish; + } + + log_set_target(LOG_TARGET_KMSG); + log_parse_environment(); + log_open(); + + n = sd_listen_fds(1); + if (n < 0) { + log_error("Failed to determine passed sockets: %s", strerror(-n)); + goto finish; + } else if (n > 1) { + log_error("Can't listen on more than one socket."); + goto finish; + } else if (n > 0) { + daemon = MHD_start_daemon( + MHD_USE_THREAD_PER_CONNECTION|MHD_USE_POLL|MHD_USE_DEBUG, + 19531, + NULL, NULL, + request_handler, NULL, + MHD_OPTION_LISTEN_SOCKET, SD_LISTEN_FDS_START, + MHD_OPTION_NOTIFY_COMPLETED, request_meta_free, NULL, + MHD_OPTION_END); + } else { + daemon = MHD_start_daemon( + MHD_USE_DEBUG|MHD_USE_THREAD_PER_CONNECTION|MHD_USE_POLL, + 19531, + NULL, NULL, + request_handler, NULL, + MHD_OPTION_NOTIFY_COMPLETED, request_meta_free, NULL, + MHD_OPTION_END); + } + + if (!daemon) { + log_error("Failed to start daemon!"); + goto finish; + } + + pause(); + + r = EXIT_SUCCESS; + +finish: + if (daemon) + MHD_stop_daemon(daemon); + + return r; +} diff --git a/units/.gitignore b/units/.gitignore index 74bff5431f..c72e2cbee3 100644 --- a/units/.gitignore +++ b/units/.gitignore @@ -1,3 +1,4 @@ +/systemd-journal-gatewayd.service /systemd-journal-flush.service /systemd-hibernate.service /systemd-suspend.service diff --git a/units/systemd-journal-gatewayd.service.in b/units/systemd-journal-gatewayd.service.in new file mode 100644 index 0000000000..c3b5c725bf --- /dev/null +++ b/units/systemd-journal-gatewayd.service.in @@ -0,0 +1,16 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Journal Gateway Service +Requires=systemd-journal-gatewayd.socket + +[Service] +ExecStart=@rootlibexecdir@/systemd-journal-gatewayd + +[Install] +Also=systemd-journal-gatewayd.socket diff --git a/units/systemd-journal-gatewayd.socket b/units/systemd-journal-gatewayd.socket new file mode 100644 index 0000000000..fd11058ab4 --- /dev/null +++ b/units/systemd-journal-gatewayd.socket @@ -0,0 +1,15 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Journal Gateway Service Socket + +[Socket] +ListenStream=19531 + +[Install] +WantedBy=sockets.target -- cgit v1.2.3-54-g00ecf From cfbc22abd0525570a6e58968d518ea9a7d0403ba Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 11 Oct 2012 16:42:46 +0200 Subject: journalctl: implement --since= and --until for filtering by time --- .gitignore | 1 + Makefile.am | 12 +++- TODO | 2 + man/journalctl.xml | 35 +++++++++- src/journal/journalctl.c | 125 ++++++++++++++++++++++++--------- src/shared/util.c | 175 +++++++++++++++++++++++++++++++++++++++++++++-- src/shared/util.h | 2 + src/test/test-date.c | 69 +++++++++++++++++++ 8 files changed, 380 insertions(+), 41 deletions(-) create mode 100644 src/test/test-date.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 13d2df4652..71359ce6f2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/test-date /install-tree /systemd-journal-gatewayd /test-mmap-cache diff --git a/Makefile.am b/Makefile.am index c23afdf173..e332183a64 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1157,14 +1157,16 @@ noinst_PROGRAMS += \ test-watchdog \ test-unit-name \ test-log \ - test-unit-file + test-unit-file \ + test-date TESTS += \ test-job-type \ test-env-replace \ test-strv \ test-unit-name \ - test-unit-file + test-unit-file \ + test-data test_engine_SOURCES = \ src/test/test-engine.c @@ -1226,6 +1228,12 @@ test_log_SOURCES = \ test_log_LDADD = \ libsystemd-core.la +test_date_SOURCES = \ + src/test/test-date.c + +test_date_LDADD = \ + libsystemd-core.la + test_daemon_SOURCES = \ src/test/test-daemon.c diff --git a/TODO b/TODO index 5943c9a48f..f3f66ff9a1 100644 --- a/TODO +++ b/TODO @@ -19,6 +19,8 @@ F18: Features: +* _SOURCE_MONOTONIC_TIMESTAMP entries from the kernel seem to be off by 1000000 + * document unit_name_mangle() * add new command to systemctl: "systemctl system-reexec" which reexecs as many daemons as virtually possible diff --git a/man/journalctl.xml b/man/journalctl.xml index 62373d88ab..3786fdf514 100644 --- a/man/journalctl.xml +++ b/man/journalctl.xml @@ -264,11 +264,42 @@ - Jump to the location - in the journal specified by the passed + Start showing entries + from the location in the journal + specified by the passed cursor. + + + + + Start showing entries + newer or of the specified date, + resp. older or of the specified + date. Date specifications should be of + the format "2012-10-30 18:17:16". If + the time part is omitted, 00:00:00 is + assumed. If only the seconds component + is omitted, :00 is assumed. If the + date component is ommitted, the + current day is assumed. Alternatively + the strings + yesterday, + today, + tomorrow are + understood, which refer to 00:00:00 of + the day before the current day, the + current day, resp the day after the + current day. now + refers to the current time. Finally, + relative times may be specified, + prefixed with - or + +, referring to + times before resp. after the current + time. + + diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 04cebff547..54ee6d8cc8 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -58,7 +58,7 @@ static OutputMode arg_output = OUTPUT_SHORT; static bool arg_follow = false; static bool arg_show_all = false; static bool arg_no_pager = false; -static int arg_lines = -1; +static unsigned arg_lines = 0; static bool arg_no_tail = false; static bool arg_quiet = false; static bool arg_merge = false; @@ -70,6 +70,8 @@ static const char *arg_verify_key = NULL; #ifdef HAVE_GCRYPT static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC; #endif +static usec_t arg_since, arg_until; +static bool arg_since_set = false, arg_until_set = false; static enum { ACTION_SHOW, @@ -88,7 +90,9 @@ static int help(void) { " --version Show package version\n" " --no-pager Do not pipe output into a pager\n" " -a --all Show all fields, including long and unprintable\n" - " -c --cursor=CURSOR Jump to the specified cursor\n" + " -c --cursor=CURSOR Start showing entries from specified cursor\n" + " --since=DATE Start showing entries newer or of the specified date\n" + " --until=DATE Stop showing entries older or of the specified date\n" " -f --follow Follow journal\n" " -n --lines[=INTEGER] Number of journal entries to show\n" " --no-tail Show all lines, even in follow mode\n" @@ -126,7 +130,9 @@ static int parse_argv(int argc, char *argv[]) { ARG_INTERVAL, ARG_VERIFY, ARG_VERIFY_KEY, - ARG_DISK_USAGE + ARG_DISK_USAGE, + ARG_SINCE, + ARG_UNTIL }; static const struct option options[] = { @@ -151,6 +157,8 @@ static int parse_argv(int argc, char *argv[]) { { "verify-key", required_argument, NULL, ARG_VERIFY_KEY }, { "disk-usage", no_argument, NULL, ARG_DISK_USAGE }, { "cursor", required_argument, NULL, 'c' }, + { "since", required_argument, NULL, ARG_SINCE }, + { "until", required_argument, NULL, ARG_UNTIL }, { NULL, 0, NULL, 0 } }; @@ -197,8 +205,8 @@ static int parse_argv(int argc, char *argv[]) { case 'n': if (optarg) { - r = safe_atoi(optarg, &arg_lines); - if (r < 0 || arg_lines < 0) { + r = safe_atou(optarg, &arg_lines); + if (r < 0 || arg_lines <= 0) { log_error("Failed to parse lines '%s'", optarg); return -EINVAL; } @@ -324,6 +332,24 @@ static int parse_argv(int argc, char *argv[]) { break; } + case ARG_SINCE: + r = parse_timestamp(optarg, &arg_since); + if (r < 0) { + log_error("Failed to parse timestamp: %s", optarg); + return -EINVAL; + } + arg_since_set = true; + break; + + case ARG_UNTIL: + r = parse_timestamp(optarg, &arg_until); + if (r < 0) { + log_error("Failed to parse timestamp: %s", optarg); + return -EINVAL; + } + arg_until_set = true; + break; + case '?': return -EINVAL; @@ -333,9 +359,19 @@ static int parse_argv(int argc, char *argv[]) { } } - if (arg_follow && !arg_no_tail && arg_lines < 0) + if (arg_follow && !arg_no_tail && arg_lines <= 0) arg_lines = 10; + if (arg_since_set && arg_until_set && arg_since_set > arg_until_set) { + log_error("--since= must be before --until=."); + return -EINVAL; + } + + if (arg_cursor && arg_since_set) { + log_error("Please specify either --since= or --cursor=, not both."); + return -EINVAL; + } + return 1; } @@ -734,6 +770,7 @@ int main(int argc, char *argv[]) { sd_id128_t previous_boot_id; bool previous_boot_id_valid = false; bool have_pager; + unsigned n_shown = 0; log_parse_environment(); log_open(); @@ -815,36 +852,24 @@ int main(int argc, char *argv[]) { if (r < 0) goto finish; - if (!arg_quiet) { - usec_t start, end; - char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX]; - - r = sd_journal_get_cutoff_realtime_usec(j, &start, &end); + if (arg_cursor) { + r = sd_journal_seek_cursor(j, arg_cursor); if (r < 0) { - log_error("Failed to get cutoff: %s", strerror(-r)); + log_error("Failed to seek to cursor: %s", strerror(-r)); goto finish; } - if (r > 0) { - if (arg_follow) - printf("Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start)); - else - printf("Logs begin at %s, end at %s.\n", - format_timestamp(start_buf, sizeof(start_buf), start), - format_timestamp(end_buf, sizeof(end_buf), end)); - } - } + r = sd_journal_next(j); - if (arg_cursor) { - r = sd_journal_seek_cursor(j, arg_cursor); + } else if (arg_since_set) { + r = sd_journal_seek_realtime_usec(j, arg_since); if (r < 0) { - log_error("Failed to seek to cursor: %s", strerror(-r)); + log_error("Failed to seek to date: %s", strerror(-r)); goto finish; } - r = sd_journal_next(j); - } else if (arg_lines >= 0) { + } else if (arg_lines > 0) { r = sd_journal_seek_tail(j); if (r < 0) { log_error("Failed to seek to tail: %s", strerror(-r)); @@ -871,12 +896,34 @@ int main(int argc, char *argv[]) { on_tty(); have_pager = !arg_no_pager && !arg_follow && pager_open(); + if (!arg_quiet) { + usec_t start, end; + char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX]; + + r = sd_journal_get_cutoff_realtime_usec(j, &start, &end); + if (r < 0) { + log_error("Failed to get cutoff: %s", strerror(-r)); + goto finish; + } + + if (r > 0) { + if (arg_follow) + printf("---- Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start)); + else + printf("---- Logs begin at %s, end at %s.\n", + format_timestamp(start_buf, sizeof(start_buf), start), + format_timestamp(end_buf, sizeof(end_buf), end)); + } + } + for (;;) { for (;;) { - int flags = - arg_show_all * OUTPUT_SHOW_ALL | - have_pager * OUTPUT_FULL_WIDTH | - on_tty() * OUTPUT_COLOR; + int flags; + + if (arg_lines > 0 && n_shown >= arg_lines) { + r = 0; + goto finish; + } if (need_seek) { r = sd_journal_next(j); @@ -889,6 +936,16 @@ int main(int argc, char *argv[]) { if (r == 0) break; + if (arg_until_set) { + usec_t usec; + + r = sd_journal_get_realtime_usec(j, &usec); + if (r < 0) { + log_error("Failed to determine timestamp: %s", strerror(-r)); + goto finish; + } + } + if (!arg_merge) { sd_id128_t boot_id; @@ -896,18 +953,24 @@ int main(int argc, char *argv[]) { if (r >= 0) { if (previous_boot_id_valid && !sd_id128_equal(boot_id, previous_boot_id)) - printf(ANSI_HIGHLIGHT_ON "----- Reboot -----" ANSI_HIGHLIGHT_OFF "\n"); + printf(ANSI_HIGHLIGHT_ON "---- Reboot ----" ANSI_HIGHLIGHT_OFF "\n"); previous_boot_id = boot_id; previous_boot_id_valid = true; } } + flags = + arg_show_all * OUTPUT_SHOW_ALL | + have_pager * OUTPUT_FULL_WIDTH | + on_tty() * OUTPUT_COLOR; + r = output_journal(stdout, j, arg_output, 0, flags); if (r < 0) goto finish; need_seek = true; + n_shown++; } if (!arg_follow) diff --git a/src/shared/util.c b/src/shared/util.c index 6310aec8aa..15481b6b9d 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -2733,16 +2733,31 @@ int parse_usec(const char *t, usec_t *usec) { const char *suffix; usec_t usec; } table[] = { + { "seconds", USEC_PER_SEC }, + { "second", USEC_PER_SEC }, { "sec", USEC_PER_SEC }, { "s", USEC_PER_SEC }, + { "minutes", USEC_PER_MINUTE }, + { "minute", USEC_PER_MINUTE }, { "min", USEC_PER_MINUTE }, + { "months", USEC_PER_MONTH }, + { "month", USEC_PER_MONTH }, + { "msec", USEC_PER_MSEC }, + { "ms", USEC_PER_MSEC }, + { "m", USEC_PER_MINUTE }, + { "hours", USEC_PER_HOUR }, + { "hour", USEC_PER_HOUR }, { "hr", USEC_PER_HOUR }, { "h", USEC_PER_HOUR }, + { "days", USEC_PER_DAY }, + { "day", USEC_PER_DAY }, { "d", USEC_PER_DAY }, + { "weeks", USEC_PER_WEEK }, + { "week", USEC_PER_WEEK }, { "w", USEC_PER_WEEK }, - { "msec", USEC_PER_MSEC }, - { "ms", USEC_PER_MSEC }, - { "m", USEC_PER_MINUTE }, + { "years", USEC_PER_YEAR }, + { "year", USEC_PER_YEAR }, + { "y", USEC_PER_YEAR }, { "usec", 1ULL }, { "us", 1ULL }, { "", USEC_PER_SEC }, /* default is sec */ @@ -2796,16 +2811,31 @@ int parse_nsec(const char *t, nsec_t *nsec) { const char *suffix; nsec_t nsec; } table[] = { + { "seconds", NSEC_PER_SEC }, + { "second", NSEC_PER_SEC }, { "sec", NSEC_PER_SEC }, { "s", NSEC_PER_SEC }, + { "minutes", NSEC_PER_MINUTE }, + { "minute", NSEC_PER_MINUTE }, { "min", NSEC_PER_MINUTE }, + { "months", NSEC_PER_MONTH }, + { "month", NSEC_PER_MONTH }, + { "msec", NSEC_PER_MSEC }, + { "ms", NSEC_PER_MSEC }, + { "m", NSEC_PER_MINUTE }, + { "hours", NSEC_PER_HOUR }, + { "hour", NSEC_PER_HOUR }, { "hr", NSEC_PER_HOUR }, { "h", NSEC_PER_HOUR }, + { "days", NSEC_PER_DAY }, + { "day", NSEC_PER_DAY }, { "d", NSEC_PER_DAY }, + { "weeks", NSEC_PER_WEEK }, + { "week", NSEC_PER_WEEK }, { "w", NSEC_PER_WEEK }, - { "msec", NSEC_PER_MSEC }, - { "ms", NSEC_PER_MSEC }, - { "m", NSEC_PER_MINUTE }, + { "years", NSEC_PER_YEAR }, + { "year", NSEC_PER_YEAR }, + { "y", NSEC_PER_YEAR }, { "usec", NSEC_PER_USEC }, { "us", NSEC_PER_USEC }, { "nsec", 1ULL }, @@ -5888,3 +5918,136 @@ bool string_is_safe(const char *p) { return true; } + +int parse_timestamp(const char *t, usec_t *usec) { + const char *k; + struct tm tm, copy; + time_t x; + usec_t plus = 0, minus = 0, ret; + int r; + + /* + * Allowed syntaxes: + * + * 2012-09-22 16:34:22 + * 2012-09-22 16:34 (seconds will be set to 0) + * 2012-09-22 (time will be set to 00:00:00) + * 16:34:22 (date will be set to today) + * 16:34 (date will be set to today, seconds to 0) + * now + * yesterday (time is set to 00:00:00) + * today (time is set to 00:00:00) + * tomorrow (time is set to 00:00:00) + * +5min + * -5days + * + */ + + assert(t); + assert(usec); + + x = time(NULL); + assert_se(localtime_r(&x, &tm)); + + if (streq(t, "now")) + goto finish; + + else if (streq(t, "today")) { + tm.tm_sec = tm.tm_min = tm.tm_hour = 0; + goto finish; + + } else if (streq(t, "yesterday")) { + tm.tm_mday --; + tm.tm_sec = tm.tm_min = tm.tm_hour = 0; + goto finish; + + } else if (streq(t, "tomorrow")) { + tm.tm_mday ++; + tm.tm_sec = tm.tm_min = tm.tm_hour = 0; + goto finish; + + } else if (t[0] == '+') { + + r = parse_usec(t+1, &plus); + if (r < 0) + return r; + + goto finish; + } else if (t[0] == '-') { + + r = parse_usec(t+1, &minus); + if (r < 0) + return r; + + goto finish; + } + + copy = tm; + k = strptime(t, "%y-%m-%d %H:%M:%S", &tm); + if (k && *k == 0) + goto finish; + + tm = copy; + k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm); + if (k && *k == 0) + goto finish; + + tm = copy; + k = strptime(t, "%y-%m-%d %H:%M", &tm); + if (k && *k == 0) { + tm.tm_sec = 0; + goto finish; + } + + tm = copy; + k = strptime(t, "%Y-%m-%d %H:%M", &tm); + if (k && *k == 0) { + tm.tm_sec = 0; + goto finish; + } + + tm = copy; + k = strptime(t, "%y-%m-%d", &tm); + if (k && *k == 0) { + tm.tm_sec = tm.tm_min = tm.tm_hour = 0; + goto finish; + } + + tm = copy; + k = strptime(t, "%Y-%m-%d", &tm); + if (k && *k == 0) { + tm.tm_sec = tm.tm_min = tm.tm_hour = 0; + goto finish; + } + + tm = copy; + k = strptime(t, "%H:%M:%S", &tm); + if (k && *k == 0) + goto finish; + + tm = copy; + k = strptime(t, "%H:%M", &tm); + if (k && *k == 0) { + tm.tm_sec = 0; + goto finish; + } + + return -EINVAL; + +finish: + x = mktime(&tm); + if (x == (time_t) -1) + return -EINVAL; + + ret = (usec_t) x * USEC_PER_SEC; + + ret += plus; + if (ret > minus) + ret -= minus; + else + ret = 0; + + *usec = ret; + + return 0; +} diff --git a/src/shared/util.h b/src/shared/util.h index cbded08617..50911ebb34 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -561,3 +561,5 @@ _malloc_ static inline void *memdup_multiply(const void *p, size_t a, size_t b) bool filename_is_safe(const char *p); bool string_is_safe(const char *p); + +int parse_timestamp(const char *t, usec_t *usec); diff --git a/src/test/test-date.c b/src/test/test-date.c new file mode 100644 index 0000000000..57f8b371d3 --- /dev/null +++ b/src/test/test-date.c @@ -0,0 +1,69 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "util.h" + +int main(int argc, char *argv[]) { + + usec_t t; + char buf[FORMAT_TIMESTAMP_MAX]; + + assert_se(parse_timestamp("17:41", &t) >= 0); + log_info("%s", format_timestamp(buf, sizeof(buf), t)); + + assert_se(parse_timestamp("18:42:44", &t) >= 0); + log_info("%s", format_timestamp(buf, sizeof(buf), t)); + + assert_se(parse_timestamp("12-10-02 12:13:14", &t) >= 0); + log_info("%s", format_timestamp(buf, sizeof(buf), t)); + + assert_se(parse_timestamp("12-10-2 12:13:14", &t) >= 0); + log_info("%s", format_timestamp(buf, sizeof(buf), t)); + + assert_se(parse_timestamp("12-10-03 12:13", &t) >= 0); + log_info("%s", format_timestamp(buf, sizeof(buf), t)); + + assert_se(parse_timestamp("2012-12-30 18:42", &t) >= 0); + log_info("%s", format_timestamp(buf, sizeof(buf), t)); + + assert_se(parse_timestamp("2012-10-02", &t) >= 0); + log_info("%s", format_timestamp(buf, sizeof(buf), t)); + + assert_se(parse_timestamp("now", &t) >= 0); + log_info("%s", format_timestamp(buf, sizeof(buf), t)); + + assert_se(parse_timestamp("yesterday", &t) >= 0); + log_info("%s", format_timestamp(buf, sizeof(buf), t)); + + assert_se(parse_timestamp("today", &t) >= 0); + log_info("%s", format_timestamp(buf, sizeof(buf), t)); + + assert_se(parse_timestamp("tomorrow", &t) >= 0); + log_info("%s", format_timestamp(buf, sizeof(buf), t)); + + assert_se(parse_timestamp("+2d", &t) >= 0); + log_info("%s", format_timestamp(buf, sizeof(buf), t)); + + assert_se(parse_timestamp("+2y 4d", &t) >= 0); + log_info("%s", format_timestamp(buf, sizeof(buf), t)); + + return 0; +} -- cgit v1.2.3-54-g00ecf From e88baee88fad8bc59d33b55a7a2d640ef9e16cd6 Mon Sep 17 00:00:00 2001 From: Zbigniew Jędrzejewski-Szmek Date: Sat, 13 Oct 2012 11:31:54 +0200 Subject: journald: properly update message size after stripping the identifier Valgrind says: ==29176== Conditional jump or move depends on uninitialised value(s) ==29176== at 0x412A85: cunescape_length_with_prefix (util.c:1565) ==29176== by 0x40B351: dev_kmsg_record (journald-kmsg.c:301) ==29176== by 0x40B653: server_read_dev_kmsg (journald-kmsg.c:347) ==29176== by 0x40B701: server_flush_dev_kmsg (journald-kmsg.c:365) ==29176== by 0x409DE7: main (journald.c:1535) --- .gitignore | 1 + Makefile.am | 12 +++++++++++ src/journal/journald-kmsg.c | 2 +- src/journal/journald-syslog.c | 7 ++++--- src/journal/journald-syslog.h | 2 +- src/journal/test-journal-syslog.c | 44 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 63 insertions(+), 5 deletions(-) create mode 100644 src/journal/test-journal-syslog.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 71359ce6f2..b9b333d9d9 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ /build-aux /test-watchdog /test-journal-send +/test-journal-syslog /systemd-multi-seat-x /systemd-cgtop /systemd-coredump diff --git a/Makefile.am b/Makefile.am index 1c2514d1b2..ebefac3438 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2462,6 +2462,15 @@ test_journal_send_LDADD = \ libsystemd-journal-internal.la \ libsystemd-id128-internal.la +test_journal_syslog_SOURCES = \ + src/journal/test-journal-syslog.c \ + src/journal/journald-syslog.c + +test_journal_syslog_LDADD = \ + libsystemd-shared.la \ + libsystemd-journal-internal.la \ + libsystemd-id128-internal.la + test_journal_match_SOURCES = \ src/journal/test-journal-match.c @@ -2595,6 +2604,7 @@ UNINSTALL_EXEC_HOOKS += \ noinst_PROGRAMS += \ test-journal \ test-journal-send \ + test-journal-syslog \ test-journal-match \ test-journal-stream \ test-journal-verify \ @@ -2602,6 +2612,8 @@ noinst_PROGRAMS += \ TESTS += \ test-journal \ + test-journal-send \ + test-journal-syslog \ test-journal-match \ test-journal-stream \ test-journal-verify \ diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c index 4f5e7a0c52..548f65311c 100644 --- a/src/journal/journald-kmsg.c +++ b/src/journal/journald-kmsg.c @@ -275,7 +275,7 @@ static void dev_kmsg_record(Server *s, char *p, size_t l) { if ((priority & LOG_FACMASK) == LOG_KERN) IOVEC_SET_STRING(iovec[n++], "SYSLOG_IDENTIFIER=kernel"); else { - syslog_parse_identifier((const char**) &p, &identifier, &pid); + pl -= syslog_parse_identifier((const char**) &p, &identifier, &pid); /* Avoid any messages we generated ourselves via * log_info() and friends. */ diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c index d3e0f6f6c1..c4f81b68f2 100644 --- a/src/journal/journald-syslog.c +++ b/src/journal/journald-syslog.c @@ -185,7 +185,7 @@ int syslog_fixup_facility(int priority) { return priority; } -void syslog_parse_identifier(const char **buf, char **identifier, char **pid) { +size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid) { const char *p; char *t; size_t l, e; @@ -201,7 +201,7 @@ void syslog_parse_identifier(const char **buf, char **identifier, char **pid) { if (l <= 0 || p[l-1] != ':') - return; + return 0; e = l; l--; @@ -231,8 +231,9 @@ void syslog_parse_identifier(const char **buf, char **identifier, char **pid) { if (t) *identifier = t; + e += strspn(p + e, WHITESPACE); *buf = p + e; - *buf += strspn(*buf, WHITESPACE); + return e; } void syslog_parse_priority(char **p, int *priority) { diff --git a/src/journal/journald-syslog.h b/src/journal/journald-syslog.h index e009ab7e72..fa9de1a951 100644 --- a/src/journal/journald-syslog.h +++ b/src/journal/journald-syslog.h @@ -26,7 +26,7 @@ int syslog_fixup_facility(int priority); void syslog_parse_priority(char **p, int *priority); -void syslog_parse_identifier(const char **buf, char **identifier, char **pid); +size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid); void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, struct ucred *ucred, struct timeval *tv); diff --git a/src/journal/test-journal-syslog.c b/src/journal/test-journal-syslog.c new file mode 100644 index 0000000000..3ae8633f22 --- /dev/null +++ b/src/journal/test-journal-syslog.c @@ -0,0 +1,44 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "journald-syslog.h" +#include "macro.h" + +static void test_syslog_parse_identifier(const char* str, + const char *ident, const char*pid, int ret) { + const char *buf = str; + char *ident2 = NULL, *pid2 = NULL; + int ret2; + + ret2 = syslog_parse_identifier(&buf, &ident2, &pid2); + + assert(ret == ret2); + assert(ident==ident2 || !strcmp(ident, ident2)); + assert(pid==pid2 || !strcmp(pid, pid2)); +} + +int main(void) { + test_syslog_parse_identifier("pidu[111]: xxx", "pidu", "111", 11); + test_syslog_parse_identifier("pidu: xxx", "pidu", NULL, 6); + test_syslog_parse_identifier("pidu xxx", NULL, NULL, 0); + + return 0; +} -- cgit v1.2.3-54-g00ecf From 6d0274f11547a0f11200bb82bf598a5a253e12cf Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Oct 2012 02:50:09 +0200 Subject: timedatectl: introduce new command line client for timedated Much like logind has a client in loginctl, and journald in journalctl introduce timedatectl, to change the system time (incl. RTC), timezones and related settings. --- .gitignore | 1 + Makefile.am | 18 ++ TODO | 4 + man/systemd-timedated.service.xml | 5 + man/timedatectl.xml | 243 ++++++++++++++ rules/50-udev-default.rules | 2 +- src/login/loginctl.c | 56 ++-- src/shared/dbus-common.h | 1 + src/shared/macro.h | 1 + src/shared/strv.c | 8 + src/shared/strv.h | 1 + src/shared/util.c | 2 +- src/timedate/timedatectl.c | 649 ++++++++++++++++++++++++++++++++++++++ 13 files changed, 959 insertions(+), 32 deletions(-) create mode 100644 man/timedatectl.xml create mode 100644 src/timedate/timedatectl.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index b9b333d9d9..bc72be8210 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/timedatectl /test-date /install-tree /systemd-journal-gatewayd diff --git a/Makefile.am b/Makefile.am index 0a3ed18a4e..06dd1b1a86 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3194,6 +3194,24 @@ MANPAGES_ALIAS += \ man/systemd-timedated.8 man/systemd-timedated.8: man/systemd-timedated.service.8 + +timedatectl_SOURCES = \ + src/timedate/timedatectl.c + +timedatectl_CFLAGS = \ + $(AM_CFLAGS) \ + $(DBUS_CFLAGS) + +timedatectl_LDADD = \ + libsystemd-shared.la \ + libsystemd-dbus.la + +bin_PROGRAMS += \ + timedatectl + +MANPAGES += \ + man/timedatectl.1 + endif polkitpolicy_in_files += \ diff --git a/TODO b/TODO index 218aad248d..a5a8057d67 100644 --- a/TODO +++ b/TODO @@ -19,6 +19,10 @@ F18: Features: +* timedated: export boolean that clarifies whether NTP is even available + +* timedated: refuse time changes when NTP is on + * journald: don't make SystemMinFileSize= configurable * clean up date formatting and parsing so that all absolute/relative timestamps we format can also be parsed diff --git a/man/systemd-timedated.service.xml b/man/systemd-timedated.service.xml index 47e762284a..ea2abc5765 100644 --- a/man/systemd-timedated.service.xml +++ b/man/systemd-timedated.service.xml @@ -64,6 +64,10 @@ is automatically activated on request and terminates itself when it is unused. + The tool + timedatectl1 + is a command line client to this service. + See the developer documentation for information about @@ -75,6 +79,7 @@ See Also systemd1, + timedatectl1, localtime5, hwclock8 diff --git a/man/timedatectl.xml b/man/timedatectl.xml new file mode 100644 index 0000000000..8fb4e38f37 --- /dev/null +++ b/man/timedatectl.xml @@ -0,0 +1,243 @@ + + + + + + + + + timedatectl + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + timedatectl + 1 + + + + timedatectl + Control the system time and date + + + + + timedatectl OPTIONS COMMAND + + + + + Description + + timedatectl may be used to + query and change the system clock and its + settings. + + + + Options + + The following options are understood: + + + + + + + Prints a short help + text and exits. + + + + + + Prints a short version + string and exits. + + + + + + Do not pipe output into a + pager. + + + + + + Don't query the user + for authentication for privileged + operations. + + + + + + + Execute operation + remotely. Specify a hostname, or + username and hostname separated by @, + to connect to. This will use SSH to + talk to a remote + system. + + + + + + If + set-local-rtc is + invoked and this option is passed the + system clock is synchronized from the + RTC again, taking the new setting into + account. Otherwise the RTC is + synchonized from the system + clock. + + + + The following commands are understood: + + + + status + + Show current settings + of the system clock and + RTC. + + + + set-time [TIME] + + Set the system clock + to the specified time. This will also + update the RTC time accordingly. The time + may be specified in the format + "2012-10-30 + 18:17:16". + + + + set-timezone [TIMEZONE] + + Set the system time + zone to the specified value. Available + time zones my be listed with + list-timezones. If + the RTC is configured to be in the + local time this will also update the + RTC time. + + + + list-timezones + + List available time + zones, one per line. Entries from the + list may selected as the system + timezone with + set-timezone. + + + + set-local-rtc [BOOL] + + Takes a boolean + argument. If 0 the + system is configured to maintain the + RTC in universal time, if + 1 it will maintain + the RTC in local time instead. Note + that maintaining the RTC in the local + timezone is is not fully supported and + will create various problems with time + zone changes and daylight saving + adjustments. If at all possible use + RTC in UTC. Note that invoking this + will also synchronize the RTC from the + system clock, unless + is + passed (see above). This command will + change the 3rd line of + /etc/adjtime, as + documented in + hwclock8. + + + + set-ntp [BOOL] + + Takes a boolean + argument. Controls whether NTP based + network time synchronization is + enabled (if + available). + + + + + + + + Exit status + + On success 0 is returned, a non-zero failure + code otherwise. + + + + Environment + + + + $SYSTEMD_PAGER + Pager to use when + is not given; + overrides $PAGER. Setting + this to an empty string or the value + cat is equivalent to passing + . + + + + + + See Also + + systemd1, + hwclock8, + date1, + systemctl1, + systemd-timedated.service8 + + + + diff --git a/rules/50-udev-default.rules b/rules/50-udev-default.rules index 6e3ab94d54..20ef5fda5a 100644 --- a/rules/50-udev-default.rules +++ b/rules/50-udev-default.rules @@ -83,6 +83,6 @@ KERNEL=="rfkill", MODE="0644" KERNEL=="fuse", ACTION=="add", MODE="0666", OPTIONS+="static_node=fuse" -SUBSYSTEM=="rtc", ATTR{hctosys}=="1", SYMLINK+="rtc" +SUBSYSTEM=="rtc", ATTR{hctosys}=="1", MODE="0644", SYMLINK+="rtc" SUBSYSTEM=="firmware", ACTION=="add", IMPORT{builtin}="firmware" diff --git a/src/login/loginctl.c b/src/login/loginctl.c index 7ef9ddeb13..0d9358d279 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -862,7 +862,7 @@ static int print_property(const char *name, DBusMessageIter *iter) { } static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) { - DBusMessage *reply = NULL; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; const char *interface = ""; int r; DBusMessageIter iter, sub, sub2, sub3; @@ -877,7 +877,7 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo zero(user_info); zero(seat_info); - r = bus_method_call_with_reply ( + r = bus_method_call_with_reply( bus, "org.freedesktop.login1", path, @@ -887,7 +887,7 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID); - if (r) + if (r < 0) goto finish; if (!dbus_message_iter_init(reply, &iter) || @@ -941,7 +941,6 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo if (r < 0) { log_error("Failed to parse reply."); - r = -EIO; goto finish; } @@ -957,14 +956,11 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo print_seat_status_info(&seat_info); } - strv_free(seat_info.sessions); - strv_free(user_info.sessions); - r = 0; finish: - if (reply) - dbus_message_unref(reply); + strv_free(seat_info.sessions); + strv_free(user_info.sessions); return r; } @@ -1339,16 +1335,16 @@ static int help(void) { printf("%s [OPTIONS...] {COMMAND} ...\n\n" "Send control commands to or query the login manager.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " -p --property=NAME Show only properties by this name\n" - " -a --all Show all properties, including empty ones\n" - " --kill-who=WHO Who to send signal to\n" - " -s --signal=SIGNAL Which signal to send\n" - " -H --host=[USER@]HOST\n" - " Show information for remote host\n" - " -P --privileged Acquire privileges before execution\n" - " --no-pager Do not pipe output into a pager\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " -p --property=NAME Show only properties by this name\n" + " -a --all Show all properties, including empty ones\n" + " --kill-who=WHO Who to send signal to\n" + " -s --signal=SIGNAL Which signal to send\n" + " --no-ask-password Don't prompt for password\n" + " -H --host=[USER@]HOST Show information for remote host\n" + " -P --privileged Acquire privileges before execution\n" + " --no-pager Do not pipe output into a pager\n\n" "Commands:\n" " list-sessions List sessions\n" " session-status [ID...] Show session status\n" @@ -1387,17 +1383,17 @@ static int parse_argv(int argc, char *argv[]) { }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "property", required_argument, NULL, 'p' }, - { "all", no_argument, NULL, 'a' }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "kill-who", required_argument, NULL, ARG_KILL_WHO }, - { "signal", required_argument, NULL, 's' }, - { "host", required_argument, NULL, 'H' }, - { "privileged",no_argument, NULL, 'P' }, - { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, - { NULL, 0, NULL, 0 } + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "property", required_argument, NULL, 'p' }, + { "all", no_argument, NULL, 'a' }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "kill-who", required_argument, NULL, ARG_KILL_WHO }, + { "signal", required_argument, NULL, 's' }, + { "host", required_argument, NULL, 'H' }, + { "privileged", no_argument, NULL, 'P' }, + { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, + { NULL, 0, NULL, 0 } }; int c; diff --git a/src/shared/dbus-common.h b/src/shared/dbus-common.h index 394ab4cb60..005a715d0a 100644 --- a/src/shared/dbus-common.h +++ b/src/shared/dbus-common.h @@ -22,6 +22,7 @@ ***/ #include +#include #ifndef DBUS_ERROR_UNKNOWN_OBJECT #define DBUS_ERROR_UNKNOWN_OBJECT "org.freedesktop.DBus.Error.UnknownObject" diff --git a/src/shared/macro.h b/src/shared/macro.h index dbdf5b3fb0..e930fdab53 100644 --- a/src/shared/macro.h +++ b/src/shared/macro.h @@ -192,6 +192,7 @@ static inline size_t IOVEC_INCREMENT(struct iovec *i, unsigned n, size_t k) { #define _cleanup_close_ __attribute__((cleanup(closep))) #define _cleanup_closedir_ __attribute__((cleanup(closedirp))) #define _cleanup_umask_ __attribute__((cleanup(umaskp))) +#define _cleanup_strv_free_ __attribute__((cleanup(strv_freep))) #define VA_FORMAT_ADVANCE(format, ap) \ do { \ diff --git a/src/shared/strv.c b/src/shared/strv.c index c8d856344c..822b2dc888 100644 --- a/src/shared/strv.c +++ b/src/shared/strv.c @@ -64,6 +64,14 @@ void strv_free(char **l) { free(l); } +void strv_freep(char ***l) { + if (!l) + return; + + strv_free(*l); + *l = NULL; +} + char **strv_copy(char **l) { char **r, **k; diff --git a/src/shared/strv.h b/src/shared/strv.h index ae4e31f14c..81e33356f9 100644 --- a/src/shared/strv.h +++ b/src/shared/strv.h @@ -30,6 +30,7 @@ char *strv_find(char **l, const char *name); char *strv_find_prefix(char **l, const char *name); void strv_free(char **l); +void strv_freep(char ***l); char **strv_copy(char **l) _malloc_; unsigned strv_length(char **l); diff --git a/src/shared/util.c b/src/shared/util.c index 1e1eb2af5f..50c4c08993 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -1959,7 +1959,7 @@ char *format_timestamp(char *buf, size_t l, usec_t t) { sec = (time_t) (t / USEC_PER_SEC); - if (strftime(buf, l, "%a, %d %b %Y %H:%M:%S %z", localtime_r(&sec, &tm)) <= 0) + if (strftime(buf, l, "%a, %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) <= 0) return NULL; return buf; diff --git a/src/timedate/timedatectl.c b/src/timedate/timedatectl.c new file mode 100644 index 0000000000..8c6e7c9212 --- /dev/null +++ b/src/timedate/timedatectl.c @@ -0,0 +1,649 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include "dbus-common.h" +#include "util.h" +#include "spawn-polkit-agent.h" +#include "build.h" +#include "hwclock.h" +#include "strv.h" +#include "pager.h" + +static bool arg_fix_system = false; +static bool arg_no_pager = false; +static enum transport { + TRANSPORT_NORMAL, + TRANSPORT_SSH, + TRANSPORT_POLKIT +} arg_transport = TRANSPORT_NORMAL; +static bool arg_ask_password = true; +static const char *arg_host = NULL; + +static void pager_open_if_enabled(void) { + + if (arg_no_pager) + return; + + pager_open(); +} + +static void polkit_agent_open_if_enabled(void) { + + /* Open the polkit agent as a child process if necessary */ + + if (!arg_ask_password) + return; + + polkit_agent_open(); +} + +typedef struct StatusInfo { + const char *timezone; + bool local_rtc; + bool ntp; +} StatusInfo; + +static bool ntp_synced(void) { + + struct timex txc; + + zero(txc); + + if (adjtimex(&txc) < 0) + return false; + + if (txc.status & STA_UNSYNC) + return false; + + return true; +} + +static void print_status_info(StatusInfo *i) { + usec_t n; + char b[FORMAT_TIMESTAMP_MAX]; + struct tm tm; + time_t sec; + int r; + + n = now(CLOCK_REALTIME); + sec = (time_t) (n / USEC_PER_SEC); + + zero(tm); + assert_se(strftime(b, sizeof(b), "%a, %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) > 0); + char_array_0(b); + printf(" Local time: %s\n", b); + + zero(tm); + assert_se(strftime(b, sizeof(b), "%a, %Y-%m-%d %H:%M:%S UTC", gmtime_r(&sec, &tm)) > 0); + char_array_0(b); + printf(" Universal time: %s\n", b); + + zero(tm); + r = hwclock_get_time(&tm); + if (r >= 0) { + /* Calculcate the week-day */ + mktime(&tm); + + assert_se(strftime(b, sizeof(b), "%a, %Y-%m-%d %H:%M:%S", &tm) > 0); + char_array_0(b); + printf(" RTC time: %s\n", b); + } + + printf(" Timezone: %s\n" + " NTP enabled: %s\n" + "NTP synchronized: %s\n" + " RTC in local TZ: %s\n", + strna(i->timezone), + yes_no(i->ntp), + yes_no(ntp_synced()), + yes_no(i->local_rtc)); + + if (i->local_rtc) + fputs("\n" ANSI_HIGHLIGHT_ON + "Warning: The RTC is configured to maintain time in the local time zone. This\n" + " mode is not fully supported and will create various problems with time\n" + " zone changes and daylight saving adjustments. If at all possible use\n" + " RTC in UTC, by calling 'timedatectl set-local-rtc 0'" ANSI_HIGHLIGHT_OFF ".\n", stdout); +} + +static int status_property(const char *name, DBusMessageIter *iter, StatusInfo *i) { + assert(name); + assert(iter); + + switch (dbus_message_iter_get_arg_type(iter)) { + + case DBUS_TYPE_STRING: { + const char *s; + + dbus_message_iter_get_basic(iter, &s); + if (!isempty(s)) { + if (streq(name, "Timezone")) + i->timezone = s; + } + break; + } + + case DBUS_TYPE_BOOLEAN: { + dbus_bool_t b; + + dbus_message_iter_get_basic(iter, &b); + if (streq(name, "LocalRTC")) + i->local_rtc = b; + else if (streq(name, "NTP")) + i->ntp = b; + } + } + + return 0; +} + +static int show_status(DBusConnection *bus, char **args, unsigned n) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + const char *interface = ""; + int r; + DBusMessageIter iter, sub, sub2, sub3; + StatusInfo info; + + assert(args); + + r = bus_method_call_with_reply( + bus, + "org.freedesktop.timedate1", + "/org/freedesktop/timedate1", + "org.freedesktop.DBus.Properties", + "GetAll", + &reply, + NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_INVALID); + if (r < 0) + return r; + + if (!dbus_message_iter_init(reply, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) { + log_error("Failed to parse reply."); + return -EIO; + } + + dbus_message_iter_recurse(&iter, &sub); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + const char *name; + + if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) { + log_error("Failed to parse reply."); + return -EIO; + } + + dbus_message_iter_recurse(&sub, &sub2); + + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) { + log_error("Failed to parse reply."); + return -EIO; + } + + if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) { + log_error("Failed to parse reply."); + return -EIO; + } + + dbus_message_iter_recurse(&sub2, &sub3); + + r = status_property(name, &sub3, &info); + if (r < 0) { + log_error("Failed to parse reply."); + return r; + } + + dbus_message_iter_next(&sub); + } + + print_status_info(&info); + return 0; +} + +static int set_time(DBusConnection *bus, char **args, unsigned n) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + dbus_bool_t relative = false, interactive = true; + usec_t t; + dbus_int64_t u; + int r; + + assert(args); + assert(n == 2); + + polkit_agent_open_if_enabled(); + + r = parse_timestamp(args[1], &t); + if (r < 0) { + log_error("Failed to parse time specification: %s", args[1]); + return r; + } + + u = (dbus_uint64_t) t; + + return bus_method_call_with_reply( + bus, + "org.freedesktop.timedate1", + "/org/freedesktop/timedate1", + "org.freedesktop.timedate1", + "SetTime", + &reply, + NULL, + DBUS_TYPE_INT64, &u, + DBUS_TYPE_BOOLEAN, &relative, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID); +} + +static int set_timezone(DBusConnection *bus, char **args, unsigned n) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + dbus_bool_t interactive = true; + + assert(args); + assert(n == 2); + + polkit_agent_open_if_enabled(); + + return bus_method_call_with_reply( + bus, + "org.freedesktop.timedate1", + "/org/freedesktop/timedate1", + "org.freedesktop.timedate1", + "SetTimezone", + &reply, + NULL, + DBUS_TYPE_STRING, &args[1], + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID); +} + +static int set_local_rtc(DBusConnection *bus, char **args, unsigned n) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + dbus_bool_t interactive = true, b, q; + int r; + + assert(args); + assert(n == 2); + + polkit_agent_open_if_enabled(); + + r = parse_boolean(args[1]); + if (r < 0) { + log_error("Failed to parse local RTC setting: %s", args[1]); + return r; + } + + b = r; + q = arg_fix_system; + + return bus_method_call_with_reply( + bus, + "org.freedesktop.timedate1", + "/org/freedesktop/timedate1", + "org.freedesktop.timedate1", + "SetLocalRTC", + &reply, + NULL, + DBUS_TYPE_BOOLEAN, &b, + DBUS_TYPE_BOOLEAN, &q, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID); +} + +static int set_ntp(DBusConnection *bus, char **args, unsigned n) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + dbus_bool_t interactive = true, b; + int r; + + assert(args); + assert(n == 2); + + polkit_agent_open_if_enabled(); + + r = parse_boolean(args[1]); + if (r < 0) { + log_error("Failed to parse NTP setting: %s", args[1]); + return r; + } + + b = r; + + return bus_method_call_with_reply( + bus, + "org.freedesktop.timedate1", + "/org/freedesktop/timedate1", + "org.freedesktop.timedate1", + "SetNTP", + &reply, + NULL, + DBUS_TYPE_BOOLEAN, &b, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID); +} + +static int zone_compare(const void *_a, const void *_b) { + const char **a = (const char**) _a, **b = (const char**) _b; + + return strcmp(*a, *b); +} + +static int list_timezones(DBusConnection *bus, char **args, unsigned n) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_strv_free_ char **zones = NULL; + size_t n_zones; + char **i; + + assert(args); + assert(n == 1); + + f = fopen("/usr/share/zoneinfo/zone.tab", "re"); + if (!f) { + log_error("Failed to open timezone database: %m"); + return -errno; + } + + for (;;) { + char l[LINE_MAX], *p, **z, *w; + size_t k; + + if (!fgets(l, sizeof(l), f)) { + if (feof(f)) + break; + + log_error("Failed to read timezone database: %m"); + return -errno; + } + + p = strstrip(l); + + if (isempty(p) || *p == '#') + continue; + + + /* Skip over country code */ + p += strcspn(p, WHITESPACE); + p += strspn(p, WHITESPACE); + + /* Skip over coordinates */ + p += strcspn(p, WHITESPACE); + p += strspn(p, WHITESPACE); + + /* Found timezone name */ + k = strcspn(p, WHITESPACE); + if (k <= 0) + continue; + + w = strndup(p, k); + if (!w) + return log_oom(); + + z = realloc(zones, sizeof(char*) * (n_zones + 2)); + if (!z) { + free(w); + return log_oom(); + } + + zones = z; + zones[n_zones++] = w; + } + + if (zones) + zones[n_zones] = 0; + + qsort(zones, n_zones, sizeof(char*), zone_compare); + + pager_open_if_enabled(); + + STRV_FOREACH(i, zones) + puts(*i); + + return 0; +} + +static int help(void) { + + printf("%s [OPTIONS...] {COMMAND} ...\n\n" + "Query or control system time and date settings.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --fix-system Adjust system clock when changing local RTC mode\n" + " --no-pager Do not pipe output into a pager\n" + " --no-ask-password Do not prompt for password\n" + " -H --host=[USER@]HOST Operate on remote host\n\n" + "Commands:\n" + " status Show current time settings\n" + " set-time [TIME] Set system time\n" + " set-timezone [ZONE] Set system timezone\n" + " list-timezones Show known timezones\n" + " set-local-rtc [BOOL] Control whether RTC is in local time\n" + " set-ntp [BOOL] Control whether NTP is enabled\n", + program_invocation_short_name); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_NO_PAGER, + ARG_FIX_SYSTEM, + ARG_NO_ASK_PASSWORD + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "host", required_argument, NULL, 'H' }, + { "privileged", no_argument, NULL, 'P' }, + { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, + { "fix-system", no_argument, NULL, ARG_FIX_SYSTEM }, + { NULL, 0, NULL, 0 } + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "+hp:as:H:P", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + puts(PACKAGE_STRING); + puts(DISTRIBUTION); + puts(SYSTEMD_FEATURES); + return 0; + + case 'P': + arg_transport = TRANSPORT_POLKIT; + break; + + case 'H': + arg_transport = TRANSPORT_SSH; + arg_host = optarg; + break; + + case ARG_FIX_SYSTEM: + arg_fix_system = true; + break; + + case ARG_NO_PAGER: + arg_no_pager = true; + break; + + case '?': + return -EINVAL; + + default: + log_error("Unknown option code %c", c); + return -EINVAL; + } + } + + return 1; +} + +static int timedatectl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) { + + static const struct { + const char* verb; + const enum { + MORE, + LESS, + EQUAL + } argc_cmp; + const int argc; + int (* const dispatch)(DBusConnection *bus, char **args, unsigned n); + } verbs[] = { + { "status", LESS, 1, show_status }, + { "set-time", EQUAL, 2, set_time }, + { "set-timezone", EQUAL, 2, set_timezone }, + { "list-timezones", EQUAL, 1, list_timezones }, + { "set-local-rtc", EQUAL, 2, set_local_rtc }, + { "set-ntp", EQUAL, 2, set_ntp, }, + }; + + int left; + unsigned i; + + assert(argc >= 0); + assert(argv); + assert(error); + + left = argc - optind; + + if (left <= 0) + /* Special rule: no arguments means "status" */ + i = 0; + else { + if (streq(argv[optind], "help")) { + help(); + return 0; + } + + for (i = 0; i < ELEMENTSOF(verbs); i++) + if (streq(argv[optind], verbs[i].verb)) + break; + + if (i >= ELEMENTSOF(verbs)) { + log_error("Unknown operation %s", argv[optind]); + return -EINVAL; + } + } + + switch (verbs[i].argc_cmp) { + + case EQUAL: + if (left != verbs[i].argc) { + log_error("Invalid number of arguments."); + return -EINVAL; + } + + break; + + case MORE: + if (left < verbs[i].argc) { + log_error("Too few arguments."); + return -EINVAL; + } + + break; + + case LESS: + if (left > verbs[i].argc) { + log_error("Too many arguments."); + return -EINVAL; + } + + break; + + default: + assert_not_reached("Unknown comparison operator."); + } + + if (!bus) { + log_error("Failed to get D-Bus connection: %s", error->message); + return -EIO; + } + + return verbs[i].dispatch(bus, argv + optind, left); +} + +int main(int argc, char *argv[]) { + int r, retval = EXIT_FAILURE; + DBusConnection *bus = NULL; + DBusError error; + + dbus_error_init(&error); + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r < 0) + goto finish; + else if (r == 0) { + retval = EXIT_SUCCESS; + goto finish; + } + + if (arg_transport == TRANSPORT_NORMAL) + bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); + else if (arg_transport == TRANSPORT_POLKIT) + bus_connect_system_polkit(&bus, &error); + else if (arg_transport == TRANSPORT_SSH) + bus_connect_system_ssh(NULL, arg_host, &bus, &error); + else + assert_not_reached("Uh, invalid transport..."); + + r = timedatectl_main(bus, argc, argv, &error); + retval = r < 0 ? EXIT_FAILURE : r; + +finish: + if (bus) { + dbus_connection_flush(bus); + dbus_connection_close(bus); + dbus_connection_unref(bus); + } + + dbus_error_free(&error); + dbus_shutdown(); + + pager_close(); + + return retval; +} -- cgit v1.2.3-54-g00ecf From dbc4fbae58e39cb0d33738f0a4d1e74511ed1fb5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 17 Oct 2012 21:23:30 +0200 Subject: hostname: add new hostnamectl tool as text client for hostnamed --- .gitignore | 1 + Makefile.am | 19 ++ man/hostname.xml | 9 +- man/hostnamectl.xml | 228 +++++++++++++++++ man/localtime.xml | 13 +- man/machine-info.xml | 9 +- man/systemd-hostnamed.service.xml | 5 +- man/timedatectl.xml | 8 +- src/hostname/hostnamectl.c | 525 ++++++++++++++++++++++++++++++++++++++ 9 files changed, 809 insertions(+), 8 deletions(-) create mode 100644 man/hostnamectl.xml create mode 100644 src/hostname/hostnamectl.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index bc72be8210..c41db45be2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/hostnamectl /timedatectl /test-date /install-tree diff --git a/Makefile.am b/Makefile.am index ad7a749cac..8e88c42c8f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3062,6 +3062,25 @@ MANPAGES_ALIAS += \ man/systemd-hostnamed.8 man/systemd-hostnamed.8: man/systemd-hostnamed.service.8 + +hostnamectl_SOURCES = \ + src/hostname/hostnamectl.c + +hostnamectl_CFLAGS = \ + $(AM_CFLAGS) \ + $(DBUS_CFLAGS) + +hostnamectl_LDADD = \ + libsystemd-shared.la \ + libsystemd-dbus.la \ + libsystemd-id128-internal.la + +bin_PROGRAMS += \ + hostnamectl + +MANPAGES += \ + man/hostnamectl.1 + endif polkitpolicy_in_files += \ diff --git a/man/hostname.xml b/man/hostname.xml index 2ada32ff71..84a2961664 100644 --- a/man/hostname.xml +++ b/man/hostname.xml @@ -70,6 +70,11 @@ Depending on the operating system other configuration files might be checked for configuration of the host name as well, however only as fallback. + + You may use + hostnamectl1 + to change the value of this file from the command + line. @@ -88,7 +93,9 @@ hostname1, hostname7, machine-id5, - machine-info5 + machine-info5, + hostnamectl1, + systemd-hostnamed.service8 diff --git a/man/hostnamectl.xml b/man/hostnamectl.xml new file mode 100644 index 0000000000..955e79bfd7 --- /dev/null +++ b/man/hostnamectl.xml @@ -0,0 +1,228 @@ + + + + + + + + + hostnamectl + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + hostnamectl + 1 + + + + hostnamectl + Control the system hostname + + + + + hostnamectl OPTIONS COMMAND + + + + + Description + + hostnamectl may be used to + query and change the system hostname and related + settings. + + This tool distuingishes three different host + names: the high-level "pretty" hostname which might + include all all kinds of special characters + (e.g. "Lennart's Laptop"), the static hostname which + is used to initialize the kernel hostname at boot + (e.g. "lennarts-laptop"), and the transient hostname + which might be assigned temporarily due to network + configuration and might revert back to the static + hostname if network connectivity is lost and is only + temporarily written to the kernel hostname + (e.g. "dhcp-47-11"). + + Note that the pretty hostname has little + restrictions on the characters used, while the static + and transient hostnames are limited to the usually + accepted characters of internet domain names. + + The static host name is stored in + /etc/hostname, see + hostname5 + for more information. The pretty host name and icon + name are stored in + /etc/machine-info, see + machine-id5. + + + + Options + + The following options are understood: + + + + + + + Prints a short help + text and exits. + + + + + + Prints a short version + string and exits. + + + + + + Don't query the user + for authentication for privileged + operations. + + + + + + + Execute operation + remotely. Specify a hostname, or + username and hostname separated by @, + to connect to. This will use SSH to + talk to a remote + system. + + + + + + + + If + set-hostname is + invoked and one or more of these + options are passed only the selected + hostnames is + updated. + + + + The following commands are understood: + + + + status + + Show current system + hostname and related + information. + + + + set-hostname [NAME] + + Set the system + hostname. By default this will alter + the pretty, the static, and the + transient hostname alike, however if + one or more of + , + , + are used + only the selected hostnames are + changed. If the pretty hostname is + being set, and static or transient are + being set as well the specified host + name will be simplified in regards to + the character set used before the + latter are updated. This is done by + replacing spaces by "-" and removing + special characters. This ensures that + the pretty and the static hostname + are always closely related while still + following the validity rules of the + specific name. This simplification of + the hostname string is not done if + only the transient and/or static host + names are set, and the pretty host + name is left untouched. Pass the empty + string "" as hostname to reset the + selected hostnames to their default + (usually + "localhost"). + + + + set-icon-name [NAME] + + Set the system icon + name. The icon name is used by some + graphical applications to visualize + this host. The icon name should follow + the Icon + Naming Specification. Pass an + empty string to this operatoin to + reset the icon name to the default + value which is determined from the + system form factor and possibly other + parameters. + + + + + + + Exit status + + On success 0 is returned, a non-zero failure + code otherwise. + + + + See Also + + systemd1, + hostname1, + hostname5, + machine-info5, + systemctl1, + systemd-hostnamed.service8 + + + + diff --git a/man/localtime.xml b/man/localtime.xml index c0e87bf78f..88c84a3682 100644 --- a/man/localtime.xml +++ b/man/localtime.xml @@ -82,14 +82,21 @@ The time zone may be overridden for individual programs by using the TZ environment variable. See environ7. + + You may use + timedatectl1 + to change the settings of this file from the command + line. See Also - tzset3 - localtime3 - systemd1 + systemd1, + tzset3, + localtime3, + timedatectl1, + systemd-timedated.service8 diff --git a/man/machine-info.xml b/man/machine-info.xml index e27b600211..b310d71334 100644 --- a/man/machine-info.xml +++ b/man/machine-info.xml @@ -74,6 +74,11 @@ Depending on the operating system other configuration files might be checked for machine information as well, however only as fallback. + + You may use + hostnamectl1 + to change the settings of this file from the command + line. @@ -140,7 +145,9 @@ ICON_NAME=computer-laptop systemd1, os-release5, hostname5, - machine-id5 + machine-id5, + hostnamectl1, + systemd-hostnamed.service8 diff --git a/man/systemd-hostnamed.service.xml b/man/systemd-hostnamed.service.xml index 9fee2a096d..555bb7a2df 100644 --- a/man/systemd-hostnamed.service.xml +++ b/man/systemd-hostnamed.service.xml @@ -74,8 +74,9 @@ systemd1, hostname5, - sethostname2, - machine-info5 + machine-info5, + hostnamectl1, + sethostname2 diff --git a/man/timedatectl.xml b/man/timedatectl.xml index 8fb4e38f37..9065577692 100644 --- a/man/timedatectl.xml +++ b/man/timedatectl.xml @@ -154,7 +154,12 @@ list-timezones. If the RTC is configured to be in the local time this will also update the - RTC time. + RTC time. This call will alter the + /etc/localtime + symlink. See + localtime5 + for more + information. @@ -235,6 +240,7 @@ systemd1, hwclock8, date1, + localtime5, systemctl1, systemd-timedated.service8 diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c new file mode 100644 index 0000000000..de2f7242ea --- /dev/null +++ b/src/hostname/hostnamectl.c @@ -0,0 +1,525 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include + +#include "dbus-common.h" +#include "util.h" +#include "spawn-polkit-agent.h" +#include "build.h" +#include "hwclock.h" +#include "strv.h" +#include "sd-id128.h" +#include "virt.h" + +static enum transport { + TRANSPORT_NORMAL, + TRANSPORT_SSH, + TRANSPORT_POLKIT +} arg_transport = TRANSPORT_NORMAL; +static bool arg_ask_password = true; +static const char *arg_host = NULL; +static bool arg_set_transient = false; +static bool arg_set_pretty = false; +static bool arg_set_static = false; + +static void polkit_agent_open_if_enabled(void) { + + /* Open the polkit agent as a child process if necessary */ + + if (!arg_ask_password) + return; + + polkit_agent_open(); +} + +typedef struct StatusInfo { + const char *hostname; + const char *static_hostname; + const char *pretty_hostname; + const char *icon_name; +} StatusInfo; + +static void print_status_info(StatusInfo *i) { + sd_id128_t mid, bid; + int r; + const char *id; + + assert(i); + + printf(" Static hostname: %s\n", + strna(i->static_hostname)); + + if (!streq_ptr(i->hostname, i->static_hostname)) + printf("Transient hostname: %s\n", + strna(i->hostname)); + + printf(" Pretty hostname: %s\n" + " Icon name: %s\n", + strna(i->pretty_hostname), + strna(i->icon_name)); + + r = sd_id128_get_machine(&mid); + if (r >= 0) + printf(" Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(mid)); + + r = sd_id128_get_boot(&bid); + if (r >= 0) + printf(" Boot ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(bid)); + + if (detect_virtualization(&id) >= 0) + printf(" Virtualization: %s\n", id); +} + +static int status_property(const char *name, DBusMessageIter *iter, StatusInfo *i) { + assert(name); + assert(iter); + + switch (dbus_message_iter_get_arg_type(iter)) { + + case DBUS_TYPE_STRING: { + const char *s; + + dbus_message_iter_get_basic(iter, &s); + if (!isempty(s)) { + if (streq(name, "Hostname")) + i->hostname = s; + if (streq(name, "StaticHostname")) + i->static_hostname = s; + if (streq(name, "PrettyHostname")) + i->pretty_hostname = s; + if (streq(name, "IconName")) + i->icon_name = s; + } + break; + } + } + + return 0; +} + +static int show_status(DBusConnection *bus, char **args, unsigned n) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + const char *interface = ""; + int r; + DBusMessageIter iter, sub, sub2, sub3; + StatusInfo info; + + assert(args); + + r = bus_method_call_with_reply( + bus, + "org.freedesktop.hostname1", + "/org/freedesktop/hostname1", + "org.freedesktop.DBus.Properties", + "GetAll", + &reply, + NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_INVALID); + if (r < 0) + return r; + + if (!dbus_message_iter_init(reply, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) { + log_error("Failed to parse reply."); + return -EIO; + } + + zero(info); + dbus_message_iter_recurse(&iter, &sub); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + const char *name; + + if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) { + log_error("Failed to parse reply."); + return -EIO; + } + + dbus_message_iter_recurse(&sub, &sub2); + + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) { + log_error("Failed to parse reply."); + return -EIO; + } + + if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) { + log_error("Failed to parse reply."); + return -EIO; + } + + dbus_message_iter_recurse(&sub2, &sub3); + + r = status_property(name, &sub3, &info); + if (r < 0) { + log_error("Failed to parse reply."); + return r; + } + + dbus_message_iter_next(&sub); + } + + print_status_info(&info); + return 0; +} + +static char* hostname_simplify(char *s) { + char *p, *d; + + for (p = s, d = s; *p; p++) { + if ((*p >= 'a' && *p <= 'z') || + (*p >= '0' && *p <= '9') || + *p == '-' || *p == '_') + *(d++) = *p; + else if (*p >= 'A' && *p <= 'Z') + *(d++) = *p - 'A' + 'a'; + else if (*p == ' ') + *(d++) = '-'; + } + + *d = 0; + + strshorten(s, HOST_NAME_MAX); + return s; +} + +static int set_hostname(DBusConnection *bus, char **args, unsigned n) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + dbus_bool_t interactive = true; + _cleanup_free_ char *h = NULL; + const char *hostname = args[1]; + int r; + + assert(args); + assert(n == 2); + + polkit_agent_open_if_enabled(); + + if (arg_set_pretty) { + r = bus_method_call_with_reply( + bus, + "org.freedesktop.hostname1", + "/org/freedesktop/hostname1", + "org.freedesktop.hostname1", + "SetPrettyHostname", + &reply, + NULL, + DBUS_TYPE_STRING, &hostname, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID); + if (r < 0) + return r; + + h = strdup(hostname); + if (!h) + return log_oom(); + + hostname = hostname_simplify(h); + } + + if (arg_set_static) { + r = bus_method_call_with_reply( + bus, + "org.freedesktop.hostname1", + "/org/freedesktop/hostname1", + "org.freedesktop.hostname1", + "SetStaticHostname", + &reply, + NULL, + DBUS_TYPE_STRING, &hostname, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID); + + if (r < 0) + return r; + } + + if (arg_set_transient) { + r = bus_method_call_with_reply( + bus, + "org.freedesktop.hostname1", + "/org/freedesktop/hostname1", + "org.freedesktop.hostname1", + "SetHostname", + &reply, + NULL, + DBUS_TYPE_STRING, &hostname, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID); + + if (r < 0) + return r; + } + + return 0; +} + +static int set_icon_name(DBusConnection *bus, char **args, unsigned n) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + dbus_bool_t interactive = true; + + assert(args); + assert(n == 2); + + polkit_agent_open_if_enabled(); + + return bus_method_call_with_reply( + bus, + "org.freedesktop.hostname1", + "/org/freedesktop/hostname1", + "org.freedesktop.hostname1", + "SetIconName", + &reply, + NULL, + DBUS_TYPE_STRING, &args[1], + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID); +} + +static int help(void) { + + printf("%s [OPTIONS...] {COMMAND} ...\n\n" + "Query or set system hostname.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --no-ask-password Do not prompt for password\n" + " --transient Only set transient hostname\n" + " --static Only set static hostname\n" + " --pretty Only set pretty hostname\n" + " -H --host=[USER@]HOST Operate on remote host\n\n" + "Commands:\n" + " status Show current hostname settings\n" + " set-hostname [NAME] Set system hostname\n" + " set-icon-name [NAME] Set icon name for host\n", + program_invocation_short_name); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_NO_ASK_PASSWORD, + ARG_SET_TRANSIENT, + ARG_SET_STATIC, + ARG_SET_PRETTY + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "transient", no_argument, NULL, ARG_SET_TRANSIENT }, + { "static", no_argument, NULL, ARG_SET_STATIC }, + { "pretty", no_argument, NULL, ARG_SET_PRETTY }, + { "host", required_argument, NULL, 'H' }, + { "privileged", no_argument, NULL, 'P' }, + { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, + { NULL, 0, NULL, 0 } + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + puts(PACKAGE_STRING); + puts(DISTRIBUTION); + puts(SYSTEMD_FEATURES); + return 0; + + case 'P': + arg_transport = TRANSPORT_POLKIT; + break; + + case 'H': + arg_transport = TRANSPORT_SSH; + arg_host = optarg; + break; + + case ARG_SET_TRANSIENT: + arg_set_transient = true; + break; + + case ARG_SET_PRETTY: + arg_set_pretty = true; + break; + + case ARG_SET_STATIC: + arg_set_static = true; + break; + + case '?': + return -EINVAL; + + default: + log_error("Unknown option code %c", c); + return -EINVAL; + } + } + + if (!arg_set_transient && !arg_set_pretty && !arg_set_static) + arg_set_transient = arg_set_pretty = arg_set_static = true; + + return 1; +} + +static int hostnamectl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) { + + static const struct { + const char* verb; + const enum { + MORE, + LESS, + EQUAL + } argc_cmp; + const int argc; + int (* const dispatch)(DBusConnection *bus, char **args, unsigned n); + } verbs[] = { + { "status", LESS, 1, show_status }, + { "set-hostname", LESS, 2, set_hostname }, + { "set-icon-name", EQUAL, 2, set_icon_name }, + }; + + int left; + unsigned i; + + assert(argc >= 0); + assert(argv); + assert(error); + + left = argc - optind; + + if (left <= 0) + /* Special rule: no arguments means "status" */ + i = 0; + else { + if (streq(argv[optind], "help")) { + help(); + return 0; + } + + for (i = 0; i < ELEMENTSOF(verbs); i++) + if (streq(argv[optind], verbs[i].verb)) + break; + + if (i >= ELEMENTSOF(verbs)) { + log_error("Unknown operation %s", argv[optind]); + return -EINVAL; + } + } + + switch (verbs[i].argc_cmp) { + + case EQUAL: + if (left != verbs[i].argc) { + log_error("Invalid number of arguments."); + return -EINVAL; + } + + break; + + case MORE: + if (left < verbs[i].argc) { + log_error("Too few arguments."); + return -EINVAL; + } + + break; + + case LESS: + if (left > verbs[i].argc) { + log_error("Too many arguments."); + return -EINVAL; + } + + break; + + default: + assert_not_reached("Unknown comparison operator."); + } + + if (!bus) { + log_error("Failed to get D-Bus connection: %s", error->message); + return -EIO; + } + + return verbs[i].dispatch(bus, argv + optind, left); +} + +int main(int argc, char *argv[]) { + int r, retval = EXIT_FAILURE; + DBusConnection *bus = NULL; + DBusError error; + + dbus_error_init(&error); + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r < 0) + goto finish; + else if (r == 0) { + retval = EXIT_SUCCESS; + goto finish; + } + + if (arg_transport == TRANSPORT_NORMAL) + bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); + else if (arg_transport == TRANSPORT_POLKIT) + bus_connect_system_polkit(&bus, &error); + else if (arg_transport == TRANSPORT_SSH) + bus_connect_system_ssh(NULL, arg_host, &bus, &error); + else + assert_not_reached("Uh, invalid transport..."); + + r = hostnamectl_main(bus, argc, argv, &error); + retval = r < 0 ? EXIT_FAILURE : r; + +finish: + if (bus) { + dbus_connection_flush(bus); + dbus_connection_close(bus); + dbus_connection_unref(bus); + } + + dbus_error_free(&error); + dbus_shutdown(); + + return retval; +} -- cgit v1.2.3-54-g00ecf From 2087a7aff26ea5d1bc2c7c29add3275328f36baa Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 19 Oct 2012 04:55:49 +0200 Subject: locale: add client tool localectl similar to hostnamectl/timedatectl --- .gitignore | 1 + Makefile.am | 18 ++ src/locale/localectl.c | 778 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 797 insertions(+) create mode 100644 src/locale/localectl.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index c41db45be2..123873acfc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/localectl /hostnamectl /timedatectl /test-date diff --git a/Makefile.am b/Makefile.am index 4c650765cc..e86e6347ed 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3159,6 +3159,24 @@ dist_noinst_SCRIPT = \ update-kbd-model-map: src/locale/generate-kbd-model-map > src/locale/kbd-model-map +localectl_SOURCES = \ + src/locale/localectl.c + +localectl_CFLAGS = \ + $(AM_CFLAGS) \ + $(DBUS_CFLAGS) + +localectl_LDADD = \ + libsystemd-shared.la \ + libsystemd-dbus.la \ + libsystemd-id128-internal.la + +bin_PROGRAMS += \ + localectl + +#MANPAGES += \ +# man/localectl.1 + endif polkitpolicy_in_files += \ diff --git a/src/locale/localectl.c b/src/locale/localectl.c new file mode 100644 index 0000000000..c05eba0d3e --- /dev/null +++ b/src/locale/localectl.c @@ -0,0 +1,778 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dbus-common.h" +#include "util.h" +#include "spawn-polkit-agent.h" +#include "build.h" +#include "strv.h" +#include "pager.h" +#include "set.h" +#include "path-util.h" + +static bool arg_no_pager = false; +static enum transport { + TRANSPORT_NORMAL, + TRANSPORT_SSH, + TRANSPORT_POLKIT +} arg_transport = TRANSPORT_NORMAL; +static bool arg_ask_password = true; +static const char *arg_host = NULL; +static bool arg_convert = true; + +static void pager_open_if_enabled(void) { + + if (arg_no_pager) + return; + + pager_open(); +} + +static void polkit_agent_open_if_enabled(void) { + + /* Open the polkit agent as a child process if necessary */ + + if (!arg_ask_password) + return; + + polkit_agent_open(); +} + +typedef struct StatusInfo { + char **locale; + const char *vconsole_keymap; + const char *vconsole_keymap_toggle; + const char *x11_layout; + const char *x11_model; + const char *x11_variant; + const char *x11_options; +} StatusInfo; + +static void print_status_info(StatusInfo *i) { + assert(i); + + if (strv_isempty(i->locale)) + puts(" System Locale: n/a\n"); + else { + char **j; + + printf(" System Locale: %s\n", i->locale[0]); + STRV_FOREACH(j, i->locale + 1) + printf(" %s\n", *j); + } + + printf(" VC Keymap: %s\n", strna(i->vconsole_keymap)); + if (!isempty(i->vconsole_keymap_toggle)) + printf("VC Toggle Keymap: %s\n", i->vconsole_keymap_toggle); + + printf(" X11 Layout: %s\n", strna(i->x11_layout)); + if (!isempty(i->x11_model)) + printf(" X11 Model: %s\n", i->x11_model); + if (!isempty(i->x11_variant)) + printf(" X11 Variant: %s\n", i->x11_variant); + if (!isempty(i->x11_options)) + printf(" X11 Options: %s\n", i->x11_options); +} + +static int status_property(const char *name, DBusMessageIter *iter, StatusInfo *i) { + int r; + + assert(name); + assert(iter); + + switch (dbus_message_iter_get_arg_type(iter)) { + + case DBUS_TYPE_STRING: { + const char *s; + + dbus_message_iter_get_basic(iter, &s); + if (!isempty(s)) { + if (streq(name, "VConsoleKeymap")) + i->vconsole_keymap = s; + else if (streq(name, "VConsoleKeymapToggle")) + i->vconsole_keymap_toggle = s; + else if (streq(name, "X11Layout")) + i->x11_layout = s; + else if (streq(name, "X11Model")) + i->x11_model = s; + else if (streq(name, "X11Variant")) + i->x11_variant = s; + else if (streq(name, "X11Options")) + i->x11_options = s; + } + break; + } + + case DBUS_TYPE_ARRAY: + + if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) { + char **l; + + r = bus_parse_strv_iter(iter, &l); + if (r < 0) + return r; + + if (streq(name, "Locale")) { + strv_free(i->locale); + i->locale = l; + l = NULL; + } + + strv_free(l); + } + } + + return 0; +} + +static int show_status(DBusConnection *bus, char **args, unsigned n) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + const char *interface = ""; + int r; + DBusMessageIter iter, sub, sub2, sub3; + StatusInfo info; + + assert(args); + + r = bus_method_call_with_reply( + bus, + "org.freedesktop.locale1", + "/org/freedesktop/locale1", + "org.freedesktop.DBus.Properties", + "GetAll", + &reply, + NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_INVALID); + if (r < 0) + return r; + + if (!dbus_message_iter_init(reply, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) { + log_error("Failed to parse reply."); + return -EIO; + } + + zero(info); + dbus_message_iter_recurse(&iter, &sub); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + const char *name; + + if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) { + log_error("Failed to parse reply."); + return -EIO; + } + + dbus_message_iter_recurse(&sub, &sub2); + + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) { + log_error("Failed to parse reply."); + return -EIO; + } + + if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) { + log_error("Failed to parse reply."); + return -EIO; + } + + dbus_message_iter_recurse(&sub2, &sub3); + + r = status_property(name, &sub3, &info); + if (r < 0) { + log_error("Failed to parse reply."); + return r; + } + + dbus_message_iter_next(&sub); + } + + print_status_info(&info); + strv_free(info.locale); + return 0; +} + +static int set_locale(DBusConnection *bus, char **args, unsigned n) { + _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL; + dbus_bool_t interactive = true; + DBusError error; + DBusMessageIter iter; + int r; + + assert(bus); + assert(args); + + dbus_error_init(&error); + + polkit_agent_open_if_enabled(); + + m = dbus_message_new_method_call( + "org.freedesktop.locale1", + "/org/freedesktop/locale1", + "org.freedesktop.locale1", + "SetLocale"); + if (!m) + return log_oom(); + + dbus_message_iter_init_append(m, &iter); + + r = bus_append_strv_iter(&iter, args + 1); + if (r < 0) + return log_oom(); + + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &interactive)) + return log_oom(); + + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); + if (!reply) { + log_error("Failed to issue method call: %s", bus_error_message(&error)); + r = -EIO; + goto finish; + } + + r = 0; + +finish: + dbus_error_free(&error); + return r; +} + +static int list_locales(DBusConnection *bus, char **args, unsigned n) { + /* Stolen from glibc... */ + + struct locarhead { + uint32_t magic; + /* Serial number. */ + uint32_t serial; + /* Name hash table. */ + uint32_t namehash_offset; + uint32_t namehash_used; + uint32_t namehash_size; + /* String table. */ + uint32_t string_offset; + uint32_t string_used; + uint32_t string_size; + /* Table with locale records. */ + uint32_t locrectab_offset; + uint32_t locrectab_used; + uint32_t locrectab_size; + /* MD5 sum hash table. */ + uint32_t sumhash_offset; + uint32_t sumhash_used; + uint32_t sumhash_size; + }; + + struct namehashent { + /* Hash value of the name. */ + uint32_t hashval; + /* Offset of the name in the string table. */ + uint32_t name_offset; + /* Offset of the locale record. */ + uint32_t locrec_offset; + }; + + const struct locarhead *h; + const struct namehashent *e; + const void *p = MAP_FAILED; + _cleanup_close_ int fd = -1; + _cleanup_strv_free_ char **l = NULL; + char **j; + Set *locales; + size_t sz = 0; + struct stat st; + unsigned i; + int r; + + locales = set_new(string_hash_func, string_compare_func); + if (!locales) + return log_oom(); + + fd = open("/usr/lib/locale/locale-archive", O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (fd < 0) { + log_error("Failed to open locale archive: %m"); + r = -errno; + goto finish; + } + + if (fstat(fd, &st) < 0) { + log_error("fstat() failed: %m"); + r = -errno; + goto finish; + } + + if (!S_ISREG(st.st_mode)) { + log_error("Archive file is not regular"); + r = -EBADMSG; + goto finish; + } + + if (st.st_size < (off_t) sizeof(struct locarhead)) { + log_error("Archive has invalid size"); + r = -EBADMSG; + goto finish; + } + + p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (p == MAP_FAILED) { + log_error("Failed to map archive: %m"); + r = -errno; + goto finish; + } + + h = (const struct locarhead *) p; + if (h->magic != 0xde020109 || + h->namehash_offset + h->namehash_size > st.st_size || + h->string_offset + h->string_size > st.st_size || + h->locrectab_offset + h->locrectab_size > st.st_size || + h->sumhash_offset + h->sumhash_size > st.st_size) { + log_error("Invalid archive file."); + return -EBADMSG; + } + + e = (const struct namehashent*) ((const uint8_t*) p + h->namehash_offset); + for (i = 0; i < h->namehash_size; i++) { + char *z; + + if (e[i].locrec_offset == 0) + continue; + + z = strdup((char*) p + e[i].name_offset); + if (!z) { + r = log_oom(); + goto finish; + } + + r = set_put(locales, z); + if (r < 0) { + free(z); + log_error("Failed to add locale: %s", strerror(-r)); + goto finish; + } + } + + l = set_get_strv(locales); + if (!l) { + r = log_oom(); + goto finish; + } + + set_free(locales); + locales = NULL; + + strv_sort(l); + + pager_open_if_enabled(); + + STRV_FOREACH(j, l) + puts(*j); + + r = 0; + +finish: + if (p != MAP_FAILED) + munmap((void*) p, sz); + + set_free_free(locales); + + return r; +} + +static int set_vconsole_keymap(DBusConnection *bus, char **args, unsigned n) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + dbus_bool_t interactive = true, b; + const char *map, *toggle_map; + + assert(bus); + assert(args); + + if (n > 3) { + log_error("Too many arguments."); + return -EINVAL; + } + + polkit_agent_open_if_enabled(); + + map = args[1]; + toggle_map = n > 2 ? args[2] : ""; + b = arg_convert; + + return bus_method_call_with_reply( + bus, + "org.freedesktop.locale1", + "/org/freedesktop/locale1", + "org.freedesktop.locale1", + "SetVConsoleKeyboard", + &reply, + NULL, + DBUS_TYPE_STRING, &map, + DBUS_TYPE_STRING, &toggle_map, + DBUS_TYPE_BOOLEAN, &b, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID); +} + +static Set *keymaps = NULL; + +static int nftw_cb( + const char *fpath, + const struct stat *sb, + int tflag, + struct FTW *ftwbuf) { + + char *p, *e; + int r; + + if (tflag != FTW_F) + return 0; + + if (!endswith(fpath, ".map") && + !endswith(fpath, ".map.gz")) + return 0; + + p = strdup(path_get_file_name(fpath)); + if (!p) + return log_oom(); + + e = endswith(p, ".map"); + if (e) + *e = 0; + + e = endswith(p, ".map.gz"); + if (e) + *e = 0; + + r = set_put(keymaps, p); + if (r == -EEXIST) + free(p); + else if (r < 0) { + log_error("Can't add keymap: %s", strerror(-r)); + free(p); + return r; + } + + return 0; +} + +static int list_vconsole_keymaps(DBusConnection *bus, char **args, unsigned n) { + char **l, **i; + + keymaps = set_new(string_hash_func, string_compare_func); + if (!keymaps) + return log_oom(); + + nftw("/usr/share/kbd/keymaps/", nftw_cb, 20, FTW_MOUNT|FTW_PHYS); + nftw("/usr/lib/kbd/keymaps/", nftw_cb, 20, FTW_MOUNT|FTW_PHYS); + nftw("/lib/kbd/keymaps/", nftw_cb, 20, FTW_MOUNT|FTW_PHYS); + + l = set_get_strv(keymaps); + if (!l) { + set_free_free(keymaps); + return log_oom(); + } + + set_free(keymaps); + + if (strv_isempty(l)) { + log_error("Couldn't find any console keymaps."); + return -ENOENT; + } + + strv_sort(l); + + pager_open_if_enabled(); + + STRV_FOREACH(i, l) + puts(*i); + + strv_free(l); + + return 0; +} + +static int set_x11_keymap(DBusConnection *bus, char **args, unsigned n) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + dbus_bool_t interactive = true, b; + const char *layout, *model, *variant, *options; + + assert(bus); + assert(args); + + if (n > 5) { + log_error("Too many arguments."); + return -EINVAL; + } + + polkit_agent_open_if_enabled(); + + layout = args[1]; + model = n > 2 ? args[2] : ""; + variant = n > 3 ? args[3] : ""; + options = n > 3 ? args[4] : ""; + b = arg_convert; + + return bus_method_call_with_reply( + bus, + "org.freedesktop.locale1", + "/org/freedesktop/locale1", + "org.freedesktop.locale1", + "SetX11Keyboard", + &reply, + NULL, + DBUS_TYPE_STRING, &layout, + DBUS_TYPE_STRING, &model, + DBUS_TYPE_STRING, &variant, + DBUS_TYPE_STRING, &options, + DBUS_TYPE_BOOLEAN, &b, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID); +} + +static int help(void) { + + printf("%s [OPTIONS...] COMMAND ...\n\n" + "Query or change system time and date settings.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --no-convert Don't convert keyboard mappings\n" + " --no-pager Do not pipe output into a pager\n" + " --no-ask-password Do not prompt for password\n" + " -H --host=[USER@]HOST Operate on remote host\n\n" + "Commands:\n" + " status Show current locale settings\n" + " set-locale LOCALE... Set system locale\n" + " list-locales Show known locales\n" + " set-keymap MAP [MAP] Set virtual console keyboard mapping\n" + " list-keymaps Show known virtual console keyboard mappings\n" + " set-x11-keymap LAYOUT [MODEL] [VARIANT] [OPTIONS]\n" + " Set X11 keyboard mapping\n", + program_invocation_short_name); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_NO_PAGER, + ARG_NO_CONVERT, + ARG_NO_ASK_PASSWORD + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "host", required_argument, NULL, 'H' }, + { "privileged", no_argument, NULL, 'P' }, + { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, + { "no-convert", no_argument, NULL, ARG_NO_CONVERT }, + { NULL, 0, NULL, 0 } + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "has:H:P", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + puts(PACKAGE_STRING); + puts(DISTRIBUTION); + puts(SYSTEMD_FEATURES); + return 0; + + case 'P': + arg_transport = TRANSPORT_POLKIT; + break; + + case 'H': + arg_transport = TRANSPORT_SSH; + arg_host = optarg; + break; + + case ARG_NO_CONVERT: + arg_convert = false; + break; + + case ARG_NO_PAGER: + arg_no_pager = true; + break; + + case '?': + return -EINVAL; + + default: + log_error("Unknown option code %c", c); + return -EINVAL; + } + } + + return 1; +} + +static int localectl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) { + + static const struct { + const char* verb; + const enum { + MORE, + LESS, + EQUAL + } argc_cmp; + const int argc; + int (* const dispatch)(DBusConnection *bus, char **args, unsigned n); + } verbs[] = { + { "status", LESS, 1, show_status }, + { "set-locale", MORE, 2, set_locale }, + { "list-locales", EQUAL, 1, list_locales }, + { "set-keymap", MORE, 2, set_vconsole_keymap }, + { "list-keymaps", EQUAL, 1, list_vconsole_keymaps }, + { "set-x11-keymap", MORE, 2, set_x11_keymap }, + }; + + int left; + unsigned i; + + assert(argc >= 0); + assert(argv); + assert(error); + + left = argc - optind; + + if (left <= 0) + /* Special rule: no arguments means "status" */ + i = 0; + else { + if (streq(argv[optind], "help")) { + help(); + return 0; + } + + for (i = 0; i < ELEMENTSOF(verbs); i++) + if (streq(argv[optind], verbs[i].verb)) + break; + + if (i >= ELEMENTSOF(verbs)) { + log_error("Unknown operation %s", argv[optind]); + return -EINVAL; + } + } + + switch (verbs[i].argc_cmp) { + + case EQUAL: + if (left != verbs[i].argc) { + log_error("Invalid number of arguments."); + return -EINVAL; + } + + break; + + case MORE: + if (left < verbs[i].argc) { + log_error("Too few arguments."); + return -EINVAL; + } + + break; + + case LESS: + if (left > verbs[i].argc) { + log_error("Too many arguments."); + return -EINVAL; + } + + break; + + default: + assert_not_reached("Unknown comparison operator."); + } + + if (!bus) { + log_error("Failed to get D-Bus connection: %s", error->message); + return -EIO; + } + + return verbs[i].dispatch(bus, argv + optind, left); +} + +int main(int argc, char *argv[]) { + int r, retval = EXIT_FAILURE; + DBusConnection *bus = NULL; + DBusError error; + + dbus_error_init(&error); + + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r < 0) + goto finish; + else if (r == 0) { + retval = EXIT_SUCCESS; + goto finish; + } + + if (arg_transport == TRANSPORT_NORMAL) + bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); + else if (arg_transport == TRANSPORT_POLKIT) + bus_connect_system_polkit(&bus, &error); + else if (arg_transport == TRANSPORT_SSH) + bus_connect_system_ssh(NULL, arg_host, &bus, &error); + else + assert_not_reached("Uh, invalid transport..."); + + r = localectl_main(bus, argc, argv, &error); + retval = r < 0 ? EXIT_FAILURE : r; + +finish: + if (bus) { + dbus_connection_flush(bus); + dbus_connection_close(bus); + dbus_connection_unref(bus); + } + + dbus_error_free(&error); + dbus_shutdown(); + + pager_close(); + + return retval; +} -- cgit v1.2.3-54-g00ecf From b87377fca3c938303a2fb25229abf8c14814841b Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Mon, 22 Oct 2012 14:33:35 +0200 Subject: update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 123873acfc..84c5e14b5f 100644 --- a/.gitignore +++ b/.gitignore @@ -52,6 +52,7 @@ /test-strv /systemd-ac-power /systemd-timestamp +/systemd-coredumpctl /systemd-cryptsetup /systemd-cryptsetup-generator /systemd-tty-ask-password-agent -- cgit v1.2.3-54-g00ecf From a4bcff5ba36115495994e9f9ba66074471de76ab Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 26 Oct 2012 03:24:03 +0200 Subject: journal: introduce entry array chain cache When traversing entry array chains for a bisection or for retrieving an item by index we previously always started at the beginning of the chain. Since we tend to look at the same chains repeatedly, let's cache where we have been the last time, and maybe we can skip ahead with this the next time. This turns most bisections and index lookups from O(log(n)*log(n)) into O(log(n)). More importantly however, we seek around on disk much less, which is good to reduce buffer cache and seek times on rotational disks. --- .gitignore | 1 + Makefile.am | 9 ++++ src/journal/journal-file.c | 109 +++++++++++++++++++++++++++++++++++++--- src/journal/journal-file.h | 3 ++ src/journal/test-journal-enum.c | 53 +++++++++++++++++++ src/shared/hashmap.c | 19 +++++++ src/shared/hashmap.h | 3 ++ 7 files changed, 189 insertions(+), 8 deletions(-) create mode 100644 src/journal/test-journal-enum.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 84c5e14b5f..2fc2bfd287 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/test-journal-enum /localectl /hostnamectl /timedatectl diff --git a/Makefile.am b/Makefile.am index 0ac63f1f66..252792651e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2509,6 +2509,14 @@ test_journal_match_LDADD = \ libsystemd-journal-internal.la \ libsystemd-id128-internal.la +test_journal_enum_SOURCES = \ + src/journal/test-journal-enum.c + +test_journal_enum_LDADD = \ + libsystemd-shared.la \ + libsystemd-journal-internal.la \ + libsystemd-id128-internal.la + test_journal_stream_SOURCES = \ src/journal/test-journal-stream.c @@ -2635,6 +2643,7 @@ noinst_PROGRAMS += \ test-journal-send \ test-journal-syslog \ test-journal-match \ + test-journal-enum \ test-journal-stream \ test-journal-verify \ test-mmap-cache diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index edf8e7dd5e..6c9deacf2c 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -65,6 +65,9 @@ /* n_data was the first entry we added after the initial file format design */ #define HEADER_SIZE_MIN ALIGN64(offsetof(Header, n_data)) +/* How many entries to keep in the entry array chain cache at max */ +#define CHAIN_CACHE_MAX 20 + void journal_file_close(JournalFile *f) { assert(f); @@ -97,6 +100,8 @@ void journal_file_close(JournalFile *f) { if (f->mmap) mmap_cache_unref(f->mmap); + hashmap_free_free(f->chain_cache); + #ifdef HAVE_XZ free(f->compress_buffer); #endif @@ -1307,37 +1312,89 @@ int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const st return r; } +typedef struct ChainCacheItem { + uint64_t first; /* the array at the begin of the chain */ + uint64_t array; /* the cached array */ + uint64_t begin; /* the first item in the cached array */ + uint64_t total; /* the total number of items in all arrays before this one in the chain */ +} ChainCacheItem; + +static void chain_cache_put( + Hashmap *h, + ChainCacheItem *ci, + uint64_t first, + uint64_t array, + uint64_t begin, + uint64_t total) { + + if (!ci) { + if (hashmap_size(h) >= CHAIN_CACHE_MAX) + ci = hashmap_steal_first(h); + else { + ci = new(ChainCacheItem, 1); + if (!ci) + return; + } + + ci->first = first; + + if (hashmap_put(h, &ci->first, ci) < 0) { + free(ci); + return; + } + } else + assert(ci->first == first); + + ci->array = array; + ci->begin = begin; + ci->total = total; +} + static int generic_array_get(JournalFile *f, uint64_t first, uint64_t i, Object **ret, uint64_t *offset) { Object *o; - uint64_t p = 0, a; + uint64_t p = 0, a, t = 0; int r; + ChainCacheItem *ci; assert(f); a = first; + + /* Try the chain cache first */ + ci = hashmap_get(f->chain_cache, &first); + if (ci && i > ci->total) { + a = ci->array; + i -= ci->total; + t = ci->total; + } + while (a > 0) { - uint64_t n; + uint64_t k; r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); if (r < 0) return r; - n = journal_file_entry_array_n_items(o); - if (i < n) { + k = journal_file_entry_array_n_items(o); + if (i < k) { p = le64toh(o->entry_array.items[i]); - break; + goto found; } - i -= n; + i -= k; + t += k; a = le64toh(o->entry_array.next_entry_array_offset); } - if (a <= 0 || p <= 0) - return 0; + return 0; + +found: + /* Let's cache this item for the next invocation */ + chain_cache_put(f->chain_cache, ci, first, a, o->entry_array.items[0], t); r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); if (r < 0) @@ -1401,11 +1458,38 @@ static int generic_array_bisect(JournalFile *f, bool subtract_one = false; Object *o, *array = NULL; int r; + ChainCacheItem *ci; assert(f); assert(test_object); + /* Start with the first array in the chain */ a = first; + + ci = hashmap_get(f->chain_cache, &first); + if (ci && n > ci->total) { + /* Ah, we have iterated this bisection array chain + * previously! Let's see if we can skip ahead in the + * chain, as far as the last time. But we can't jump + * backwards in the chain, so let's check that + * first. */ + + r = test_object(f, ci->begin, needle); + if (r < 0) + return r; + + if (r == TEST_LEFT) { + /* OK, what we are looking for is right of th + * begin of this EntryArray, so let's jump + * straight to previously cached array in the + * chain */ + + a = ci->array; + n -= ci->total; + t = ci->total; + } + } + while (a > 0) { uint64_t left, right, k, lp; @@ -1486,6 +1570,9 @@ found: if (subtract_one && t == 0 && i == 0) return 0; + /* Let's cache this item for the next invocation */ + chain_cache_put(f->chain_cache, ci, first, a, array->entry_array.items[0], t); + if (subtract_one && i == 0) p = last_p; else if (subtract_one) @@ -2265,6 +2352,12 @@ int journal_file_open( goto fail; } + f->chain_cache = hashmap_new(uint64_hash_func, uint64_compare_func); + if (!f->chain_cache) { + r = -ENOMEM; + goto fail; + } + f->fd = open(f->path, f->flags|O_CLOEXEC, f->mode); if (f->fd < 0) { r = -errno; diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h index d87cbe4876..cdbc8e41f6 100644 --- a/src/journal/journal-file.h +++ b/src/journal/journal-file.h @@ -33,6 +33,7 @@ #include "journal-def.h" #include "util.h" #include "mmap-cache.h" +#include "hashmap.h" typedef struct JournalMetrics { uint64_t max_use; @@ -64,6 +65,8 @@ typedef struct JournalFile { JournalMetrics metrics; MMapCache *mmap; + Hashmap *chain_cache; + #ifdef HAVE_XZ void *compress_buffer; uint64_t compress_buffer_size; diff --git a/src/journal/test-journal-enum.c b/src/journal/test-journal-enum.c new file mode 100644 index 0000000000..8a843ecdda --- /dev/null +++ b/src/journal/test-journal-enum.c @@ -0,0 +1,53 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "log.h" +#include "sd-journal.h" + +int main(int argc, char *argv[]) { + unsigned n = 0; + sd_journal *j; + + log_set_max_level(LOG_DEBUG); + + assert_se(sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY) >= 0); + + assert_se(sd_journal_add_match(j, "_TRANSPORT=syslog", 0) >= 0); + assert_se(sd_journal_add_match(j, "_UID=0", 0) >= 0); + + SD_JOURNAL_FOREACH_BACKWARDS(j) { + const void *d; + size_t l; + + assert_se(sd_journal_get_data(j, "MESSAGE", &d, &l) >= 0); + + printf("%.*s\n", (int) l, (char*) d); + + n ++; + if (n >= 10) + break; + } + + sd_journal_close(j); + return 0; +} diff --git a/src/shared/hashmap.c b/src/shared/hashmap.c index ef78070f4c..dcfbb67228 100644 --- a/src/shared/hashmap.c +++ b/src/shared/hashmap.c @@ -147,6 +147,25 @@ int trivial_compare_func(const void *a, const void *b) { return a < b ? -1 : (a > b ? 1 : 0); } +unsigned uint64_hash_func(const void *p) { + uint64_t u; + + assert_cc(sizeof(uint64_t) == 2*sizeof(unsigned)); + + u = *(const uint64_t*) p; + + return (unsigned) ((u >> 32) ^ u); +} + +int uint64_compare_func(const void *_a, const void *_b) { + uint64_t a, b; + + a = *(const uint64_t*) _a; + b = *(const uint64_t*) _b; + + return a < b ? -1 : (a > b ? 1 : 0); +} + Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func) { bool b; Hashmap *h; diff --git a/src/shared/hashmap.h b/src/shared/hashmap.h index 55dea0a692..6fd71cf519 100644 --- a/src/shared/hashmap.h +++ b/src/shared/hashmap.h @@ -44,6 +44,9 @@ int string_compare_func(const void *a, const void *b); unsigned trivial_hash_func(const void *p); int trivial_compare_func(const void *a, const void *b); +unsigned uint64_hash_func(const void *p); +int uint64_compare_func(const void *a, const void *b); + Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func); void hashmap_free(Hashmap *h); void hashmap_free_free(Hashmap *h); -- cgit v1.2.3-54-g00ecf From 6524990fdc98370ecba5d9f73e67161e8798c010 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 28 Oct 2012 00:49:04 +0200 Subject: logind: support for hybrid sleep (i.e. suspend+hibernate at the same time) --- .gitignore | 1 + Makefile.am | 17 ++++++++- man/logind.conf.xml | 5 ++- man/systemctl.xml | 5 +++ man/systemd-suspend.service.xml | 42 +++++++++++++-------- man/systemd.special.xml | 15 ++++++-- src/core/special.h | 1 + src/login/logind-button.c | 21 ++++++----- src/login/logind-button.h | 1 + src/login/logind-dbus.c | 69 +++++++++++++++++++++++++++++++---- src/shared/util.c | 24 ++++++++++++ src/shared/util.h | 1 + src/sleep/sleep.c | 20 +++++++++- src/systemctl/systemctl.c | 17 +++++++-- src/test/test-sleep.c | 39 ++++++++++++++++++++ units/.gitignore | 1 + units/hybrid-sleep.target | 13 +++++++ units/systemd-hybrid-sleep.service.in | 17 +++++++++ 18 files changed, 264 insertions(+), 45 deletions(-) create mode 100644 src/test/test-sleep.c create mode 100644 units/hybrid-sleep.target create mode 100644 units/systemd-hybrid-sleep.service.in (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 2fc2bfd287..94a85423ab 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /test-journal-enum +/test-sleep /localectl /hostnamectl /timedatectl diff --git a/Makefile.am b/Makefile.am index 3d96500c89..fadc1c7b59 100644 --- a/Makefile.am +++ b/Makefile.am @@ -273,6 +273,7 @@ dist_systemunit_DATA = \ units/nss-user-lookup.target \ units/mail-transfer-agent.target \ units/hibernate.target \ + units/hybrid-sleep.target \ units/http-daemon.target \ units/poweroff.target \ units/reboot.target \ @@ -329,6 +330,7 @@ nodist_systemunit_DATA = \ units/rescue.service \ units/user@.service \ units/systemd-hibernate.service \ + units/systemd-hybrid-sleep.service \ units/systemd-suspend.service \ units/systemd-halt.service \ units/systemd-poweroff.service \ @@ -379,6 +381,7 @@ EXTRA_DIST += \ units/systemd-udev-settle.service \ units/debug-shell.service.in \ units/systemd-hibernate.service.in \ + units/systemd-hybrid-sleep.service.in \ units/systemd-suspend.service.in \ units/quotaon.service.in \ introspect.awk \ @@ -534,6 +537,7 @@ MANPAGES_ALIAS = \ man/systemd-shutdownd.socket.8 \ man/systemd-shutdownd.8 \ man/systemd-hibernate.service.8 \ + man/systemd-hybrid-sleep.service.8 \ man/systemd-sleep.8 \ man/systemd-shutdown.8 \ man/systemd-poweroff.service.8 \ @@ -608,6 +612,7 @@ man/systemd-initctl.8: man/systemd-initctl.service.8 man/systemd-shutdownd.socket.8: man/systemd-shutdownd.service.8 man/systemd-shutdownd.8: man/systemd-shutdownd.service.8 man/systemd-hibernate.service.8: man/systemd-suspend.service.8 +man/systemd-hybrid-sleep.service.8: man/systemd-suspend.service.8 man/systemd-sleep.8: man/systemd-suspend.service.8 man/systemd-shutdown.8: man/systemd-halt.service.8 man/systemd-poweroff.service.8: man/systemd-halt.service.8 @@ -1176,7 +1181,8 @@ noinst_PROGRAMS += \ test-unit-name \ test-log \ test-unit-file \ - test-date + test-date \ + test-sleep TESTS += \ test-job-type \ @@ -1184,7 +1190,8 @@ TESTS += \ test-strv \ test-unit-name \ test-unit-file \ - test-date + test-date \ + test-sleep test_engine_SOURCES = \ src/test/test-engine.c @@ -1252,6 +1259,12 @@ test_date_SOURCES = \ test_date_LDADD = \ libsystemd-core.la +test_sleep_SOURCES = \ + src/test/test-sleep.c + +test_sleep_LDADD = \ + libsystemd-core.la + test_daemon_SOURCES = \ src/test/test-daemon.c diff --git a/man/logind.conf.xml b/man/logind.conf.xml index 3d83d2c81c..29469d37e1 100644 --- a/man/logind.conf.xml +++ b/man/logind.conf.xml @@ -211,8 +211,9 @@ poweroff, reboot, halt, - kexec and - hibernate. If + kexec, + hibernate and + hybrid-sleep. If ignore logind will never handle these keys. Otherwise the specified action will be taken in the diff --git a/man/systemctl.xml b/man/systemctl.xml index d547410696..786c2bdfcf 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -1180,6 +1180,11 @@ Hibernate the system. + + hybrid-sleep + + Hibernate and suspend the system. + switch-root [ROOT] [INIT] diff --git a/man/systemd-suspend.service.xml b/man/systemd-suspend.service.xml index 604aab6f6b..b9464c8826 100644 --- a/man/systemd-suspend.service.xml +++ b/man/systemd-suspend.service.xml @@ -45,6 +45,7 @@ systemd-suspend.service systemd-hibernate.service + systemd-hybrid-sleep.service systemd-sleep System sleep state logic @@ -52,6 +53,7 @@ systemd-suspend.service systemd-hibernate.service + systemd-hybrid-sleep.service /usr/lib/systemd/systemd-sleep @@ -64,19 +66,25 @@ for the actual system suspend. Similar, systemd-hibernate.service is pulled in by hibernate.target to - execute the actual hibernation. - - Immediately before entering system suspend and - hibernation - systemd-suspend.service will run - all executables in + execute the actual hibernation. Finally, + systemd-hybrid-sleep.service is + pulled in by hybrid-sleep.target + to execute hybrid hibernation with system + suspend. + + Immediately before entering system suspend + and/or hibernation + systemd-suspend.service (and the + other mentioned units, respectively) will run all + executables in /usr/lib/systemd/system-sleep/ and pass two arguments to them. The first argument will be "pre", the second either - "suspend" or - "hibernate", depending on the + "suspend", + "hibernate", or + "hybrid-sleep" depending on the chosen action. Immediately after leaving system - suspend and hibernation the same executables are run, + suspend and/or hibernation the same executables are run, but the first argument is now "post". All executables in this directory are executed in parallel, and execution of @@ -87,15 +95,17 @@ /usr/lib/systemd/system-sleep/ are intended for local use only and should be considered hacks. If applications want to be notified - of system suspend and resume there are much nicer - interfaces available. + of system suspend/hibernation and resume there are + much nicer interfaces available. Note that - systemd-suspend.service and - systemd-hibernate.service should - never be executed directly. Instead, trigger system - sleep states with a command such as "systemctl - suspend" or suchlike. + systemd-suspend.service, + systemd-hibernate.service and + systemd-hybrid-sleep.service + should never be executed directly. Instead, trigger + system sleep states with a command such as + "systemctl suspend" or + similar. diff --git a/man/systemd.special.xml b/man/systemd.special.xml index 3e5f653494..6b8e0ec7f0 100644 --- a/man/systemd.special.xml +++ b/man/systemd.special.xml @@ -63,6 +63,7 @@ graphical.target, hibernate.target, http-daemon.target, + hybrid-sleep.target, halt.target, kbrequest.target, kexec.target, @@ -302,6 +303,15 @@ facility. + + hybrid-sleep.target + + A special target unit + for hibernating and suspending the + system at the same time. This pulls in + sleep.target. + + halt.target @@ -652,9 +662,8 @@ A special target unit that is pulled in by - suspend.target - and - hibernate.target + suspend.target, + hibernate.target and hybrid-sleep.target and may be used to hook units into the sleep state logic. diff --git a/src/core/special.h b/src/core/special.h index e3004a518d..8923f340ba 100644 --- a/src/core/special.h +++ b/src/core/special.h @@ -37,6 +37,7 @@ #define SPECIAL_EXIT_TARGET "exit.target" #define SPECIAL_SUSPEND_TARGET "suspend.target" #define SPECIAL_HIBERNATE_TARGET "hibernate.target" +#define SPECIAL_HYBRID_SLEEP_TARGET "hybrid-sleep.target" /* Special boot targets */ #define SPECIAL_RESCUE_TARGET "rescue.target" diff --git a/src/login/logind-button.c b/src/login/logind-button.c index 8fdab789bc..753d95454c 100644 --- a/src/login/logind-button.c +++ b/src/login/logind-button.c @@ -163,16 +163,18 @@ static int button_handle( [HANDLE_HALT] = "Halting...", [HANDLE_KEXEC] = "Rebooting via kexec...", [HANDLE_SUSPEND] = "Suspending...", - [HANDLE_HIBERNATE] = "Hibernating..." + [HANDLE_HIBERNATE] = "Hibernating...", + [HANDLE_HYBRID_SLEEP] = "Hibernating and suspend...", }; static const char * const target_table[_HANDLE_BUTTON_MAX] = { - [HANDLE_POWEROFF] = "poweroff.target", - [HANDLE_REBOOT] = "reboot.target", - [HANDLE_HALT] = "halt.target", - [HANDLE_KEXEC] = "kexec.target", - [HANDLE_SUSPEND] = "suspend.target", - [HANDLE_HIBERNATE] = "hibernate.target" + [HANDLE_POWEROFF] = SPECIAL_POWEROFF_TARGET, + [HANDLE_REBOOT] = SPECIAL_REBOOT_TARGET, + [HANDLE_HALT] = SPECIAL_HALT_TARGET, + [HANDLE_KEXEC] = SPECIAL_KEXEC_TARGET, + [HANDLE_SUSPEND] = SPECIAL_SUSPEND_TARGET, + [HANDLE_HIBERNATE] = SPECIAL_HIBERNATE_TARGET, + [HANDLE_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET }; DBusError error; @@ -193,7 +195,7 @@ static int button_handle( return 0; } - inhibit_operation = handle == HANDLE_SUSPEND || handle == HANDLE_HIBERNATE ? INHIBIT_SLEEP : INHIBIT_SHUTDOWN; + inhibit_operation = handle == HANDLE_SUSPEND || handle == HANDLE_HIBERNATE || handle == HANDLE_HYBRID_SLEEP ? INHIBIT_SLEEP : INHIBIT_SHUTDOWN; /* If the actual operation is inhibited, warn and fail */ if (!ignore_inhibited && @@ -305,7 +307,8 @@ static const char* const handle_button_table[_HANDLE_BUTTON_MAX] = { [HANDLE_HALT] = "halt", [HANDLE_KEXEC] = "kexec", [HANDLE_SUSPEND] = "suspend", - [HANDLE_HIBERNATE] = "hibernate" + [HANDLE_HIBERNATE] = "hibernate", + [HANDLE_HYBRID_SLEEP] = "hybrid-sleep", }; DEFINE_STRING_TABLE_LOOKUP(handle_button, HandleButton); DEFINE_CONFIG_PARSE_ENUM(config_parse_handle_button, handle_button, HandleButton, "Failed to parse handle button setting"); diff --git a/src/login/logind-button.h b/src/login/logind-button.h index ca820ed7e5..827a03e460 100644 --- a/src/login/logind-button.h +++ b/src/login/logind-button.h @@ -32,6 +32,7 @@ typedef enum HandleButton { HANDLE_KEXEC, HANDLE_SUSPEND, HANDLE_HIBERNATE, + HANDLE_HYBRID_SLEEP, _HANDLE_BUTTON_MAX, _HANDLE_BUTTON_INVALID = -1 } HandleButton; diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index a7647e3c80..3bcb91bf13 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -145,6 +145,9 @@ " \n" \ " \n" \ " \n" \ + " \n" \ + " \n" \ + " \n" \ " \n" \ " \n" \ " \n" \ @@ -157,6 +160,9 @@ " \n" \ " \n" \ " \n" \ + " \n" \ + " \n" \ + " \n" \ " \n" \ " \n" \ " \n" \ @@ -1054,6 +1060,7 @@ static int bus_manager_can_shutdown_or_sleep( const char *action_multiple_sessions, const char *action_ignore_inhibit, const char *sleep_type, + const char *sleep_disk_type, DBusError *error, DBusMessage **_reply) { @@ -1085,6 +1092,17 @@ static int bus_manager_can_shutdown_or_sleep( } } + if (sleep_disk_type) { + r = can_sleep_disk(sleep_disk_type); + if (r < 0) + return r; + + if (r == 0) { + result = "na"; + goto finish; + } + } + ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), error); if (ul == (unsigned long) -1) return -EIO; @@ -1234,6 +1252,7 @@ static int bus_manager_do_shutdown_or_sleep( const char *action_multiple_sessions, const char *action_ignore_inhibit, const char *sleep_type, + const char *sleep_disk_type, DBusError *error, DBusMessage **_reply) { @@ -1271,6 +1290,15 @@ static int bus_manager_do_shutdown_or_sleep( return -ENOTSUP; } + if (sleep_disk_type) { + r = can_sleep_disk(sleep_disk_type); + if (r < 0) + return r; + + if (r == 0) + return -ENOTSUP; + } + ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), error); if (ul == (unsigned long) -1) return -EIO; @@ -2065,7 +2093,7 @@ static DBusHandlerResult manager_message_handler( "org.freedesktop.login1.power-off", "org.freedesktop.login1.power-off-multiple-sessions", "org.freedesktop.login1.power-off-ignore-inhibit", - NULL, + NULL, NULL, &error, &reply); if (r < 0) return bus_send_error_reply(connection, message, &error, r); @@ -2077,7 +2105,7 @@ static DBusHandlerResult manager_message_handler( "org.freedesktop.login1.reboot", "org.freedesktop.login1.reboot-multiple-sessions", "org.freedesktop.login1.reboot-ignore-inhibit", - NULL, + NULL, NULL, &error, &reply); if (r < 0) return bus_send_error_reply(connection, message, &error, r); @@ -2090,7 +2118,7 @@ static DBusHandlerResult manager_message_handler( "org.freedesktop.login1.suspend", "org.freedesktop.login1.suspend-multiple-sessions", "org.freedesktop.login1.suspend-ignore-inhibit", - "mem", + "mem", NULL, &error, &reply); if (r < 0) return bus_send_error_reply(connection, message, &error, r); @@ -2102,7 +2130,20 @@ static DBusHandlerResult manager_message_handler( "org.freedesktop.login1.hibernate", "org.freedesktop.login1.hibernate-multiple-sessions", "org.freedesktop.login1.hibernate-ignore-inhibit", - "disk", + "disk", NULL, + &error, &reply); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "HybridSleep")) { + r = bus_manager_do_shutdown_or_sleep( + m, connection, message, + SPECIAL_HYBRID_SLEEP_TARGET, + INHIBIT_SLEEP, + "org.freedesktop.login1.hibernate", + "org.freedesktop.login1.hibernate-multiple-sessions", + "org.freedesktop.login1.hibernate-ignore-inhibit", + "disk", "suspend", &error, &reply); if (r < 0) return bus_send_error_reply(connection, message, &error, r); @@ -2115,7 +2156,7 @@ static DBusHandlerResult manager_message_handler( "org.freedesktop.login1.power-off", "org.freedesktop.login1.power-off-multiple-sessions", "org.freedesktop.login1.power-off-ignore-inhibit", - NULL, + NULL, NULL, &error, &reply); if (r < 0) return bus_send_error_reply(connection, message, &error, r); @@ -2126,7 +2167,7 @@ static DBusHandlerResult manager_message_handler( "org.freedesktop.login1.reboot", "org.freedesktop.login1.reboot-multiple-sessions", "org.freedesktop.login1.reboot-ignore-inhibit", - NULL, + NULL, NULL, &error, &reply); if (r < 0) return bus_send_error_reply(connection, message, &error, r); @@ -2138,7 +2179,7 @@ static DBusHandlerResult manager_message_handler( "org.freedesktop.login1.suspend", "org.freedesktop.login1.suspend-multiple-sessions", "org.freedesktop.login1.suspend-ignore-inhibit", - "mem", + "mem", NULL, &error, &reply); if (r < 0) return bus_send_error_reply(connection, message, &error, r); @@ -2150,7 +2191,19 @@ static DBusHandlerResult manager_message_handler( "org.freedesktop.login1.hibernate", "org.freedesktop.login1.hibernate-multiple-sessions", "org.freedesktop.login1.hibernate-ignore-inhibit", - "disk", + "disk", NULL, + &error, &reply); + if (r < 0) + return bus_send_error_reply(connection, message, &error, r); + + } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CanHybridSleep")) { + r = bus_manager_can_shutdown_or_sleep( + m, connection, message, + INHIBIT_SLEEP, + "org.freedesktop.login1.hibernate", + "org.freedesktop.login1.hibernate-multiple-sessions", + "org.freedesktop.login1.hibernate-ignore-inhibit", + "disk", "suspend", &error, &reply); if (r < 0) return bus_send_error_reply(connection, message, &error, r); diff --git a/src/shared/util.c b/src/shared/util.c index db8e75b628..2d4a4c1102 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -5691,6 +5691,30 @@ int can_sleep(const char *type) { return false; } +int can_sleep_disk(const char *type) { + char *w, *state; + size_t l, k; + int r; + _cleanup_free_ char *p = NULL; + + assert(type); + + r = read_one_line_file("/sys/power/disk", &p); + if (r < 0) + return r == -ENOENT ? 0 : r; + + k = strlen(type); + FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state) { + if (l == k && memcmp(w, type, l) == 0) + return true; + + if (l == k + 2 && w[0] == '[' && memcmp(w + 1, type, l - 2) == 0 && w[l-1] == ']') + return true; + } + + return false; +} + bool is_valid_documentation_url(const char *url) { assert(url); diff --git a/src/shared/util.h b/src/shared/util.h index f6a4c1ea6c..f726263dd3 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -529,6 +529,7 @@ int setrlimit_closest(int resource, const struct rlimit *rlim); int getenv_for_pid(pid_t pid, const char *field, char **_value); int can_sleep(const char *type); +int can_sleep_disk(const char *type); bool is_valid_documentation_url(const char *url); diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c index 2f312675c5..218de3a567 100644 --- a/src/sleep/sleep.c +++ b/src/sleep/sleep.c @@ -46,7 +46,7 @@ int main(int argc, char *argv[]) { if (streq(argv[1], "suspend")) verb = "mem"; - else if (streq(argv[1], "hibernate")) + else if (streq(argv[1], "hibernate") || streq(argv[1], "hybrid-sleep")) verb = "disk"; else { log_error("Unknown action '%s'.", argv[1]); @@ -54,6 +54,16 @@ int main(int argc, char *argv[]) { goto finish; } + /* Configure the hibernation mode */ + if (streq(argv[1], "hibernate")) { + if (write_one_line_file("/sys/power/disk", "platform") < 0) + write_one_line_file("/sys/power/disk", "shutdown"); + } else if (streq(argv[1], "hybrid-sleep")) { + if (write_one_line_file("/sys/power/disk", "suspend") < 0) + if (write_one_line_file("/sys/power/disk", "platform") < 0) + write_one_line_file("/sys/power/disk", "shutdown"); + } + f = fopen("/sys/power/state", "we"); if (!f) { log_error("Failed to open /sys/power/state: %m"); @@ -73,12 +83,18 @@ int main(int argc, char *argv[]) { "MESSAGE=Suspending system...", "SLEEP=suspend", NULL); - else + else if (streq(argv[1], "hibernate")) log_struct(LOG_INFO, MESSAGE_ID(SD_MESSAGE_SLEEP_START), "MESSAGE=Hibernating system...", "SLEEP=hibernate", NULL); + else + log_struct(LOG_INFO, + MESSAGE_ID(SD_MESSAGE_SLEEP_START), + "MESSAGE=Hibernating and suspending system...", + "SLEEP=hybrid-sleep", + NULL); fputs(verb, f); fputc('\n', f); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 41dcefb675..b9e64a677d 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -99,6 +99,7 @@ static enum action { ACTION_EXIT, ACTION_SUSPEND, ACTION_HIBERNATE, + ACTION_HYBRID_SLEEP, ACTION_RUNLEVEL2, ACTION_RUNLEVEL3, ACTION_RUNLEVEL4, @@ -1608,6 +1609,8 @@ static enum action verb_to_action(const char *verb) { return ACTION_SUSPEND; else if (streq(verb, "hibernate")) return ACTION_HIBERNATE; + else if (streq(verb, "hybrid-sleep")) + return ACTION_HYBRID_SLEEP; else return ACTION_INVALID; } @@ -1628,7 +1631,8 @@ static int start_unit(DBusConnection *bus, char **args) { [ACTION_DEFAULT] = SPECIAL_DEFAULT_TARGET, [ACTION_EXIT] = SPECIAL_EXIT_TARGET, [ACTION_SUSPEND] = SPECIAL_SUSPEND_TARGET, - [ACTION_HIBERNATE] = SPECIAL_HIBERNATE_TARGET + [ACTION_HIBERNATE] = SPECIAL_HIBERNATE_TARGET, + [ACTION_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET }; int r, ret = 0; @@ -1764,6 +1768,10 @@ static int reboot_with_logind(DBusConnection *bus, enum action a) { method = "Hibernate"; break; + case ACTION_HYBRID_SLEEP: + method = "HybridSleep"; + break; + default: return -EINVAL; } @@ -1815,7 +1823,8 @@ static int start_special(DBusConnection *bus, char **args) { (a == ACTION_POWEROFF || a == ACTION_REBOOT || a == ACTION_SUSPEND || - a == ACTION_HIBERNATE)) { + a == ACTION_HIBERNATE || + a == ACTION_HYBRID_SLEEP)) { r = reboot_with_logind(bus, a); if (r >= 0) return r; @@ -3967,7 +3976,8 @@ static int systemctl_help(void) { " exit Request user instance exit\n" " switch-root [ROOT] [INIT] Change to a different root file system\n" " suspend Suspend the system\n" - " hibernate Hibernate the system\n", + " hibernate Hibernate the system\n" + " hybrid-sleep Hibernate and suspend the system\n", program_invocation_short_name); return 0; @@ -4896,6 +4906,7 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError { "kexec", EQUAL, 1, start_special }, { "suspend", EQUAL, 1, start_special }, { "hibernate", EQUAL, 1, start_special }, + { "hybrid-sleep", EQUAL, 1, start_special }, { "default", EQUAL, 1, start_special }, { "rescue", EQUAL, 1, start_special }, { "emergency", EQUAL, 1, start_special }, diff --git a/src/test/test-sleep.c b/src/test/test-sleep.c new file mode 100644 index 0000000000..5a98ecda2f --- /dev/null +++ b/src/test/test-sleep.c @@ -0,0 +1,39 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "util.h" +#include "log.h" + +int main(int argc, char* argv[]) { + log_info("Can Suspend: %s", yes_no(can_sleep("mem") > 0)); + log_info("Can Hibernate: %s", yes_no(can_sleep("disk") > 0)); + log_info("Can Hibernate+Suspend (Hybrid-Sleep): %s", yes_no(can_sleep_disk("suspend") > 0)); + log_info("Can Hibernate+Reboot: %s", yes_no(can_sleep_disk("reboot") > 0)); + log_info("Can Hibernate+Platform: %s", yes_no(can_sleep_disk("platform") > 0)); + log_info("Can Hibernate+Shutdown: %s", yes_no(can_sleep_disk("shutdown") > 0)); + + return 0; +} diff --git a/units/.gitignore b/units/.gitignore index c72e2cbee3..63c7ba06bb 100644 --- a/units/.gitignore +++ b/units/.gitignore @@ -1,3 +1,4 @@ +/systemd-hybrid-sleep.service /systemd-journal-gatewayd.service /systemd-journal-flush.service /systemd-hibernate.service diff --git a/units/hybrid-sleep.target b/units/hybrid-sleep.target new file mode 100644 index 0000000000..d2d3409225 --- /dev/null +++ b/units/hybrid-sleep.target @@ -0,0 +1,13 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Hybrid Suspend+Hibernate +Documentation=man:systemd.special(7) +DefaultDependencies=no +BindsTo=systemd-hybrid-sleep.service +After=systemd-hybrid-sleep.service diff --git a/units/systemd-hybrid-sleep.service.in b/units/systemd-hybrid-sleep.service.in new file mode 100644 index 0000000000..914b686c36 --- /dev/null +++ b/units/systemd-hybrid-sleep.service.in @@ -0,0 +1,17 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Hybrid Suspend+Hibernate +Documentation=man:systemd-suspend.service(8) +DefaultDependencies=no +Requires=sleep.target +After=sleep.target + +[Service] +Type=oneshot +ExecStart=@rootlibexecdir@/systemd-sleep hybrid-sleep -- cgit v1.2.3-54-g00ecf From 6e6fb527f987d0c1f5bd930020301c9ac76a5e2c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 14 Nov 2012 22:20:17 +0100 Subject: shared: add API for replacing @FOO@ style variables in strings --- .gitignore | 1 + Makefile.am | 14 +++++- src/shared/replace-var.c | 110 ++++++++++++++++++++++++++++++++++++++++++++ src/shared/replace-var.h | 24 ++++++++++ src/test/test-replace-var.c | 46 ++++++++++++++++++ 5 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 src/shared/replace-var.c create mode 100644 src/shared/replace-var.h create mode 100644 src/test/test-replace-var.c (limited to '.gitignore') diff --git a/.gitignore b/.gitignore index 94a85423ab..18e4e23c50 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/test-replace-var /test-journal-enum /test-sleep /localectl diff --git a/Makefile.am b/Makefile.am index 452de152dc..53a3bb9f90 100644 --- a/Makefile.am +++ b/Makefile.am @@ -798,6 +798,8 @@ libsystemd_shared_la_SOURCES = \ src/shared/spawn-ask-password-agent.h \ src/shared/specifier.c \ src/shared/specifier.h \ + src/shared/replace-var.c \ + src/shared/replace-var.h \ src/shared/spawn-polkit-agent.c \ src/shared/spawn-polkit-agent.h \ src/shared/hwclock.c \ @@ -1182,7 +1184,8 @@ noinst_PROGRAMS += \ test-log \ test-unit-file \ test-date \ - test-sleep + test-sleep \ + test-replace-var TESTS += \ test-job-type \ @@ -1191,7 +1194,8 @@ TESTS += \ test-unit-name \ test-unit-file \ test-date \ - test-sleep + test-sleep \ + test-replace-var test_engine_SOURCES = \ src/test/test-engine.c @@ -1265,6 +1269,12 @@ test_sleep_SOURCES = \ test_sleep_LDADD = \ libsystemd-core.la +test_replace_var_SOURCES = \ + src/test/test-replace-var.c + +test_replace_var_LDADD = \ + libsystemd-shared.la + test_daemon_SOURCES = \ src/test/test-daemon.c diff --git a/src/shared/replace-var.c b/src/shared/replace-var.c new file mode 100644 index 0000000000..e11c57a43d --- /dev/null +++ b/src/shared/replace-var.c @@ -0,0 +1,110 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "macro.h" +#include "util.h" +#include "replace-var.h" + +/* + * Generic infrastructure for replacing @FOO@ style variables in + * strings. Will call a callback for each replacement. + */ + +static int get_variable(const char *b, char **r) { + size_t k; + char *t; + + assert(b); + assert(r); + + if (*b != '@') + return 0; + + k = strspn(b + 1, "ABCDEFGHIJKLMNOPQRSTUVWXYZ_"); + if (k <= 0 || b[k+1] != '@') + return 0; + + t = strndup(b + 1, k); + if (!t) + return -ENOMEM; + + *r = t; + return 1; +} + +char *replace_var(const char *text, char *(*lookup)(const char *variable, void*userdata), void *userdata) { + char *r, *t; + const char *f; + size_t l; + + assert(text); + assert(lookup); + + l = strlen(text); + r = new(char, l+1); + if (!r) + return NULL; + + f = text; + t = r; + while (*f) { + _cleanup_free_ char *v = NULL, *n = NULL; + char *a; + int k; + size_t skip, d, nl; + + k = get_variable(f, &v); + if (k < 0) + goto oom; + if (k == 0) { + *(t++) = *(f++); + continue; + } + + n = lookup(v, userdata); + if (!n) + goto oom; + + skip = strlen(v) + 2; + + d = t - r; + nl = l - skip + strlen(n); + a = realloc(r, nl + 1); + if (!a) + goto oom; + + l = nl; + r = a; + t = r + d; + + t = stpcpy(t, n); + f += skip; + } + + *t = 0; + return r; + +oom: + free(r); + return NULL; +} diff --git a/src/shared/replace-var.h b/src/shared/replace-var.h new file mode 100644 index 0000000000..7eaee93a3e --- /dev/null +++ b/src/shared/replace-var.h @@ -0,0 +1,24 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +char *replace_var(const char *text, char *(*lookup)(const char *variable, void*userdata), void *userdata); diff --git a/src/test/test-replace-var.c b/src/test/test-replace-var.c new file mode 100644 index 0000000000..b1d42d77fd --- /dev/null +++ b/src/test/test-replace-var.c @@ -0,0 +1,46 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "util.h" +#include "macro.h" +#include "replace-var.h" + +static char *lookup(const char *variable, void *userdata) { + return strjoin("<<<", variable, ">>>", NULL); +} + +int main(int argc, char *argv[]) { + char *r; + + assert_se(r = replace_var("@@@foobar@xyz@HALLO@foobar@test@@testtest@TEST@...@@@", lookup, NULL)); + puts(r); + assert_se(streq(r, "@@@foobar@xyz<<>>foobar@test@@testtest<<>>...@@@")); + free(r); + + assert_se(r = strreplace("XYZFFFFXYZFFFFXYZ", "XYZ", "ABC")); + puts(r); + assert_se(streq(r, "ABCFFFFABCFFFFABC")); + free(r); + + return 0; +} -- cgit v1.2.3-54-g00ecf